一 全局命令:

1 查看所有键

keys *

redis set contains的复杂度 redis del时间复杂度_redis

2 键总数

dbsize

dbsize计算键数时不会遍历所有键,而是直接获取redis内置的键总数变量,所以时间复杂度是O(1),而keys命令会遍历所有键,时间复杂度为O(n),当redis保存了大量键时,线上环境禁止使用。

redis set contains的复杂度 redis del时间复杂度_字符串_02

3 检查键是否存在

 exists key

存在返回1,否则0

redis set contains的复杂度 redis del时间复杂度_字符串_03

4 删除键

del key [key....]

del是通用的命令,可以删除任何数据结构类型,成功删除键会返回删除成功的键的个数,否则返回0。

del删除不存在的键会返回0。

del支持删除多个键(例del mylist1 mylist2 mylist3)。

5 键过期

expire keys seconds

redis支持对键添加过期时间,当过期后,会自动删除键

redis set contains的复杂度 redis del时间复杂度_数据结构_04

ttl key

ttl命令可以查看键的剩余过期时间,它有三中返回类型:

  • 大于0的整数,表示键的剩余过期时间;
  • -1表示未设置过期时间;
  • -2表示键不存在。

6 返回键的数据结构类型

type key

redis set contains的复杂度 redis del时间复杂度_redis_05

redis set contains的复杂度 redis del时间复杂度_redis_06

若键不存在返回none

二 数据结构和内部编码

type命令返回的是当前键的数据结果类型,它是redis对外的数据结构,分别有string、hash、list、set、zset。

  • 每种数据结构都有自己底层的内部编码实现,但redis会在合适的场景选择合适的内部编码。
  • 每种数据结构都有两种内部编码实现(例list有linkedlist和ziplist实现)。

object encoding key可以查看键的内部编码信息。

redis set contains的复杂度 redis del时间复杂度_redis_07

redis set contains的复杂度 redis del时间复杂度_数据结构_08

redis这样设计的好处:

  • 可以改进内部编码,而对外的数据结构和命令没有影响,这样一旦开发出更优秀的内部编码,无需改动外部数据结构和命令;
  • 多种内部编码可以在不同场景下发挥各自的优势。

三 单线程架构

redis使用单线程架构和I/O多路复用模型来实现高性能的内存数据库服务。

1 单线程模型

redis客户端与服务端的模型可以简化为下图,每次客户端都经历了发送命令、执行命令、返回结果三个过程。

redis set contains的复杂度 redis del时间复杂度_字符串_09

第2步是比较重要的,redis是单线程执行命令的,所以一条命令到达服务端后不会立刻执行,而是所有命令都进入一个队列中,然后逐个被执行。当多个客户端执行命令时,这些命令的执行顺序是不确定的。

2 为什么单线程redis依然很快?

为什么Redis使用单线程回达到每秒万级别的处理能力呢?

  • 纯内存访问,redis将所有数据放在内存中,内存的响应时长大约为100纳秒,这是redis达到每秒万级别访问的重要基础;
  • 非阻塞IO,Redis使用epoll()作为I/O多路复用技术的实现,再加上Redis自身的事件处理模型将epoll()中的连接、读写、关闭都转换为时间,不在网络I/O上浪费过多的时间;
  • 单线程避免了线程切换和竞争产生的消耗(单线程简化数据结构和算法的实现)。

但是单线程有一个问题,它对每个命令的执行时间是有要求的。因为单个命令时间执行时间过长,会造成其他命令的阻塞,这样对redis这种高性能的服务来说是致命的。

3 字符串

redis的键都是字符串。字符串的值可以是字符串、数字和二进制,但是值最大不能超过512MB。

常用命令:

(1)设置值

set key value [ex seconds] [px milliseconds] [nx|xx]

  • ex seconds:为键设置秒级过期时间;
  • px milliseconds:为键设置毫秒级过期时间;
  • nx:键必须不存在,才可以设置成功,用于添加;
  • xx:与nx相反,键必须存在,才可以设置成功,用于更新。

另外redis还提供了setex、setnx两种命令。

setex key seconds value  //相当于set key value ex seconds

redis set contains的复杂度 redis del时间复杂度_redis_10

setnx key value

redis set contains的复杂度 redis del时间复杂度_redis_11

 

setnx的应用场景:当有多个客户端执行setnx命令时,只有一个客户端能成功,setnx可以做分布式锁的一种实现方案。

(2) 获取值

get key

redis set contains的复杂度 redis del时间复杂度_redis_12

(3) 批量设置值

mset key value [key value.....]

redis set contains的复杂度 redis del时间复杂度_redis_13

(4) 批量获取值

mget key [key....]

redis set contains的复杂度 redis del时间复杂度_数据结构_14

redis set contains的复杂度 redis del时间复杂度_数据结构_15

如果键不存在会返回Nil空。

批量操作命令可以提高开发效率。

(5) 计数

incr key //对key进行自增操作

  • 值不是整数,返回错误;
  • 值是整数,返回自增的结果;
  • 键不存在,按照值为0自增,返回结果1。

 

redis set contains的复杂度 redis del时间复杂度_数据结构_16

redis set contains的复杂度 redis del时间复杂度_数据结构_17

redis除了自增,还有自减decr,incrby(自增指定数字),decrby(自减指定数字)、incrbyfloat(自增浮点数)。

decr key

incrby key increment

decrby key decrement

incrbyfloat key increment

不常用命令

(1) 追加值

append key value

向字符串末尾追加值

redis set contains的复杂度 redis del时间复杂度_字符串_18

(2)字符串长度

strlen key

redis set contains的复杂度 redis del时间复杂度_字符串_19

中文在redis中,每个字符占3个字节。

(3)设置并返回原值

getset key value

redis set contains的复杂度 redis del时间复杂度_字符串_20

(4) 设置指定位置的字符

setrange ket offeset value

redis set contains的复杂度 redis del时间复杂度_字符串_21

(5) 获取部分字符串

getrange key start end

redis set contains的复杂度 redis del时间复杂度_字符串_22

start表示起始偏移量,end表示结束偏移量。

3 字符串的内部编码

  • int:8个字节的长整型;
  • embstr:小于等于39字节的字符串;
  • raw:大于39字节的字符串。

redis会根据值的类型和长度选择合适的编码。

redis set contains的复杂度 redis del时间复杂度_redis_23

4 使用场景

  • 缓存功能:由于redis具有支撑高并发的特性,所以缓存通常能起到加速读写和降低后端压力的作用;
  • 计数:很多网页点击量及视频播放次数都是使用redis作为计数工具;
  • 共享Session:使用分布式web服务器保存Session信息时可能出现不一致情况,使用redis保存session是一种解决方案;
  • 限速:例如限制用户每分钟获取验证码的频率。

4 Hash哈希

哈希类型是指键值类型本身又是一个键值对结构。如下图:

redis set contains的复杂度 redis del时间复杂度_数据结构_24

(1)设置值

hset key field value

redis set contains的复杂度 redis del时间复杂度_字符串_25

(2) 获取值

hget key field

redis set contains的复杂度 redis del时间复杂度_数据结构_26

当键或者field不存在返回Nil。

(3) 删除field

hdel key field [field....]     //删除一个到多个field

redis set contains的复杂度 redis del时间复杂度_redis_27

(4)计算field的个数

hlen key 

redis set contains的复杂度 redis del时间复杂度_数据结构_28

(5) 批量获取或设置值

hmget key filed [field....]

hmset key field value [field value....]

redis set contains的复杂度 redis del时间复杂度_数据结构_29

(6) 判断field是否存在

hexists key field

redis set contains的复杂度 redis del时间复杂度_字符串_30

存在返回1,否则返回0

(7)获取所有field

hkeys key

redis set contains的复杂度 redis del时间复杂度_redis_31

(8) 获取所有值

hvals key

redis set contains的复杂度 redis del时间复杂度_数据结构_32

(9)获取所有的field-value

hgetall key

redis set contains的复杂度 redis del时间复杂度_字符串_33

(10)hincrby hincrbyfloat

hincrby key field

hincrbyfloat key field

自增命令

(11)计算value的字符串长度(Redis3.2以上支持)

hstrlen key field

 

内部编码

  • ziplist(压缩列表):当哈希类型元素个数小于hash-max-ziplist-entrlies配置(默认512个)、同时所有值都小于hash-max-ziplist-value配置(默认64字节),redis会使用ziplist作为内部实现;ziplist使用更加紧凑的结构实现多个元素的连续存储,所以在节省内存方面比hashtable更优秀;
  • hashtable(哈希表):当哈希类型无法满足ziplist的条件时,redis会使用hashtable作为哈希的实现,因为此时ziplist的读写效率会下降,而hashtable的读写时间复杂度为O(1)。

使用场景

  • 缓存用户信息

哈希类型是稀疏的,关系数据库是完全结构化的,例如哈希类型每个键都有不同的field,关系数据库添加新的列,所有行都要为其设置值;

关系数据库可以做复杂关系查询,redis实现困难,且维护成本高。

缓存用户信息的方式:

  • 使用原生字符串:优点:简单直观   缺点:键占用过多,内存占用量大,用户信息内聚性比较差;
  • 序列化字符串类型:优点:简化编程,合理使用序列化可以提高内存使用率 缺点:但序列化和反序列化有开销;
  • 哈希类型:优点:简单直观,合理使用可以减少内存空间使用 缺点:要控制ziplist和hashtable的内部编码转换,hashtable会消耗更多内存。

5 列表

列表类型是用来存储多个有序的字符串

一个列表最多可以存储

-1个元素。在redis中可以对列表两端插入和弹出,还可以获取指定范围的元素列表、获取指定索引下表的元素。

列表操作

1从右边添加元素操作

rpush key value [value....]

redis set contains的复杂度 redis del时间复杂度_数据结构_34

2  从左边插入元素

lpush key value [value.....]

redis set contains的复杂度 redis del时间复杂度_字符串_35

3 向某个元素前插入或者后插入元素

linsert key before|after pivot value

redis set contains的复杂度 redis del时间复杂度_字符串_36

若元素不存在,插入失败返回-1

查找操作

1 获取指定范围内的元素列表

lrange key start end

索引下标有两个特点:从左到右,下表为0到n-1,从右到左,下表为-1到-N。end选项包含自身。

redis set contains的复杂度 redis del时间复杂度_字符串_37

2 获取指定下标的元素

lindex key index

redis set contains的复杂度 redis del时间复杂度_redis_38

3 获取列表长度

llen key

redis set contains的复杂度 redis del时间复杂度_数据结构_39

删除操作

1 从列表中左侧弹出元素

lpop key

redis set contains的复杂度 redis del时间复杂度_数据结构_40

 

2 从列表右侧弹出元素

rpop key

redis set contains的复杂度 redis del时间复杂度_数据结构_41

3 删除指定元素

lrem key count value

  • count>0:从左到右最多删除count个元素;
  • count<0:从右到左删除最多count绝对值个元素;
  • count=0:删除所有。

redis set contains的复杂度 redis del时间复杂度_redis_42

4 按照索引范围修剪列表

ltrim key start end

截取start到end之间的元素

redis set contains的复杂度 redis del时间复杂度_数据结构_43

 

修改操作

1 修改 指定下标元素值

lset key index value

redis set contains的复杂度 redis del时间复杂度_数据结构_44

阻塞操作

1 阻塞时弹出

blpop key [key.....] timeout

brpop key [key....] timeout

  • key指定弹出的键
  • timeout:指定阻塞时间。

(1)key为空,timeout不为0时

redis set contains的复杂度 redis del时间复杂度_redis_45

阻塞timeout时间就会返回nil。

(2) key为空,timeout=0时

redis set contains的复杂度 redis del时间复杂度_redis_46

它会一直阻塞,知道键存在为止。

(3)key不空,timeout=0时会立刻返回数据

 

若brpop弹出多个键时,brpop会从左到右遍历键,只要有一个键能弹出就立刻返回;

若多个客户端同时对一个键执行brpop,那么最先执行brpop命令的客户端可以获取到弹出的值。

 

内部编码

  • ziplist(压缩列表):当列表的元素个数小于list-max-ziplist-entries配置(默认512个),同时列表中每个元素的值都小于list-max-ziplist-value配置(默认64字节)时,redis会选用ziplist来作为列表的内部实现来减少内存的使用;
  • linkedlist(链表):当列表类型无法满足ziplist的条件时,redis使用linkedlist作为列表的内部实现。

使用场景

  • 消息队列:使用lpush+brpop命令组合即可实现阻塞队列。
  • 文章列表

 

6 集合

集合用来存储不重复的字符串并且集合 是无序的。

集合内操作

1 添加元素

sadd key element [element....]

添加成功会返回添加成功的元素个数。

redis set contains的复杂度 redis del时间复杂度_redis_47

2 删除元素

srem key element [element....]

返回删除成功的元素数。

3 计算个数

scard key

它的时间复杂度为O(1), 它不会遍历集合,而是直接使用redis内部变量。

redis set contains的复杂度 redis del时间复杂度_字符串_48

4 判断元素是否在集合内

sismember key element

redis set contains的复杂度 redis del时间复杂度_redis_49

成功返回1 失败返回0

5 随机从集合中返回指定个数元素

srandmember key [count]

redis set contains的复杂度 redis del时间复杂度_redis_50

count默认为1。

6 从集合中弹出元素

spop key

redis set contains的复杂度 redis del时间复杂度_redis_51

7 获取所有元素

smembers key

redis set contains的复杂度 redis del时间复杂度_数据结构_52

集合间操作

1 求多个集合的交集

sinter key [key....]

redis set contains的复杂度 redis del时间复杂度_redis_53

2 求多个集合的并集

sunion key [key.....]

redis set contains的复杂度 redis del时间复杂度_数据结构_54

3 求多个集合的差集

sdiff key [key...]

redis set contains的复杂度 redis del时间复杂度_redis_55

4 将交集、并集、差集的结果保存

sinterstore destination key [key.....]

sunionstore destination key [key.....]

sdiffstore destination key [key.....]

集合间的运算在元素较多的情况下比较耗时,redis提供上面三个命令将集合间交集、并集、差集的结果保存在destination key中。

 

内部编码:

  • intset(整数集合):当集合中的元素都是整数且元素个数小于set-max-intset-entries配置时(默认512个),redis会选用intset作为内部实现,从而减少内存使用;
  • hashtable(哈希表):当集合类型无法满足intset的条件时,redis会使用hashtable作为集合的内部实现。

使用场景:

集合类型比较典型的使用场景是标签。

7 有序集合

命令

集合内:

1 添加成员

zadd key score member [score member.....]

redis3.2后添加了nx、xx、ch、incr四个选项。

  • nx,member不存在,才可以设置成功,用于添加;
  • xx,member必须存在,才可以设置成功,用于更新;
  • ch,返回此次操作后,有序集合元素和分数发生变化的个数;
  • incr,对score做增加,相当于后面介绍的zincrby。

有序集合虽然相比集合进行了排序,但也产生了代价,zadd的时间复杂度为O(log(n)),sadd的时间复杂度为O(1)。

redis set contains的复杂度 redis del时间复杂度_字符串_56

2 计算成员个数

zcard key

redis set contains的复杂度 redis del时间复杂度_字符串_57

3 计算某个成员的分数

zscore key member

redis set contains的复杂度 redis del时间复杂度_redis_58

4 计算成员排名

zrank key member   //从低到高返回排名

zrevrank key member  //从高到低返回排名

redis set contains的复杂度 redis del时间复杂度_数据结构_59

5 删除成员

zrem key member

redis set contains的复杂度 redis del时间复杂度_redis_60

6 增加成员的分数

zincrby key increment member

redis set contains的复杂度 redis del时间复杂度_redis_61

7 返回指定排名范围的成员

zrange key start end [withscores]  //从低到高返回指定范围的成员,withsocre表示返回分数

zrevrange key start end [withscores]   //从高到低返回指定范围的成员,withsocre表示返回分数

redis set contains的复杂度 redis del时间复杂度_redis_62

8 返回指定分数范围的成员

zrangebyscore key min max [withscores] [limit offset count] //从低到高返回min到max分数范围的成员

zrevrangebyscore key min max [withscores] [limit offset count]//从高到低返回min到max分数范围的成员

redis set contains的复杂度 redis del时间复杂度_redis_63

9 返回指定分数范围的个数

zcount key min max

redis set contains的复杂度 redis del时间复杂度_字符串_64

10 删除指定排名内的升序元素

zremrangebyrank key start end

redis set contains的复杂度 redis del时间复杂度_redis_65

11 删除指定分数范围的成员

zremrangebyscore key min max

redis set contains的复杂度 redis del时间复杂度_字符串_66

集合间的操作

1 交集

zinterstore destination numkeys key [key....] [weights weight [weight...]] [aggregate sum | min | max]

  • destination:交集计算结果保存到这个键;
  • numkeys:需要做交集计算键的个数;
  • key [key...]:需要做交集计算的键;
  • weights weight [weight...]:每个键的权重,在做交集计算时,每个键中的每个member会将自己分数乘以这个权重,每个键的权重默认是1;
  • aggregate sum|max|min:计算成员交集后,分值可以按照sum,min,max做汇总,默认值是sum。

 

redis set contains的复杂度 redis del时间复杂度_字符串_67

2 并集

zunionstore destination numkeys key [key....] [weights weight [weight...]] [aggregate sum | min | max]

redis set contains的复杂度 redis del时间复杂度_redis_68

内部编码

  • ziplist:当有序集合的元素个数小于zset-max-ziplist-entries配置(默认128个),同时每个元素的值都小于zset-max-ziplist-value配置(默认64字节),redis会使用ziplist作为内部实现;
  • skiplist:当ziplist条件不满足时,有序集合会使用skiplist作为内部实现,因为此时ziplist的读写效率下降。

使用场景:

  • 添加用户点赞数
  • 取消用户赞数
  • 展示获取赞数最多的十个用户
  • 展示用户信息以及用户分数

八 键管理

1 单个键管理

(1)键重命名

rename key newkey

redis set contains的复杂度 redis del时间复杂度_数据结构_69

renamenx key newKey   //只有当newKey不存在时才会被重命名

使用重命名命令时,有两点需要注意:

  • 由于重命名键期间会执行del命令删除旧的键,如果键对应的值比较大,会存在阻塞Redis的可能性,这点不可忽视;
  • 如果rename和renamenx中的key和newKey是相同的,在redis3.2和之前返回结果不同。

(2)随机返回一个键

randomkey

redis set contains的复杂度 redis del时间复杂度_字符串_70


(3)键过期

除了expire、ttl命令对键过期进行操作,还有expireat、pexpire、pexpireat、pttl、persist等一系列命令。

  • expire key seconds:键在seconds秒后过期;
  • expireat key timestamp:键在秒级时间戳timestamp后过期;

ttl命令和pttl命令都可以查询间的过期时间,但是pttl精度更高可以达到毫秒级别,有三种返回值:

  • 大于等于0的整数:键剩余的过期时间(ttl是秒,pttl是毫秒)
  • -1:表示键没有设置过期时间;
  • -2:表示键不存在

若要让key在2019-11-1 00:00:00过期,可设时间戳为1572537600

redis set contains的复杂度 redis del时间复杂度_数据结构_71

除此之外,redis2.6版本后提供了毫秒级的过期方案。

  • pexpire key milliseconds:键在milliseconds毫秒过期;
  • pexpireat key milliseconds-timestamp:键在毫秒级时间戳timestamp后过期。

但是无论是使用过期时间还是时间戳,秒级还是毫秒级,在redis内部最终使用的都是pexpireat。

使用redis相关命令时需注意:

  • 如果expire key的键不存在,返回结果为0;
  • 如果过期时间为负值,键会立即被删除,犹如使用del命令;
  • persisit命令可以将键的过期时间清除。
  • 对于字符串类型键,执行set命令会去掉过期时间,这个问题很容易在开发中被忽视。
  • redis不支持二级数据结构(哈希、列表)内部元素的过期时间,例如不能对列表的一个元素做过期时间设置;
  • setex命令作为set+expire的组合,不但是原子执行,同时减少了一次网络通讯的时间。

persist清楚键过期时间

redis set contains的复杂度 redis del时间复杂度_redis_72

set命令会清除键过期时间

redis set contains的复杂度 redis del时间复杂度_数据结构_73

(4)迁移键

redis支持把部分数据由一个redis迁移到另一个redis中,redis提供了move,dump+restore,migrate三组迁移的方法,他们的实现方式以及使用的场景不太相同。

  • move key db:move命令用于在redis内部进行数据迁移,redis内部可以有多个数据库。这个命令是把指定的键从源数据库迁移到目标数据中,不建议在生产环境中使用;
  • dump+restore:dump key;  restore key ttl value;dump + store可以实现在不同的redis之间进行数据迁移的功能,整个迁移的过程分为两步:1)在原redis上,dump命令回将键值序列化,格式采用的是RDB格式;2) 在目标redis上,restore命令将上面序列化的值进行复原,其中ttl参数代表过期时间,如果ttl=0代表没有过期时间。
  • migrate:migrate命令也是用于在Redis实例间进行数据迁移的,实际上migrate命令就是将dump、restore、del命令进行组合,从而简化了操作流程,migrate命令具有原子性,而且从redis3.0.6版本以后支持迁移多个键的功能,有效地提高了效率,migrate在水平扩容中起到重要作用。

>1 dump+restore

在原redis上执行dump

dump key

在目标redis上执行restore

restore key ttl value

redis set contains的复杂度 redis del时间复杂度_字符串_74

>2 migrate

migrate host post key | "" destination-db timeout [copy] [replace] [keys key [key....]]

 

2 遍历键

(1)全量遍历键

keys pattern

  • *:代表匹配任意字符;
  • ?:代表匹配一个字符;
  • []:代表匹配部分字符。
  • \x:用来做转义。

当需要遍历所有键时(例如检测过期或闲置时间、寻找大对象等),keys是一个很有帮助的命令,例如想删除所有以video字符串揩油的键,可以执行如下操作:

redis-cli keys video* | xargs redis-cli del

当redis中键值比较多时,keys命令可能会导致redis阻塞。可以采用渐进式遍历避免阻塞。

(2) 渐进式遍历

redis2.8以后提供了一个新命令scan,它能有效的解决keys命令存在的问题。scan采用渐进式遍历方式解决keys阻塞问题,每次会遍历字典中的一部分键,直到键遍历完毕。

scan cursor [match pattern] [count number]

  • cursor:是必须参数,cursor是一个游标,第一次遍历从0开始,每次scan遍历完都会返回当前游标的值,直到游标值为0,表示遍历结束;
  • match pattern:是可选参数,做模式匹配,这点和keys的模式匹配相似;
  • count number:是可选参数,表明每次要遍历的键的个数,默认10

redis set contains的复杂度 redis del时间复杂度_数据结构_75

渐进式遍历能够有效解决keys阻塞问题,但是scan过程中如果有键的变化,那么遍历效果就会产生如下问题:新增的键可能没有遍历到,遍历重复的键。也就是说scan不能完整遍历出所有键。

3 数据库管理

(1)切换数据库

select dbIndex

redis默认配置中有16个数据库。多数据库的缺点:

  • redis是单线程的,如果使用多个数据库,那么这些数据库仍然是使用一个CPU,彼此之间还是会受到影响的;
  • 多数据库的使用方式,会让调试和运维不同业务的数据库变得困难,假如有一个慢查询存在,依然会影响其他数据库。这样会使得别的业务方定位问题困难;
  • 部分redis客户端不支持这种方式,即使支持,开发难度也大。

(2)flushdb/flushall

flushdb/flushall用于清楚数据库,两者区别是flushdb用于清除当前数据库,flushlall会清除所有数据库。

  • flushdb/flushall命令会将所有数据清除,一旦误操作后果不堪设想;
  • 如果当前数据库键值比较多,flushdb/flushall存在阻塞redis的可能性。