Redis五种基本数据类型
五种基本数据类型
主要包括常见的
5种
数据类型,分别是:
String、List、Set、Zset、Hash
。
String字符串 | 可以是字符串、整数或浮点数 | 对整个字符串或字符串的一部分进行操作;对整数或浮点数进行自增或自减操作; |
List列表 | 一个链表,链表上的每个节点都包含一个字符串 | 对链表的两端进行push和pop操作,读取单个或多个元素;根据值查找或删除元素; |
Set集合 | 包含字符串的无序集合 | 字符串的集合,包含基础的方法有看是否存在添加、获取、删除;还包含计算交集、并集、差集等 |
Hash散列 | 包含键值对的无序散列表 | 包含方法有添加、获取、删除单个元素 |
Zset有序集合 | 和散列一样,用于存储键值对 | 字符串成员与浮点数分数之间的有序映射;元素的排列顺序由分数的大小决定;包含方法有添加、获取、删除单个元素以及根据分值范围或成员来获取元素 |
String字符串
简介
String是redis最基本的类型,一个key对应一个value。
- String类型是二进制安全的,意思是redis的string可以包含任何数据,比如jpg图片或者序列化的对象。
- String类型是redis最基本的数据类型,一个redis中字符串value最多可以是512M
命令
基本操作
set (key) (value)
:设置键值对setnx (key) (value)
:防止覆盖,设置键值对,如果key不存在就设置,返回1如果key已经存在就不设置,返回0get(key)
:获取key对应的valuegetset (key) (value)
append (key) (value)
:向指定的key的value后追加字符串del (key)
strlen (key)
:获取key对应值的字符串长度
数字value的加减
incr (key)
decr (key)
:value - 1incrby (key) (number)
:value + numberdecrby (key) (number)
:value - number
获取或者设置指定范围内的值
getrange (key) (begin) (end)
setrange (key) (begin) (xxxx)
设置键值过期时间
setex (key) (seconds) expire
:设置键过期时间ttl (key)
同时设置或获取多个key-value
met (key1) (value1) (key2) (value2)
:用于同时设置一个或多个 key-value 对mget (key1) (key2)
msetnx(key1) (value1) (key2) (value2)
:当所有 key 都成功设置,返回 1 。 如果有一个key设置失败,所有的key设置都会失败,返回 0 。
使用场景
实战场景
- 缓存: 经典使用场景,把常用信息,字符串,图片或者视频等信息放到redis中,redis作为缓存层,mysql做持久化层,降低mysql的读写压力
- 计数器:redis是单线程模型,一个命令执行完才会执行下一个,同时数据可以一步落地到其他的数据源。
- session:常见方案spring session + redis实现session共享,
编码与底层结构
字符串是Redis最基本的数据类型,不仅所有key都是字符串类型,其它几种数据类型构成的元素也是字符串。注意字符串的长度不能超过512M。
编码
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-hYsLFl0o-1651558697727)(Redis.assets/image-20220106194438014.png)]
字符串对象的编码可以是int
,raw
或者embstr
。
int 编码
:保存的是可以用 long 类型表示的整数值embstr 编码
:保存长度小于44字节的短字符串(redis3.2版本之前是39字节,之后是44字节)
- 对其进行修改后变成raw编码,无论是否达到44字节
raw 编码
:保存长度大于44字节的长字符串(redis3.2版本之前是39字节,之后是44字节)
内存布局
字符串对象支持三种编码方式: RAW, INT, EMBSTR, 三种方式的内存布局分别如下:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-SBpOnVpM-1651558697728)(https://pdai-1257820000.cos.ap-beijing.myqcloud.com/pdai.tech/public/_images/db/redis/db-redis-ds-x-21.png)]
🔊 raw 和 embstr 的区别
其实 embstr 编码是专门用来保存短字符串的一种优化编码,
embstr与raw都使用redisObject和sds保存数据,
- embstr的使用只分配一次内存空间(因此redisObject和sds是连续的)
- raw需要分配两次内存空间(分别为redisObject和sds分配空间)
只分配一次内存空间的好处是,创建时少分配一次空间,删除时少释放一次空间,以及对象的所有数据连在一起,寻找方便。
而embstr的坏处也很明显,如果字符串的长度增加需要重新分配内存时,整个,
💬 编码的转换
- 当 int 编码保存的值不再是整数,或大小超过了long的范围时,自动转化为raw。
- 对于 embstr 编码,由于 Redis 没有对其编写任何的修改程序(),在对embstr对象进行修改时,都会先转化为raw再进行修改,因此==,只要是修改embstr对象,修改后的对象一定是raw的,无论是否达到了44个字节。==
List列表
简介
Redis中的List其实就是双端链表
- 使用List结构,我们可以轻松地实现最新消息排队功能(比如新浪微博的TimeLine)。List的另一个应用就是消息队列,可以利用List的 PUSH 操作,将任务存放在List中,然后工作线程再用 POP 操作将任务取出进行执行。
- Redis还提供了操作List中某一段的api,你可以直接查询,删 除List中某一段的元素。
- 使用列表的技巧lpush+lpop=Stack(栈)lpush+rpop=Queue(队列)lpush+ltrim=Capped Collection(有限集合)lpush+brpop=Message Queue(消息队列)
命令使用
RPUSH | 将给定值推入到列表右端 | RPUSH key value |
LPUSH | 将给定值推入到列表左端 | LPUSH key value |
RPOP | 从列表的右端弹出一个值,并返回被弹出的值 | RPOP key |
LPOP | 从列表的左端弹出一个值,并返回被弹出的值 | LPOP key |
LRANGE | 获取列表在给定范围上的所有值 | LRANGE key 0 -1 |
LINDEX | 通过索引获取列表中的元素。你也可以使用负数下标,以 -1 表示列表的最后一个元素, -2 表示列表的倒数第二个元素,以此类推。 | LINEX key index |
命令测试
# =================================================== # Lpush:将一个或多个值插入到列表头部。(左) # rpush:将一个或多个值插入到列表尾部。(右) # lrange:返回列表中指定区间内的元素,区间以偏移量 START 和 END 指定。 # 其中 0 表示列表的第一个元素, 1 表示列表的第二个元素,以此类推。 # 你也可以使用负数下标,以 -1 表示列表的最后一个元素, -2 表示列表的倒数第二个元素,以此 类推。 # =================================================== 127.0.0.1:6379> LPUSH list "one" (integer) 1 127.0.0.1:6379> LPUSH list "two" (integer) 2 127.0.0.1:6379> RPUSH list "right" (integer) 3 127.0.0.1:6379> Lrange list 0 -1 1) "two" 2) "one" 3) "right" 127.0.0.1:6379> Lrange list 0 1 1) "two" 2) "one" # =================================================== # lpop 命令用于移除并返回列表的第一个元素。当列表 key 不存在时,返回 nil 。 # rpop 移除列表的最后一个元素,返回值为移除的元素。 # =================================================== 127.0.0.1:6379> Lpop list "two" 127.0.0.1:6379> Rpop list "right" 127.0.0.1:6379> Lrange list 0 -1 1) "one" # =================================================== # Lindex,按照索引下标获得元素(-1代表最后一个,0代表是第一个) # =================================================== 127.0.0.1:6379> Lindex list 1 (nil) 127.0.0.1:6379> Lindex list 0 "one" 127.0.0.1:6379> Lindex list -1 "one" # =================================================== # llen 用于返回列表的长度。 # =================================================== 127.0.0.1:6379> flushdb OK 127.0.0.1:6379> Lpush list "one" (integer) 1 127.0.0.1:6379> Lpush list "two" (integer) 2 127.0.0.1:6379> Lpush list "three" (integer) 3 127.0.0.1:6379> Llen list # 返回列表的长度 (integer) 3 # =================================================== # lrem key 根据参数 COUNT 的值,移除列表中与参数 VALUE 相等的元素。 # =================================================== 127.0.0.1:6379> lrem list 1 "two" (integer) 1 127.0.0.1:6379> Lrange list 0 -1 1) "three" 2) "one" # =================================================== # Ltrim key 对一个列表进行修剪(trim),就是说,让列表只保留指定区间内的元素,不在指定区 间之内的元素都将被删除。 # =================================================== 127.0.0.1:6379> RPUSH mylist "hello" (integer) 1 127.0.0.1:6379> RPUSH mylist "hello" (integer) 2 127.0.0.1:6379> RPUSH mylist "hello2" (integer) 3 127.0.0.1:6379> RPUSH mylist "hello3" (integer) 4 127.0.0.1:6379> ltrim mylist 1 2 OK 127.0.0.1:6379> lrange mylist 0 -1 1) "hello" 2) "hello2" # =================================================== # rpoplpush 移除列表的最后一个元素,并将该元素添加到另一个列表并返回。 # =================================================== 127.0.0.1:6379> rpush mylist "hello" (integer) 1 127.0.0.1:6379> rpush mylist "foo" (integer) 2 127.0.0.1:6379> rpush mylist "bar" (integer) 3 127.0.0.1:6379> rpoplpush mylist myotherlist "bar" 127.0.0.1:6379> lrange mylist 0 -1 1) "hello" 2) "foo" 127.0.0.1:6379> lrange myotherlist 0 -1 1) "bar" # =================================================== # lset key index value 将列表 key 下标为 index 的元素的值设置为 value 。 # =================================================== 127.0.0.1:6379> exists list # 对空列表(key 不存在)进行 LSET (integer) 0 127.0.0.1:6379> lset list 0 item # 报错 (error) ERR no such key 127.0.0.1:6379> lpush list "value1" # 对非空列表进行 LSET (integer) 1 127.0.0.1:6379> lrange list 0 0 1) "value1" 127.0.0.1:6379> lset list 0 "new" # 更新值 OK 127.0.0.1:6379> lrange list 0 0 1) "new" 127.0.0.1:6379> lset list 1 "new" # index 超出范围报错 (error) ERR index out of range # =================================================== # linsert key before/after pivot value 用于在列表的元素前或者后插入元素。 # 将值 value 插入到列表 key 当中,位于值 pivot 之前或之后。 # =================================================== redis> RPUSH mylist "Hello" (integer) 1 redis> RPUSH mylist "World" (integer) 2 redis> LINSERT mylist BEFORE "World" "There" (integer) 3 redis> LRANGE mylist 0 -1 1) "Hello" 2) "There" 3) "World" 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126
使用场景
- 微博TimeLine: 有人发布微博,用lpush加入时间轴,展示新的列表信息。
- 消息队列
编码与底层结构
list 列表,它是简单的字符串列表,按照插入顺序排序,你可以添加一个元素到列表的头部(左边)或者尾部(右边),它的底层实际上是个链表结构。
编码
列表对象的编码是quicklist
。 (之前版本中有linked和ziplist这两种编码)
内存布局
列表对象的内存布局如下图所示:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-oLjbplpy-1651558697729)(https://pdai-1257820000.cos.ap-beijing.myqcloud.com/pdai.tech/public/_images/db/redis/db-redis-ds-x-22.png)]
Set集合
简介
Redis 的 Set 是 String 类型的无序集合。
- 集合成员是****
- Redis 中集合是通过哈希表实现的,所以添加,删除,查找的复杂度都是 O(1)
使用场景
- 标签(tag),给用户添加标签,或者用户给消息添加标签,这样有同一标签或者类似标签的可以给推荐关注的事或者关注的人。
- 点赞,或点踩,收藏等,可以放到set中实现
命令使用
SADD | 向集合添加一个或多个成员 | SADD key value |
SCARD | 获取集合的成员数 | SCARD key |
SMEMBER | 返回集合中的所有成员 | SMEMBER key member |
SISMEMBER | 判断 member 元素是否是集合 key 的成员 | SISMEMBER key member |
测试命令
# =================================================== # sadd 将一个或多个成员元素加入到集合中,不能重复 # smembers 返回集合中的所有的成员。 # sismember 命令判断成员元素是否是集合的成员。 # =================================================== 127.0.0.1:6379> sadd myset "hello" (integer) 1 127.0.0.1:6379> sadd myset "kuangshen" (integer) 1 127.0.0.1:6379> sadd myset "kuangshen" (integer) 0 127.0.0.1:6379> SMEMBERS myset 1) "kuangshen" 2) "hello" 127.0.0.1:6379> SISMEMBER myset "hello" (integer) 1 127.0.0.1:6379> SISMEMBER myset "world" (integer) 0 # =================================================== # scard,获取集合里面的元素个数 # =================================================== 127.0.0.1:6379> scard myset (integer) 2 # =================================================== # srem key value 用于移除集合中的一个或多个成员元素 # =================================================== 127.0.0.1:6379> srem myset "kuangshen" (integer) 1 127.0.0.1:6379> SMEMBERS myset 1) "hello" # =================================================== # srandmember key 命令用于返回集合中的一个随机元素。 # =================================================== 127.0.0.1:6379> SMEMBERS myset 1) "kuangshen" 2) "world" 3) "hello" 127.0.0.1:6379> SRANDMEMBER myset "hello" 127.0.0.1:6379> SRANDMEMBER myset 2 1) "world" 2) "kuangshen" 127.0.0.1:6379> SRANDMEMBER myset 2 1) "kuangshen" 2) "hello" # =================================================== # spop key 用于移除集合中的指定 key 的一个或多个随机元素 # =================================================== 127.0.0.1:6379> SMEMBERS myset 1) "kuangshen" 2) "world" 3) "hello" 127.0.0.1:6379> spop myset "world" 127.0.0.1:6379> spop myset "kuangshen" 127.0.0.1:6379> spop myset "hello" # =================================================== # smove SOURCE DESTINATION MEMBER # 将指定成员 member 元素从 source 集合移动到 destination 集合。 # =================================================== 127.0.0.1:6379> sadd myset "hello" (integer) 1 127.0.0.1:6379> sadd myset "world" (integer) 1 127.0.0.1:6379> sadd myset "kuangshen" (integer) 1 127.0.0.1:6379> sadd myset2 "set2" (integer) 1 127.0.0.1:6379> smove myset myset2 "kuangshen" (integer) 1 127.0.0.1:6379> SMEMBERS myset 1) "world" 2) "hello" 127.0.0.1:6379> SMEMBERS myset2 1) "kuangshen" 2) "set2" # =================================================== - 数字集合类 - 差集: sdiff - 交集: sinter - 并集: sunion # =================================================== 127.0.0.1:6379> sadd key1 "a" (integer) 1 127.0.0.1:6379> sadd key1 "b" (integer) 1 127.0.0.1:6379> sadd key1 "c" (integer) 1 127.0.0.1:6379> sadd key2 "c" (integer) 1 127.0.0.1:6379> sadd key2 "d" (integer) 1 127.0.0.1:6379> sadd key2 "e" (integer) 1 127.0.0.1:6379> SDIFF key1 key2 # 差集 1) "a" 2) "b" 127.0.0.1:6379> SINTER key1 key2 # 交集 1) "c" 127.0.0.1:6379> SUNION key1 key2 # 并集 1) "a" 2) "b" 3) "c" 4) "e" 5) "d" 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107
编码与底层结构
集合对象 set 是 string 类型(整数也会转换成string类型进行存储)的无序集合。
⚡️ 注意集合和列表的区别:
- 集合中的元素是无序的,因此不能通过索引来操作元素;
- 集合中的元素不能重复
编码
集合对象的编码可以是 intset 或者 hashtable;
底层结构
对应的底层实现分别是intset和dict
- 显然当使用intset作为底层实现的数据结构时, 集合中存储的只能是数值数据, 且必须是整数;
- 当使用dict作为集合对象的底层实现时, 是将数据全部存储于dict的键中, 值字段闲置不用.
集合对象的内存布局如下图所示:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-n5Pm9bPW-1651558697730)(https://pdai-1257820000.cos.ap-beijing.myqcloud.com/pdai.tech/public/_images/db/redis/db-redis-ds-x-24.png)]
- 举例说明
SADD numbers 1 3 5 1
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-fXkGnKx0-1651558697731)(https://pdai-1257820000.cos.ap-beijing.myqcloud.com/pdai.tech/public/_images/db/redis/db-redis-x-object-11.png)]
SADD Dfruits "apple" "banana" "cherry" 1
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-S9YFxiAr-1651558697733)(https://pdai-1257820000.cos.ap-beijing.myqcloud.com/pdai.tech/public/_images/db/redis/db-redis-x-object-12.png)]
编码转换
当集合同时满足以下两个条件时,使用 intset
编码,否则使用 hashtable
编码
1、集合对象中所有元素都是整数
2、集合对象所有元素数量不超过512(可以通过配置文件的 set-max-intset-entries
进行配置)
Hash散列
简介
Redis hash 是一个 string 类型的 field(字段) 和 value(属性) 的映射表,hash 特别适合用于存储对象。
一个hash可以存多个key-value,类似一个对象的多个字段和属性
使用场景:
- 缓存: 能直观,相比string更节省空间,的维护缓存信息,如用户信息,视频信息等。
命令使用
HSET | 添加键值对 | HSET hash-key sub-key1 value1 |
HGET | 获取指定散列键的值 | HGET hash-key key1 |
HGETALL | 获取散列中包含的所有键值对 | HGETALL hash-key |
HDEL | 如果给定键存在于散列中,那么就移除这个键 | HDEL hash-key sub-key1 |
测试命令
# =================================================== # hset、hget 命令用于为哈希表中的字段赋值 。 # hmset、hmget 同时将多个field-value对设置到哈希表中。会覆盖哈希表中已存在的字段。 # hgetall 用于返回哈希表中,所有的字段和值。 # hdel 用于删除哈希表 key 中的一个或多个指定字段 # =================================================== 127.0.0.1:6379> hset myhash field1 "huangkaiyu" (integer) 1 127.0.0.1:6379> hget myhash field1 "huangkaiyu" 127.0.0.1:6379> HMSET myhash field1 "Hello" field2 "World" OK 127.0.0.1:6379> HGET myhash field1 "Hello" 127.0.0.1:6379> HGET myhash field2 "World" 127.0.0.1:6379> hgetall myhash 1) "field1" 2) "Hello" 3) "field2" 4) "World" 127.0.0.1:6379> HDEL myhash field1 (integer) 1 127.0.0.1:6379> hgetall myhash 1) "field2" 2) "World" # =================================================== # hlen 获取哈希表中字段的数量。 # =================================================== 127.0.0.1:6379> hlen myhash (integer) 1 127.0.0.1:6379> HMSET myhash field1 "Hello" field2 "World" OK 127.0.0.1:6379> hlen myhash (integer) 2 # =================================================== # hexists 查看哈希表的指定字段是否存在。 # =================================================== 127.0.0.1:6379> hexists myhash field1 (integer) 1 127.0.0.1:6379> hexists myhash field3 (integer) 0 # =================================================== # hkeys 获取哈希表中的所有域(field)。 # hvals 返回哈希表所有域(field)的值。 # =================================================== 127.0.0.1:6379> HKEYS myhash 1) "field2" 2) "field1" 127.0.0.1:6379> HVALS myhash 1) "World" 2) "Hello" # =================================================== # hincrby 为哈希表中的字段值加上指定增量值。 # =================================================== 127.0.0.1:6379> hset myhash field 5 (integer) 1 127.0.0.1:6379> HINCRBY myhash field 1 (integer) 6 127.0.0.1:6379> HINCRBY myhash field -1 (integer) 5 127.0.0.1:6379> HINCRBY myhash field -10 (integer) -5 # =================================================== # hsetnx 为哈希表中不存在的的字段赋值 。 # =================================================== 127.0.0.1:6379> HSETNX myhash field1 "hello" (integer) 1 # 设置成功,返回 1 。 127.0.0.1:6379> HSETNX myhash field1 "world" (integer) 0 # 如果给定字段已经存在,返回 0 。 127.0.0.1:6379> HGET myhash field1 "hello" 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172
编码与底层结构
哈希对象的键是一个字符串类型,值是一个键值对集合。
编码
哈希对象的编码可以是 ziplist
或者 hashtable
;
底层结构
对应的底层实现有两种, 一种是ziplist
, 一种是dict
。
内存布局
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-rFg6ht57-1651558697735)(https://pdai-1257820000.cos.ap-beijing.myqcloud.com/pdai.tech/public/_images/db/redis/db-redis-ds-x-23.png)]
需要注意的是: 当采用HT编码, 即使用dict作为哈希对象的底层数据结构时, 键与值均是以sds的形式存储的.
举例说明
1️⃣
当使用ziplist,也就是压缩列表作为底层实现时,新增的键值对是保存到压缩列表的表尾。
比如执行以下命令:
hset profile name "Tom" hset profile age 25 hset profile career "Programmer" 123
:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-PV2CRj70-1651558697736)(https://pdai-1257820000.cos.ap-beijing.myqcloud.com/pdai.tech/public/_images/db/redis/db-redis-x-object-9.png)]
⚡️ 在前面介绍压缩列表时,我们介绍过压缩列表是Redis为了节省内存而开发的,是由一系列特殊编码的连续内存块组成的顺序型数据结构,相对于字典数据结构,压缩列表用于元素个数少、元素长度小的场景。其优势在于集中存储,节省空间。
2️⃣
当使用 hashtable 编码时,底层使用字典数据结构,哈希对象中的每个键值对都使用一个字典键值对。上面命令存储如下:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-D4NxMo38-1651558697737)(https://pdai-1257820000.cos.ap-beijing.myqcloud.com/pdai.tech/public/_images/db/redis/db-redis-x-object-10.png)]
编码转换
和上面列表对象使用 ziplist 编码一样,当同时满足下面两个条件时,使用ziplist(压缩列表)编码,否则使用hashtable 编码
1、列表保存元素个数小于512个(配置文件中的 set-max-intset-entries
修改)
2、每个元素长度小于64字节
Zset有序集合
简介
Redis 有序集合和集合一样基本一致
- ****使得集合中的元素能够按score进行有序排列。
特点:
- 有序集合的成员是唯一的,但分数(score)却可以重复。集合是通过哈希表实现的,所以添加,删除,查找的复杂度都是 O(1)。
使用场景:
- 排行榜:有序集合经典使用场景。例如小说视频等网站需要对用户上传的小说视频做排行榜,榜单可以按照用户关注数,更新时间,字数等打分,做排行。
- 成绩排行:比如一个存储全班同学成绩的sorted set,其集合value可以是同学的学号,而score就可以是其考试得分, 形成了按成绩排序。
- 权重分配:可以用sorted set来做带权重的队列,比如普通消息的score为1,重要消息的score为2,然后工作线程可以选择按score的倒序来获取工作任务。让重要的任务优先执行。
测试用例
# =================================================== # zadd 将一个或多个成员元素及其分数值加入到有序集当中。 # zrange 返回有序集中,指定区间内的成员 # =================================================== 127.0.0.1:6379> zadd myset 1 "one" (integer) 1 127.0.0.1:6379> zadd myset 2 "two" 3 "three" (integer) 2 127.0.0.1:6379> ZRANGE myset 0 -1 1) "one" 2) "two" 3) "three" # =================================================== # zrangebyscore 返回有序集合中指定分数区间的成员列表。有序集成员按分数值递增(从小到大) 次序排列。 # =================================================== 127.0.0.1:6379> zadd salary 2500 xiaoming (integer) 1 127.0.0.1:6379> zadd salary 5000 xiaohong (integer) 1 127.0.0.1:6379> zadd salary 500 kuangshen (integer) 1 # Inf无穷大量+∞,同样地,-∞可以表示为-Inf。 127.0.0.1:6379> ZRANGEBYSCORE salary -inf +inf # 显示整个有序集 1) "kuangshen" 2) "xiaoming" 3) "xiaohong" 127.0.0.1:6379> ZRANGEBYSCORE salary -inf +inf withscores # 递增排列 1) "kuangshen" 2) "500" 3) "xiaoming" 4) "2500" 5) "xiaohong" 6) "5000" 127.0.0.1:6379> ZREVRANGE salary 0 -1 WITHSCORES # 递减排列 1) "xiaohong" 2) "5000" 3) "xiaoming" 4) "2500" 5) "kuangshen" 6) "500" 127.0.0.1:6379> ZRANGEBYSCORE salary -inf 2500 WITHSCORES # 显示工资 <=2500 的所有成员 1) "kuangshen" 2) "500" 3) "xiaoming" 4) "2500" # =================================================== # zrem 移除有序集中的一个或多个成员 # =================================================== 127.0.0.1:6379> ZRANGE salary 0 -1 1) "kuangshen" 2) "xiaoming" 3) "xiaohong" 127.0.0.1:6379> zrem salary kuangshen (integer) 1 127.0.0.1:6379> ZRANGE salary 0 -1 1) "xiaoming" 2) "xiaohong" # =================================================== # zcard 命令用于计算集合中元素的数量。 # =================================================== 127.0.0.1:6379> zcard salary (integer) 2 OK # =================================================== # zcount 计算有序集合中指定分数区间的成员数量。 # =================================================== 127.0.0.1:6379> zadd myset 1 "hello" (integer) 1 127.0.0.1:6379> zadd myset 2 "world" 3 "kuangshen" (integer) 2 127.0.0.1:6379> ZCOUNT myset 1 3 (integer) 3 127.0.0.1:6379> ZCOUNT myset 1 2 (integer) 2 # =================================================== # zrank 返回有序集中指定成员的排名。其中有序集成员按分数值递增(从小到大)顺序排列。 # =================================================== 127.0.0.1:6379> zadd salary 2500 xiaoming (integer) 1 127.0.0.1:6379> zadd salary 5000 xiaohong (integer) 1 127.0.0.1:6379> zadd salary 500 kuangshen (integer) 1 127.0.0.1:6379> ZRANGE salary 0 -1 WITHSCORES # 显示所有成员及其 score 值 1) "kuangshen" 2) "500" 3) "xiaoming" 4) "2500" 5) "xiaohong" 6) "5000" 127.0.0.1:6379> zrank salary kuangshen # 显示 kuangshen 的薪水排名,最少 (integer) 0 127.0.0.1:6379> zrank salary xiaohong # 显示 xiaohong 的薪水排名,第三 (integer) 2 # =================================================== # zrevrank 返回有序集中成员的排名。其中有序集成员按分数值递减(从大到小)排序。 # =================================================== 127.0.0.1:6379> ZREVRANK salary kuangshen # 狂神第三 (integer) 2 127.0.0.1:6379> ZREVRANK salary xiaohong # 小红第一 (integer) 0 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103
编码与底层结构
有序集合对象是有序的。与列表使用索引下标作为排序依据不同,有序集合为每个元素设置一个分数(score)作为排序依据。
编码
1️⃣ 一种的编码值宏为ZIPLIST
2️⃣对应的编码值宏为SKIPLIST
底层结构
1️⃣ 一种是使用ziplist
作为底层实现,
- 使用ziplist来实现在序集合很容易理解, 只需要在ziplist这个数据结构的基础上做好排序与去重就可以了.
2️⃣ 另外一种比较特殊, 底层使用了两种数据结构: dict与skiplist.
- 单独使用 哈希表,虽然能以 O(1) 的时间复杂度查找成员,但是字典是以无序的方式来保存集合元素,所以进行范围操作的时候都要进行排序;
- 单独使用 跳表,虽然能执行范围操作,但是查找操作为O(logN)。因此Redis使用了两种数据结构来共同实现有序集合。
首先是编码为ZIPLIST时, 有序集合的内存布局如下:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-vMF5EfIG-1651558697738)(https://pdai-1257820000.cos.ap-beijing.myqcloud.com/pdai.tech/public/_images/db/redis/db-redis-ds-x-25.png)]
然后是编码为SKIPLIST时, 有序集合的内存布局如下:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-6Tkzv0bE-1651558697738)(https://pdai-1257820000.cos.ap-beijing.myqcloud.com/pdai.tech/public/_images/db/redis/db-redis-ds-x-26.png)]
- 举例说明
ZADD price 8.5 apple 5.0 banana 6.0 cherry 1
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-CBsOkDE9-1651558697739)(https://pdai-1257820000.cos.ap-beijing.myqcloud.com/pdai.tech/public/_images/db/redis/db-redis-x-object-13.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-LwEX1Kpj-1651558697739)(https://pdai-1257820000.cos.ap-beijing.myqcloud.com/pdai.tech/public/_images/db/redis/db-redis-x-object-14.png)]
编码转换
当有序集合对象同时满足以下两个条件时,对象使用 ziplist
编码,否者使用 skiplist
编码
1、保存的元素数量小于128;(配置文件zset-max-ziplist-entries
修改)
2、保存的所有元素长度都小于64字节。(zset-max-ziplist-value
进行修改)