2.Redis五种数据结构
2.1 预备
2.1.1 全局命令
2.1.2 数据结构和内部编码
2.1.3 单线程架构
2.2 字符串
2.2.1 命令
2.2.2 内部编码
2.2.3 典型使用场景
2.3 哈希
2.3.1 命令
2.3.2 内部编码
2.3.3 使用场景
2.4 列表
2.4.1 命令
2.4.2 内部编码
2.4.3 使用场景
2.5 集合
2.5.1 命令
2.5.2 内部编码
2.5.3 使用场景
2.6 有序集合
2.6.1 命令
2.6.2 内部编码
2.6.3 使用场景
2.7 键管理
2.7.1 单个键管理
2.7.2 键遍历
2.7.3 数据库管理
2.8 本章重点回顾
2.1 预备
2.1.1 全局命令:键的通用命令。
(1)查看所有键:keys *
keys命令会遍历所有键,keys命令时间复杂度是O(n)。如果Redis有大量键时,则需要禁止使用keys命令。
(2)键总数:dbsize
dbsize命令在计算键总数时不会遍历所有键,而是直接获取Redis内置的键总数变量,dbsize命令的时间复杂度是O(1)。
(3)检查键是否存在:exists key
如果键存在则返回1,如果键不存在则返回0。
(4)删除键:del key [key ...]
del命令用于删除键,不用关注键值的类型;支持一次删除多个键,返回结果是删除键的个数,删除一个不存在的键时则返回结果为0。
(5)键过期:expire key seconds
redis支持对键添加过期时间,当超过过期时间后,键会被redis自动删除。
ttl命令会返回键的剩余过期时间,返回值大于等于0时,表示键的剩余过期时间;返回值为-1时,表示键没有设置过期时间(永久键);返回值为-2时,表示键不存在。
set hello world
expire hello 20
ttl hello
ttl hello1
(6)键的数据结构类型:type key
如果键不存在,则返回none;如果键存在,则根据键值的类型返回string\hash\list\set\zset等。
2.1.2 数据结构和内部编码
数据结构类型分为string字符串\hash哈希\list列表\set集合\zset有序集合\Bitmaps位图\HyperLogLog\GEO地理信息定位等。
每种数据结构类型都有自己的内部编码实现,且是多种内部编码实现,对应关系如下:
string raw/int/embstr
hash hashtable/ziplist
list linkedlist/ziplist
set hashtable/intset
zset skiplist/ziplist
查看键的内部编码:object encoding key
数据结构与内部编码分离的好处:redis改进内部编码,不影响用户对redis数据结构的使用;同一种数据结构采用多种内部编码以使用不同的业务场景。
2.1.3 单线程架构
Redis使用单线程架构和I/O多路复用模型来实现高性能的内存数据库服务。
客户端调用流程:发送命令、执行命令、返回结果。
Redis单线程架构应对多客户端并发:一条命令从客户端发送到服务端后不会立刻被执行,(所有命令都)会进入一个对列中,然后逐个被执行,同一时刻只有一条命令被执行,从而避免了并发执行带来的问题。
Redis采用单线程架构的原因:
纯内存访问,Redis将所有数据保存在内存中,内存的响应时长约为100ns,从而保证了Redis每秒万级的访问速度。
非阻塞I/O,Redis使用epoll作为I/O多路复用技术的实现,将epoll中的连接、读写、关闭都转换成事件,减少了网络I/O的延迟。
单线程避免了线程切换和竞态锁产生的消耗。
单线程可以简化数据结构和算法的实现。
单线程的问题:命令按顺序执行,如果一条命令执行时间过长将导致系统阻塞。
2.2 字符串
键都是字符串类型。
字符串类型的键值可以是字符串(包括:JSON和XML等)、数字(包括:整数、浮点数)、二进制(最大不能超过512MB)。
字符串类型是其它数据结构的基础。
2.2.1 命令
(1)设置值:set key value [ex seconds] [px milliseconds] [nx|xx]
输出:成功时返回OK。setnx成功时返回1;失败返回0。
参数说明:
ex seconds:为键设置秒级的过期时间。
px milliseconds:为键设置毫秒级的过期时间。
nx:键必须不存在时才可以设置成功,用于添加,并发setnx时只有一个客户端进程能够成功执行。
xx:键必须存在时才可以设置成功,用于修改。
相关命令:
setex key seconds value
setnx key value
例子:
exists hello
set hello world
setnx hello redis
set hello redis xx
(2)获取值:get key
输出:成功时返回key对应的value值;key不存在时返回nil(空)。
例子:
get hello
get hello1
(3)批量设置值:mset key value [key value ...]
例子:
mset a 1 b 2 c 3
(4)批量获取:mget key [key ...]
例子:
mget a b c
mget a b c d 批量获取中如果有不存在的键,则不存在的键对应的取值为nil,按照键的顺序输出。
注意:批量操作可以优化网络时间(不能优化命令时间),从而优化了客户端响应时间。
客户端响应时间=网络时间+命令时间
分别n次get操作的客户端响应时间=n次网络时间+n次命令时间
1次mget操作n个key的客户端响应时间=1次网络时间+n次命令时间
(5)计数(对key值做自增操作):incr key
输出:值不是整数时返回错误;值是整数时返回自增后的结果;键不存在时以0作为初始值,返回结果1。
例子:
exists key
incr key
incr key
set hello world
incr hello
相关命令:
decr key 自减
incrby key increment 自增指定数字
decrby key increment 自减指定数字
incrbyfloat key increment 自增指定浮点数
例子:
decr key 键不存在时以0作为初始值,返回结果-1;值是整数时返回自减后的结果;值不是整数时返回错误。
incrby key 2
decrby key 3
increbyfloat key 2.2
increbyfloat key -1.8
(6)追加值:append key value 向字符串尾部增加一段字符串。
输出:原有字符串值与追加字符串合并后的总长度。
例子:
get hello
append hello hi
append hello " hi"
(7)字符串长度:strlen key
输出:字符串长度的字节数,汉字为3字节。
例子:
strlen hello
set hi "您好!"
(8)设置并返回原值:getset key value
输出:将key的值设置为新值,并将key的原值返回。
例子:
get hello
getset hello world
(9)设置指定位置的字符串:setrange key offset value
输出:将offset位置开始的字符替换为新的value,offset从0开始,原字符串的长度不变,成功返回原字符串的长度。
例子:
get hello
setrange hello 0 "hi w"
(10)获取部分字符串:getrange key start end
输出:start和end分别是开始和结束的偏移量,偏移量从0开始。
get hello
getrange hello 0 2
2.2.2 内部编码
字符串类型的内部编码分为3种,Redis会根据当前值的类型和长度决定使用哪种内部编码。
int:8个字节的长整形。
embstr:小于等于44B的字符串。
raw:大于44B的字符串。
例子:
set int 123456
object encoding int
set embstr "abcdefghijklmnopqrstuvwxyz"
object encoding embstr
set raw "abcdefghijklmnopqrstuvwxyz0123456789+-*0123/"
object encoding raw
del raw
set raw "abcdefghijklmnopqrstuvwxyz0123456789+-*01234/"
object encoding raw
strlen raw
2.2.3 典型使用场景
(1)缓存功能:用Redis作为MySQL的缓存层,以加速读写减少MySQL的压力。
(2)计数:使用Redis实现快速计数功能。
(3)共享Session:分布式系统导致用户Session分布到各自服务器上,导致刷新后需要重新登陆的问题,将用户Session统计保存在Redis中能解决该问题。
(4)限速:用Redis的计数功能限制同一账户每分钟的登陆频率。
2.3 哈希
哈希类型是指键值本身又是一个键值对结构,例如:value={{field1,value1},...{fieldN,valueN}}。
哈希类型中映射关系叫做field-value,此处的value是指field对应的值,而哈希类型整体作为键的值。
2.3.1 命令
(1)设置值:hset key field value [field value ...]
功能:为一个key的一个或多个field设置值
输出:成功时返回1(设置值的field个数);失败返回0。
例子:
hset user:1 name tom
hset user:2 name tom2 age 28
相关命令:
hsetnx key field value 同一个key的同一个field不存在时可以设置成功并返回1,否则失败返回0
例子:
hsetnx user:1 name tom1 --0
hsetnx user:1 name1 tom --1
(2)获取值:hget key field
功能:获取一个key的一个field的值
输出:成功时返回key+field对应的值;key或field不存在时返回nil(空)。
例子:
gset user:1 name
gset user:2 age
(3)删除field:hdel key field [field ...]
功能:删除key下的一个或多个field。key下field全部被删除后key也被删除。
例子:
hdel user:1 name1
hdel user:2 name age
exists user:2
(4)计算field个数:hlen key
功能:返回key下的field个数;key不存在时返回0。
例子:
hlen user:1
hlen user:2
(5)批量设置、批量获取filed-value
hmset key field value [field value ...]
hmget key field [field ...]
功能:为一个key的一个或多个field设置值;获取一个key的一个或多个field的值。
输出:hmset成功返回OK。hmget成功后按顺序输出多个field的值。
例子:
hmset user:2 name tom age 28
hmget user:2 name age
(6)判断field是否存在:hexists key field
输出:存在返回1;不存在返回0。
例子:
hexists user:1 age
hexists user:2 age
(7)获取所有field:hkeys key
输出:获取key下的所有filed名,如果key不存在返回空。
例子:
hkeys user:1
hkeys user:2
hkeys user:0
(8)获取所有value:hvals key
输出:获取key下的所有filed的值,如果key不存在返回空。
例子:
hvals user:1
hvals user:2
hvals user:0
(9)获取所有field-value:hgetall key
输出:获取key下的所有filed的名和值,如果key不存在返回空。注意:key下的元素过多时可能会阻塞Redis,建议使用hmget和hscan命令。
例子:
hgetall user:1
hgetall user:2
hgetall user:0
(10)元素值自增
hincrby key field increment 自增整数
hincrbyfloat key field increment 自增浮点数
输出:自增后的元素值。
例子:
hincrby user:2 age 1
hincrbyfloat user:2 age 1.2
(11)计算value的字符串长度:hstrlen key field
输出:元素值的长度。
例子:
hstrlen user:1 name
hstrlen user:2 age
2.3.2 内部编码
哈希类型的内部编码分为2种,Redis会根据当前值的类型和长度决定使用哪种内部编码。
ziplist压缩列表:当哈希类型的元素个数小于512个(hash-max-ziplist-entries参数默认配置)并且所有元素的值都小于等于64B(hash-max-ziplist-value参数默认配置)时,Redis会使用ziplist作为哈希类型的内部编码。
hashtable哈希表:当哈希类型无法满足元素个数小于512个或这所有元素的值都小于等于64B的条件时,Redis会使用hashtable作为哈希类型的内部编码。
注意:ziplist使用的存储更小;hashtable读写时间复杂读为O(1)。
例子:
hset user:3 name tom3 age 28
object encoding user:3
hset user:4 name "1234567890123456789012345678901234567890123456789012345678901234"
object encoding user:4 --ziplist
del user:4
hset user:4 name "12345678901234567890123456789012345678901234567890123456789012345"
object encoding user:4 --hashtable
hset user:5 f1 v1 f2 v2 ... f513 v513
object encoding user:5
2.3.3 使用场景
可以使用一个哈希类型的key来映射数据库表中的一行信息,key的命名为[db:]table:id。
注意哈希类型与关系型数据库有两点不同:
哈希类型是稀疏的,而关系型数据库是完全结构化的。
关系型数据库可以做复杂的查询,而使用Redis模拟复杂查询会非常困难。
三种缓存表信息方案的方案对比:
(1)原生字符串类型:表中一行数据的每个属性的取值用一个键保存。
例如:
set user:1:name tome
set user:1:age 23
set user:1:city beijing
优点:简单直观,每个属性都支持更新操作。
缺点:占用过多的键,内存占用大,内聚性差。一般不用。
(2)序列化字符串类型:将表中一行数据序列化后用一个键保存。
例如:
set user:1 serialize(UserInfo)
优点:开发简单,提高了内存的使用效率。
缺点:序列化和反序列化操作有一定开销,更新单个属性时需要将全部数据取出进行反序列化,更新后再进行序列化。
(3)哈希类型:将表中一行数据用一个哈希类型键保存。
例如:
hmset user:1 name tom age 23 city beijing
优点:简单直观,提高了内存的使用效率。
缺点:需要控制哈希在ziplist和hashtable两种内部编码之间的转换。数据库表的列个数应控制在512个以内,列长度控制在64B以内(如果不能满足时,可以调整hash-max-ziplist-value参数的默认配置)。
2.4 列表
列表(list)类型是用来存储多个有序的字符串,每个字符串成为一个元素,元素从左到右有序,一个列表最多可以存储2^32-1个元素。
Redis中列表可以从两端插入(push)元素、弹出(pop)元素,还可以获取指定下标的元素、获取指定下标范围的元素列表。
列表可以作为栈和对列使用。
列表类型的两个特点:元素是有序的;元素可以重复。
2.4.1 命令
(1)添加
语法及功能:
lpush key value [value ...] --从左边插入一个或多个元素
rpush key value [value ...] --从右边插入一个或多个元素
linsert key before pivot value --将value元素插入到pivot元素之前
linsert key after pivot value --将value元素插入到pivot元素之后
例子:
lpush list1 a b c d
rpush list1 1 2 3 4
linsert list1 before b "xyz"
linsert list1 after c "test"
(2)查找
语法及功能:
lrange key strart stop --获取strart和stop下标范围内的元素,索引下标从左到分别是0到N-1,从右到左分别是-1到-N;stop选项包含自身。获取全部元素lrange key 0 -1
lindex key index --获取指定下标index的元素
llen key --获取列表key的长度,即元素个数。
例子:
lrange list1 0 -1
lrange list1 1 5
lindex list1 2
llen list1
(3)删除
语法及功能:
lpop key [count] --从列表左侧弹出count个元素,count默认为1
rpop key [count] --从列表右侧弹出count个元素,count默认为1
lrem key count value --删除指定的value元素。count=0时删除所有的value元素;count>0时从左到右删除count个value元素;count<0时从右到左删除count绝对值个value元素。
ltrim key start stop --保留start和stop范围内的元素,即删除不在start和stop范围内的元素。
例子:
lrange list1 0 -1
lpop list1
lpop list1 2
rpop list1
rpop list1 2
ltrim key 1 2
lpush list1 a a a
rpush list1 b b b
lrem list1 2 b
lrem list1 -3 a
lrem list1 0 b
(4)修改
语法及功能:
lset key index newValue --将指定下标index的元素修改为newValue。如果指定的下标元素不存在将报错。
例子:
lset list1 0 hello
(5)阻塞式弹出
语法及功能:
blpop key [key ...] timeout --列表不为空时,从一个或多个列表左侧弹出一个元素;列表为空时,阻塞timeout秒之后返回,timeout为0时会一直阻塞;多个列表时有一个列表不为空则弹出,所有列表都为空则阻塞;多个客户端并发时,第一个客户端可能会弹出,其余客户端则可能阻塞。
brpop key [key ...] timeout --列表不为空时,从一个或多个列表右侧弹出一个元素;列表为空时,阻塞timeout秒之后返回,timeout为0时会一直阻塞;多个列表时有一个列表不为空则弹出,所有列表都为空则阻塞;多个客户端并发时,第一个客户端可能会弹出,其余客户端则可能阻塞。
例子:
lrange list1 0 -1
blpop list1 2
blpop list1 2
2.4.2 内部编码
列表类型的内部编码分为3种,Redis默认使用quicklist快速列表。
ziplist压缩列表:当列表类型的元素个数小于512个(list-max-ziplist-entries参数默认配置)并且所有元素的值都小于等于64B(list-max-ziplist-value参数默认配置)时,Redis早期会使用ziplist作为列表类型的内部编码。
linkedlist链表:当列表类型无法满足元素个数小于512个或这所有元素的值都小于等于64B的条件时,Redis早期会使用linkedlist作为列表类型的内部编码。
quicklist快速列表:以一个ziplist为节点的linkedlist。quicklist结构是在Redis 3.2版本中新加的数据结构,用在列表的底层实现。
quicklist宏观上是一个双向链表,因此,它具有一个双向链表的有点,进行插入或删除操作时非常方便,虽然复杂度为O(n),但是不需要内存的复制,提高了效率,而且访问两端元素复杂度为O(1)。
quicklist微观上是一片片entry节点,每一片entry节点内存连续且顺序存储,可以通过二分查找以 log2(n) 的复杂度进行定位。
quicklist的结构为什么这样设计呢?总结起来,大概又是一个空间和时间的折中:
双向链表便于在表的两端进行push和pop操作,但是它的内存开销比较大。首先,它在每个节点上除了要保存数据之外,还要额外保存两个指针;其次,双向链表的各个节点是单独的内存块,地址不连续,节点多了容易产生内存碎片。
ziplist由于是一整块连续内存,所以存储效率很高。但是,它不利于修改操作,每次数据变动都会引发一次内存的realloc。特别是当ziplist长度很长的时候,一次realloc可能会导致大批量的数据拷贝,进一步降低性能。
在quicklist表头结构中,有两个成员是fill和compress,其中” : “是位域运算符,表示fill占int类型32位中的16位,compress也占16位。fill和compress的配置文件是redis.conf。
fill成员对应的配置:list-max-ziplist-size -2
当数字为负数,表示以下含义:
-1 每个quicklistNode节点的ziplist字节大小不能超过4kb。(建议)
-2 每个quicklistNode节点的ziplist字节大小不能超过8kb。(默认配置)
-3 每个quicklistNode节点的ziplist字节大小不能超过16kb。(一般不建议)
-4 每个quicklistNode节点的ziplist字节大小不能超过32kb。(不建议)
-5 每个quicklistNode节点的ziplist字节大小不能超过64kb。(正常工作量不建议)
当数字为正数,表示:ziplist结构所最多包含的entry个数。最大值为 215。
compress成员对应的配置:list-compress-depth 0
后面的数字有以下含义:
0 表示不压缩。(默认)
1 表示quicklist列表的两端各有1个节点不压缩,中间的节点压缩。
2 表示quicklist列表的两端各有2个节点不压缩,中间的节点压缩。
3 表示quicklist列表的两端各有3个节点不压缩,中间的节点压缩。
以此类推,最大为 216。
例子:
rpush list:1 1 2 --quicklist
object encoding list:1
rpush list:2 name tom3 age 28 --quicklist
object encoding list:2
rpush list:3 name "1234567890123456789012345678901234567890123456789012345678901234"
object encoding list:3 --quicklist
rpush list:4 name "12345678901234567890123456789012345678901234567890123456789012345"
object encoding list:4 --quicklist
rpush list:5 l1 l2 ... l513
object encoding list:5
2.4.3 使用场景
(1)lpush+lpop=栈
(2)lpush+rpop=队列
(3)lpush+ltrim=有限集合
(4)lpush+brpop=消息队列
2.5 集合
集合(set)类型是用来存储多个有序的字符串,每个字符串成为一个元素,元素无序、不能重复(唯一)、不能通过下标访问。
Redis支持集合内元素的增删改查,支持多个集合的之间的并、交、查操作。
2.5.1 命令
(1)添加元素
语法及功能:
sadd key element [element ...] --给key添加一个或多个元素,成功后返回添加的元素个数。
例子:
sadd set1 a b c
(2)删除元素
语法及功能:
srem key element [element ...] --将key中的一个或多个元素删除,成功后返回删除的元素个数。
例子:
srem set1 c
(3)计算元素个数
语法及功能:
scard key --计算元素个数。scard的时间复杂读为O(1),直接获取Redis内部变量,而不会扫描整个集合。
例子:
scard set1
(4)判断元素是否在集合中
语法及功能:
sismember key element --判断元素是否在集合中,给定的元素element如果在key集合中则返回1,不在则返回0。
例子:
sismember set1 b
sismember set1 c
(5)随机从集合返回指定个数的元素
语法及功能:
srandmember key [count] --随机从集合返回count个元素,count默认为1。srandmember执行后元素不会被删除。
例子:
srandmember set1
sadd set1 c d e
srandmember set1 3
(6)从集合随机弹出元素
语法及功能:
spop key [count] --从集合随机弹出count个元素,count默认为1。spop执行后元素会被删除。
例子:
spop set1
spop set1 2
(7)获取所有元素
语法及功能:
smembers key --返回集合key中的所有元素。时间复杂度O(n),n是集合中的元素总数。元素过多时会引发Redis阻塞,建议使用sscan替代。
例子:
smembers set1
(8)交集
语法及功能:
sinter key [key ...] --求多个集合的交集。
例子:
sadd set2 hello world
sadd set3 hello redis
sinter set2 set3
(9)并集
语法及功能:
sunion key [key ...] --求多个集合的并集。
例子:
sunion set2 set3
(10)差集
语法及功能:
sdiff key [key ...] --求多个集合的差集。
例子:
sdiff set2 set3
sdiff set3 set2
(11)保存交集、并集、差集
语法及功能:
sinterstore destination key [key ...] --sinter+store将集合的交集的结果保存在destination键中。
sunionstore destination key [key ...] --sunion+store将集合的并集的结果保存在destination键中。
sdiffstore destination key [key ...] --sdiff+store将集合的查集的结果保存在destination键中。
例子:
sinterstore result1 set2 set3
sunionstore result2 set2 set3
sdiffstore result3 set2 set3
sdiffstore result4 set3 set2
smembers result1
smembers result2
smembers result3
smembers result4
2.5.2 内部编码
集合类型的内部编码分为2种,Redis会根据当前值的类型和长度决定使用哪种内部编码。
intset整数集合:当集合类型的元素都是整数且元素个数小于512个(set-max-intset-entries参数默认配置)时,Redis会使用intset作为集合类型的内部编码(减少内存使用)。
hashtable哈希表:当集合类型无法满足元素都是整数或元素个数小于512个的条件时,Redis会使用hashtable作为集合类型的内部编码。
注意:intset使用的存储更小;hashtable读写时间复杂读为O(1)。
例子:
sadd set4 1 2 3
object encoding set4 --intset
object encoding result4 --hashtable
2.5.3 使用场景
集合类型可以作为标签使用。
标签是一个人的兴趣爱好(兴趣点),通过给不同人记录不同标签、给同一个人记录多个标签,从而做用户画像及增强用户体验等。
sadd=Tagging(标签)
spop/srandmember=Random item(生成随机数,抽奖)
sadd+sinter=Social Graph(社交需求)
sadd user:1:tags tag1 tag2 tag3 --给用户添加标签
sadd tag1:users user:1 user:3 --给标签添加用户
srem user:1:tags tag1 --删除用户下的标签
srem tag1:users user:1 --删除标签下的用户
sinter user:1:tags user:2:tags --计算两用户共同都感兴趣的标签
2.6 有序集合
有序集合(zset)类型保留了集合(set)类型元素不能重复的特性,并增加了元素有序的特性(按分值score有序)。
2.6.1 命令
(1)添加元素
语法及功能:
zadd key [NX|XX] [GT|LT] [CH] [INCR] score member [score member ...]
NX:member必须不存在才能添加成功,用于新增。
XX:member必须存在才能添加成功,用于更新。
GT:大于
LT:小于
CH:返回此次操作后,有序集合元素和分数发生变化的个数。
INCR:对score做自增操作,相当于zincrby。
有序集合比集合多提供了排序字段(score),时间复杂度增加,zadd时间复杂度O(log(n)),sadd时间复杂度O(1)。
例子:
zadd zset1 100 a 90 b 80 c
zrangebyscore zset1 0 100 withscores
zadd zset1 70 d 60 e 50 f
zrangebyscore zset1 0 100 withscores
(2)计算元素个数
语法及功能:
zcard key --计算元素个数。zcard的时间复杂读为O(1),直接获取Redis内部变量,而不会扫描整个集合。
例子:
zcard zset1
(3)计算某个元素的分数
语法及功能:
zscore key member --获取有序集合key中member元素的分数score的值;如果元素不存在则返回空。
例子:
zscore zset1 a
zscore zset1 hello
(4)计算成员排名
语法及功能:
zrank key member --按分数由低到高对元素进行排名(排名从0开始),然后返回member的名次。
zrevrank key member --按分数由高到低对元素进行排名(排名从0开始),然后返回member的名次。
例子:
zrank zset1 f
zrevrank zset1 a
(5)删除成员
语法及功能:
zrem key member [member ...] --删除有序集合中的一个或多个成员
例子:
zrem zset1 f
zrem zset1 a d
(6)增加成员分数
语法及功能:
zincrby key increment member --随机从集合返回count个元素,count默认为1。srandmember执行后元素不会被删除。
例子:
zincrby zset1 5 b
zincrby zset1 -11 c
(7)返回指定排名范围的成员
语法及功能:
zrange key min max [byscore|bylex] [rev] [limit offset count] [withscores] --zrange把成员由低到高排序,返回排名在min和max之间成员,[withscores]返回成员分数。
zrevrange key start stop [withscores] --zrevrange把成员由高到低排序,返回排名在start和stop之间成员,[withscores]返回成员分数。
例子:
zrange zset1 0 2
zrevrange zset1 0 2 withscores
(8)返回指定分数范围的成员
语法及功能:
zrangebyscore key min max [withscores] [limit offset count] --zrangebyscore把成员由低到高排序,返回分数在min和max之间的成员,[withscores]返回成员分数。
zrevrangebyscore key max min [withscores] [limit offset count] --zrevrangebyscore把成员由高到低排序,返回排名在max和min之间成员,[withscores]返回成员分数。
max和min支持开区间(、闭区间](默认为闭区间)、无限小-inf、无限大+inf等符号。
例子:
zrangebyscore zset1 70 100
zrevrangebyscore zset1 100 70 withscores
zrangebyscore zset1 (70 100
zrangebyscore zset1 [70 100 --ERR min or max is not a float
zrangebyscore zset1 70 100) --ERR min or max is not a float
zrangebyscore zset1 [70 100] --ERR min or max is not a float
zrangebyscore zset1 -inf +inf
zrevrangebyscore zset1 (100 70 withscores
zrevrangebyscore zset1 100 70) withscores --ERR min or max is not a float
(9)返回指定分数范围成员个数
语法及功能:
zcount key min max --min和max是分数范围,返回指定分数范围内的成员个数。
例子:
zcount zset1 60 90
(10)删除指定排名内范围的成员
语法及功能:
zremrangebyrank key start stop --删除排名在start和stop内的元素。
例子:
zremrangebyrank zset1 0 2
(11)删除指定分数范围内的成员
语法及功能:
zremrangebscore key min max --sinter+store将集合的交集的结果保存在destination键中。
例子:
zremrangebscore zset1 70 80
(12)交集
语法及功能:
zinter numkeys key [key ...] [WEIGHTS weight [weight ...]] [AGGREGATE SUM|MIN|MAX] [WITHSCORES] --计算两个有序集合的交集并输出
zinterstore destination numkeys key [key ...] [WEIGHTS weight [weight ...]] [AGGREGATE SUM|MIN|MAX] --计算两个有序集合的交集并保存至目标有序集合。
选项说明:
WITHSCORES:输出分值。
numkeys:需要做交集、并集计算的键个数。
key [key ...]:需要做交集、并集计算的键。
[WEIGHTS weight [weight ...]]:每个需要做交集、并集计算的键的权重,默认为1。
[AGGREGATE SUM|MIN|MAX]:做交集、并集计算后,分值的汇总算法,默认使用SUM。
例子:
zadd zset2 100 a 90 b
zadd zset3 100 a 80 c
zinter 2 zset2 zset3 WEIGHTS 1 0.5 AGGREGATE SUM WITHSCORES
zinterstore r1 2 zset2 zset3 WEIGHTS 1 0.5 AGGREGATE SUM
zrangebyscore r1 -inf +inf withscores
(13)并集
语法及功能:
zunion numkeys key [key ...] [WEIGHTS weight [weight ...]] [AGGREGATE SUM|MIN|MAX] [WITHSCORES] --计算两个有序集合的并集并输出
zunionstore destination numkeys key [key ...] [WEIGHTS weight [weight ...]] [AGGREGATE SUM|MIN|MAX] --计算两个有序集合的并集并保存至目标有序集合。
选项说明:
WITHSCORES:输出分值。
numkeys:需要做交集、并集计算的键个数。
key [key ...]:需要做交集、并集计算的键。
[WEIGHTS weight [weight ...]]:每个需要做交集、并集计算的键的权重,默认为1。
[AGGREGATE SUM|MIN|MAX]:做交集、并集计算后,分值的汇总算法,默认使用SUM。
例子:
zunion 2 zset2 zset3 WEIGHTS 1 0.5 AGGREGATE SUM WITHSCORES
zunionstore r2 2 zset2 zset3 WEIGHTS 1 0.5 AGGREGATE MAX
zrangebyscore r2 -inf +inf withscores
2.6.2 内部编码
有序集合类型的内部编码分为2种,Redis会根据当前值的类型和长度决定使用哪种内部编码。
ziplist压缩列表:当有序集合类型的元素个数小于等于128个(zset-max-ziplist-entries参数默认配置)并且所有元素的值都小于等于64B(zset-max-ziplist-value参数默认配置)时,Redis会使用ziplist作为有序集合类型的内部编码。
skiplist跳跃表:当有序集合类型无法满足ziplist压缩列表的条件时,Redis会使用skiplist跳跃表作为有序集合类型的内部编码。
例子:
zadd zset4 100 a
object encoding zset4 --ziplist
zadd zset5 100 "1234567890123456789012345678901234567890123456789012345678901234"
object encoding zset5 --ziplist
del zset5
hset zset5 100 "12345678901234567890123456789012345678901234567890123456789012345"
object encoding zset5 --skiplist
hset zset6 1 a1 2 a2 ... 128 a 128 129 a129
object encoding zset6
2.6.3 使用场景
有序集合类型适用于排行榜系统。
zadd+zioncrby 可以记录获赞数等信息。
zrevrangebyrank key 0 9 可以获取前10的信息。
zscore+zrank 可以获取成员的分数和排名
2.7 键管理
2.7.1 单个键管理
单个键命令包括:type、object、exists、del、rename、randomkey、expire、pexpire、expireat、pexpireat、ttl、pttl、persist、move、dump、restore、migrate等。
(1)键的数据结构类型:type key
如果键不存在,则返回none;如果键存在,则根据键值的类型返回string\hash\list\set\zset等。
(2)查看键的内部编码:object encoding key
如果键不存在,则返回none;如果键存在,则根据键值的类型返回ziplist\hashtable\quicklist\intset\skiplist等。
(3)检查键是否存在:exists key
如果键存在则返回1,如果键不存在则返回0。
(4)删除键:del key [key ...]
del命令用于删除键,不用关注键值的类型;支持一次删除多个键,返回结果是删除键的个数,删除一个不存在的键时则返回结果为0。
(5)键重命名
语法及功能:
rename key newkey 对键进行更名操作,如果newkey不存在,则更名;如果newkey已存在,则覆盖。
renamex key newkey 对键进行更名操作,如果newkey不存在,则更名;如果newkey已存在,则失败。
注意:更名操作会创建newkey并删除key,如果值较大时可能会引起阻塞。
例子:
keys * --30
get python --redis-py
rename python python1 --python1不存在,则更名
keys * --30
get a --1
rename python1 a -- a存在,则覆盖
keys * -- 29,键总数减少了一个
get a --redis-py
renamenx a b --b存在,renamenx更名失败,返回结果0。
renamenx a a1 --a1不存在,renamenx更名成功,返回结果1。
(6)随机返回一个键:randomkey
randomkey随机返回一个键,键总数不会发生变化。
例子:
dbsize
randomkey
keys *
(7)键过期
语法及功能:
expire key seconds 键在seconds秒后过期。redis支持对键添加过期时间,当超过过期时间后,键会被redis自动删除。
expireat key timestamp 键在秒级的时间戳后过期
pexpire key milliseconds 键在milliseconds毫秒后过期
pexpireat key milliseconds-timestamp 键在毫秒级的时间戳后过期。Redis内部使用的都是毫秒级时间戳。
ttl key ttl命令会返回键的剩余过期时间,返回值大于等于0时,表示键的剩余过期时间;返回值为-1时,表示键没有设置过期时间(永久键);返回值为-2时,表示键不存在。
pttl key pttl命令会返回键的剩余过期时间(毫秒级)
persist key 取消键的过期时间限制,使键永远不会过期,永久键。
例子:
set hello world
expire hello 200
ttl hello
persist hello
ttl hello
(8)迁移键
迁移键是指将一部分数据从一个Redis迁移到另一个Redis,实现方法分为:move、dump+restore、migrate三种。
语法及功能:
move key db move命令可以将key从Redis内的一个DB迁移另一个DB。
dump key dump命令会将键值key序列化,采用RDB格式保存为文件,将该文件拷贝至其它Redis上并通过restore命令复原序列化的值,即完成不同Redis的数据迁移。
restore key ttl value restore命令将RDB文件中的键值序列化进行复原,ttl参数指过期时间,ttl=0即不会过期。
migrate host port key|"" destination-db timeout [COPY] [REPLACE] [AUTH password] [AUTH2 username password] [KEYS key [key ...]]
migrate=dump+restore+del,实现数据在Redis之间迁移。
migrate命令具有原子性,支持一次迁移多个键,在源Redis执行即可。
参数说明:
host:目标Redis的IP地址。
port:目标Redis的端口号。
key|"":迁移一个键时为具体的键名,迁移多个键时为空。
destination-db:目标Redis的数据库。
timeout:迁移的超时时间。
[COPY]:迁移后是否删除源Redis中的键,默认删除。
[REPLACE]:如果目标Redis中已存在该键是否对其进行覆盖,默认不覆盖。
[KEYS key [key ...]]:需要迁移的多个键。
例子:
keys *
move a1 1
keys * -- a1从0号DB中删除
select 1 --切换到1号DB
keys * --a1已迁移至1号DB
select 0
keys *
dump hello --序列化后的键值 "\x00\x05world\t\x00\xc9#mH\x84/\x11s"
select 1
restore hello 0 "\x00\x05world\t\x00\xc9#mH\x84/\x11s" --对键值序列化进行复原
get hello
select 0
keys *
migrate 192.168.234.14 6379 zset5 0 1000
migrate 192.168.234.14 6379 "" 0 1000 b c java
2.7.2 键遍历
键遍历命令包括:kyes、scan、hscan、sscan、zscan。
(1)全量遍历键:
语法及功能:
keys pattern
pattern使用glob风格的通配符:
*代表匹配任意字符
?代表匹配一个字符
[]代表匹配部分字符,例如:[1,5]表示匹配数字1或者5,[0-9]表示匹配所有数字,[a-z]表示匹配所有小写字母,[A-Z]表示匹配所有大写字母。
\x代表需要做转义,例如:\x*,\x?,\x[,\x],\x\等。
例子:
keys * 查找所有键,keys命令时间复杂度是O(n)。如果Redis有大量键时,则需要禁止使用keys命令。
keys [r,j]edis 查找redis或jedis的键
keys h?ll* 查找h?ll*的键
redis-cli keys video* | xargs redis-cli del 查找到video*键并删除
注意:不建议在生产环境使用keys命令,keys命令时间复杂度是O(n)。如果Redis有大量键,为避免keys命令引发阻塞,建议使用scan命令。
(2)渐进式遍历
语法及功能:
scan cursor [MATCH pattern] [COUNT count] [TYPE type]
cursor:是一个游标,第一次遍历从0开始,每次scan完都会返回当前游标的值,再次scan当前游标的值,直到返回的游标值为0(表示遍历结束)。
[MATCH pattern]:表示使用模式匹配,与keys的模式匹配一致。
[COUNT count]:表示每次要遍历的键个数,默认值是10。
[TYPE type]: 表示只输出指定数据类型的键,如:string\list\hash\set\zset等。
例子:
scan 0 match * count 10 --10
scan 10 match * count 10 --13
scan 13 match * count 10 --0
scan 0 match * count 10 type string --10
scan 10 match * count 10 type string --13
scan 13 match * count 10 type string --0
scan 0 match * count 10 type list --10
scan 0 match * count 10 type hash --10
scan 0 match * count 10 type set --10
scan 0 match * count 10 type zset --10
(3)渐进式遍历hash\set\zset
对于hash\set\zset类型元素较多的情况,使用hgetall\smembers\zrange命令也会产生阻塞,使用hscan\sscan\zscan命令可以渐进式遍历其元素。
语法及功能:
hscan key cursor [MATCH pattern] [COUNT count]
sscan key cursor [MATCH pattern] [COUNT count]
zscan key cursor [MATCH pattern] [COUNT count]
cursor:是一个游标,第一次遍历从0开始,每次scan完都会返回当前游标的值,再次scan当前游标的值,直到返回的游标值为0(表示遍历结束)。
[MATCH pattern]:表示使用模式匹配,与keys的模式匹配一致。
[COUNT count]:表示每次要遍历的键个数,默认值是10。
例子:
hscan user:2 0 MATCH * COUNT 10
sscan set4 0 MATCH * COUNT 10
zscan zset4 0 MATCH * COUNT 10
2.7.3 数据库管理
数据库命令包括:dbsize、select、flushdb、flushall。
(1)键总数:dbsize
dbsize命令在计算键总数时不会遍历所有键,而是直接获取Redis内置的键总数变量,dbsize命令的时间复杂度是O(1)。
(2)切换数据库:
语法及功能:
select index
Redis一个实例支持16个数据库,编号为0到15,即index取值为0-15,默认使用0。
select 0 即选择0号数据库,依次类推,select 1 即选择1号数据库。不同数据库之间没有关系,即不同数据库内可以使用相同名称的键,同一数据库内键名不能重复。
分布式的Redis Cluster只能使用0号数据库。
例子:
dbsize --30
select 1
dbsize --0
注意:Redis支持16个数据库但仍是单线程,只能使用一个CPU,一个库遇到阻塞命令时其他库也会被阻塞,所以不建议使用多数据库功能,应用使用默认的0号数据库即可。
(3)清除数据库:
语法及功能:
flushdb:清除当前数据库。
flushall:清除所有数据库。
注意:清除数据库会将所有键值删除,谨防误操作,建议使用rename-command配置来规避该问题。如果数据库中键值较多时,flushdb/flushall命令会阻塞Redis。
例子:
select 1
set str1 "hello world"
dbsize
flushdb
dbsize
get str1
select 0
2.8 本章重点回顾