Redis五中数据类型的底层实现
String字符串
字符串是 Redis 中最基础的数据类型,可以存储整数、浮点数或字符串。
底层实现:int整数编码和SDS简单动态字符串
SDS(Simple Dynamic String简单动态字符串):
Redis 并未直接使用 C 语言的 char
数组来存储字符串,而是实现了一种名为 SDS(简单动态字符串) 的结构,它比 C 语言的字符串更高效和灵活。
核心特点:
- 动态扩展:避免频繁的内存分配和复制操作。
- 常数时间O(1)获取字符串长度(SDS 结构会存储字符串的长度)。
- 末尾有空字符
\0
,确保可以兼容 C 语言的字符串函数。
常用命令
#设置key-value类型的值
>SET name lin
OK
#根据key 获得对应的value
>GET name
"lin"
#判断某个 key是否存在
>EXISTS name
(integer)1
#返回key所储存的字符串值的长度
>STRLEN name
(integer)3
#删除某个 key对应的值
>DEL name
(integer)1
#批量设置key-value类型的值
>MSET key1 value1 key2 value2OK
#批量获取多个 key 对应的value
>MGET key1 key2
1)value1"
2) "value2"
#设置key-value类型的值
>SET number o
OK
#将key中储存的数字值增一
>INCR number
(integer)1
# 将key中存储的数字值加10
>INCRBY number 10
(integer)11
#将 key中储存的数字值减一
> DECR number
(integer)10
#将key中存储的数字值键10
>DECRBY number 10
(integer)0
#不存在就插入(not exists)//分布式锁
>SETNX key value
(integer)1
应用场景
- 常规计数。因为Redis处理命令是单线程,所以执行命令的过程是原子的。因此 String 数据类型适合计数场景,比如计算访问次数、点赞、转发、库存数量等等。
- 分布式锁。SET命令有个NX参数可以实现「key不存在才插入J,可以用它来实现分布式锁
- 共享Session信息。
Hash哈希
哈希类型是一个键值对的集合,可以类比为一个小型的字典或哈希表。
底层实现:Ziplist(压缩列表)和Hashtable(哈希表)
Ziplist(压缩列表):
如果哈希类型元素个数小于512个(默认值,可由hash-max-ziplist-entries配置),所有值小于64字节(默认值,可由hash-max-ziplist-value配置)的话,Redis 会使用压缩列表作为Hash类型的底层数据结构;
Hashtable(哈希表):
当哈希表中的键值对数量增多或键值对的长度较大,不满足上面条件时,Redis 会将底层实现切换为字典(hashtable)。Redis 的 hashtable 使用开放寻址法来解决哈希冲突,并通过渐进式 rehash 来应对扩展和收缩。
常用命令
#存储一个哈希表key的键值
HSET key field value
#获取哈希表key对应的field键值
HGET key field
#在一个哈希表key中存储多个键值对
HMSET key field value [field value. . .]
#批量获取哈希表key中多个field键值
HMGET key field [field ...]
#删除哈希表key中的field键值
HDEL key field [field ...]
#返回哈希表key中field的数量
HLEN key
#返回哈希表key中所有的键值
HGETALL key
#为哈希表key中field键的值加上增量n
HINCRBY key field n
应用场景
- 缓存对象(购物车等)
List列表
列表类型用于存储一系列的有序字符串,支持从头部和尾部操作(如插入、删除)等。
底层实现:Ziplist(压缩列表)、Linkedlist(双向链表)、Quicklist(快速列表)
Linkedlist(双向链表):
当列表中的元素较多或元素较长时,Redis 会使用双向链表来存储列表。双向链表可以有效地支持 O(1) 的头尾插入和删除操作,同时还支持正向和反向遍历
Quicklist(快速列表):
Redis 3.2 之后,引入了一种新的数据结构 Quicklist(只用它了),它结合了压缩列表和双向链表的优点。Quicklist 是由多个压缩列表组成的双向链表,既减少了内存开销,又保留了双向链表的灵活性。
常用命令
# 将一个或多个值value插入到key列表的表头(最左边),最后的值在最前面
LPUSH key value [value ...]
# 将一个或多个值value插入到key列表的表尾(最右边)
RPUSH key value [value ...]
#移除并返回key列表的头元素
LPOP key
#移除并返回key列表的尾元素
RPOP key
#返回列表key中指定区间内的元素,区间以偏移量start和stop指定,从o开始
LRANGE key start stop
# 从key列表表头弹出一个元素,没有就阻塞timeout秒,如果timeout=0则一直阻塞
BLPOP key [key ...] timeout
# 从key列表表尾弹出一个元素,没有就阻塞timeout秒,如果timeout=0则一直阻塞
BRPOP key[key ... ] timeout
应用场景
-
消息队列。列表用来存储多个有序的字符串,既然是有序的,那么就满足消息队列的特点。使用lpush+rpop或者rpush+lpop实现消息队列。除此之外,redis支持阻塞操作,在弹出元素的时候使用阻塞命令来实现阻塞队列。
-
栈。由于列表存储的是有序字符串,满足队列的特点,也就能满足栈先进后出的特点,使用lpush+lpop或者rpush+rpop实现栈。
-
文章列表。因为列表的元素不但是有序的,而且还支持按照索引范围获取元素。因此我们可以使用命令lrange key 0 9分页获取文章列表
Set集合
集合类型用于存储不重复的字符串元素,支持集合运算如交集、并集、差集等。
底层实现:Intset(整数集合)和Hashtable(哈希表)
Intset(整数集合):
当集合中的元素都是整数且数量较少时,Redis 会使用整数集合(intset)来存储数据。整数集合是一块连续的内存,存储了排序后的整数,并且不允许重复,适用于存储较少数量的整数集合。
Hashtable(哈希表):
当集合中的元素增多或包含非整数类型时,集合会使用哈希表存储。哈希表的键是集合中的元素,值为特殊的占位符,这样可以快速判断集合中是否包含某个元素。
常用命令
#往集合key中存入元素,元素存在则忽略,若key不存在则新建
SADD key member [member ...]
#从集合key中删除元素
SREM key member [member ...]
#获取集合key中所有元素
SMEMBERS key
#获取集合key中的元素个数
SCARD key
#判断member元素是否存在于集合key中
SISMEMBER key member
#从集合key中随机选出count个元素,元素不从key中删除
SRANDMEMBER key [count]
#从集合key中随机选出count个元素,元素从key中删除
SPOP key [ count]
#交集运算
SINTER key [key ...]
#将交集结果存入新集合destination中
SINTERSTORE destination key [key ...]
#并集运算
SUNION key [key ...]
#将并集结果存入新集合destination中
SUNIONSTORE destination key [key ...]
#差集运算
SDIFF key [key ...]
#将差集结果存入新集合destination中
SDIFFSTORE destination key [key ...]
应用场景
-
点赞。
Set类型可以保证一个用户只能点一个赞,这里举例子一个场景,key是文章id, value是用户id。 - 共同关注。
Set类型支持交集运算,所以可以用来计算共同关注的好友、公众号等。 - 抽奖活动
存储某活动中中奖的用户名,Set类型因为有去重功能,可以保证同一个用户不会中奖两次。
Zset有序集合
Zset类型(有序集合类型)相比于Set类型多了一个排序属性score(分值),对于有序集合ZSet 来说,每个存储元素相当于有两个值组成的,一个是有序集合的元素值,一个是排序值。
有序集合保留了集合不能有重复成员的特性(分值可以重复),但不同的是,有序集合中的元素可以排序。
底层实现:Ziplist(压缩列表)、Skiplist(跳表) + Hashtable(哈希表)
Skiplist(跳表) + Hashtable(哈希表):
当有序集合中的元素较多时,Redis 会使用跳表和哈希表的组合结构来实现有序集合:
- 跳表(skiplist):跳表是一种用于快速查找的有序数据结构,能够在 O(logN) 时间复杂度下查找、插入和删除数据。跳表是 Redis 有序集合中保证数据按分数排序的核心数据结构。
- 哈希表(hashtable):哈希表用来存储有序集合中的元素和值的映射关系,确保能够在 O(1) 时间内查找到元素的分数。
常用命令
#往有序集合key中加入带分值元素
ZADD key score member [[score member]...]
#往有序集合key中删除元素
ZREM key member[member. . .]
#返回有序集合key中元素member的分值
ZSCORE key member
#返回有序集合key中元素个数
ZCARD key
#为有序集合key中元素member的分值加上
incrementZINCRBY key increment member
#正序获取有序集合key从start下标到stop下标的元素
ZRANGE key start stop [WITHSCORES]
#倒序获取有序集合key从start下标到stop下标的元素
ZREVRANGE key start stop [WITHSCORES]
#返回有序集合中指定分数区间内的成员,分数由低到高排序。
ZRANGEBYSCORE key min max [WITHSCORES][LIMIT offset count]
#返回指定成员区间内的成员,按字典正序排列,分数必须相同。
ZRANGEBYLEX key min max [LIMIT offset count]
#返回指定成员区间内的成员,按字典倒序排列,分数必须相同
ZREVRANGEBYLEX key max min [LIMIT offset count]
应用场景
-
排行榜。有序集合比较典型的使用场景就是排行榜。例如学生成绩的排名榜、游戏积分排行榜、视频播放排名、电商系统中商品的销量排名等。
- 电话、姓名排序。使用有序集合的ZRANGEBYLEX或ZREVRANGEBYLEX可以帮助我们实现电话号码或姓名的排序,我们以ZRANGEBYLEX(返回指定成员区间内的成员,按key正序排列,分数必须相同)为例。
注意:不要在分数不一致的SortSet集合中去使用ZRANGEBYLEX和ZREVRANGEBYLEX指令,因为获取的结果会不准确。