
常见的有五种:String(字符串),Hash(哈希),List(列表),Set(集合)、Zset(有序集合)。
支持了四种数据类型: BitMap(2.2 版新增)、HyperLogLog(2.8 版新增)、GEO(3.2 版新增)、Stream(5.0 版新增)。
Redis是key-value存储,key和value在Redis中都被抽象为对象,key只能是String对象,而Value支持丰富的对象种类。
Object在内存当中包括:
String 是最基本的 key-value 结构,key 是唯一标识,value 是具体的值,value其实不仅是字符串, 也可以是数字(整数或浮点数),value 最多可以容纳的数据长度是 512MB
。
适用场景:一般用来存字节数据、文本数据、序列化后的对象数据等。
SET name lin
,MSET key1 value1 key2 value2
,SETNX key value
GET name
,MGET key1 key2
,STRELEN name
,TTL name
INCR number
,EXPIRE name 60
DEL name
采用不同编码方式来应对不同的场景,以达到高性能。
包含了三种编码方式:
其中EMBSTR和RAW由redisObject和**SDS(Simple Dynamic String)**构成,EMBSTR下的redisObject和SDS是连续的内存,而RAW是离散的。
EMBSTR可以一次性分配空间,连续分布的。
embstr
编码的字符串对象同样只需要调用一次内存释放函数,连续分布的因此EMBSTR在发生写操作后,会变成RAW,默认当作发生过修改的字符串通常是易变的。
编码可能会发生转换
SDS 不仅可以保存文本数据,还可以保存二进制数据。
len
属性的值而不是空字符来判断字符串是否结束buf[]
数组里的数据SDS 获取字符串长度的时间复杂度是 O(1):增加了len长度字段,原生C是O(n)
Redis 的 SDS API 是安全的,拼接字符串不会造成缓冲区溢出:增加了空余空间(alloc - len),有了预留空间,节约性能。SDS 在拼接字符串之前会检查 SDS 空间是否满足要求,如果空间不够会自动扩容,所以不会导致缓冲区溢出的问题。
缓存对象
常规计数
分布式锁
# 将一个或多个值value插入到key列表的表头(最左边),最后的值在最前面
LPUSH key value [value ...]
# 将一个或多个值value插入到key列表的表尾(最右边)
RPUSH key value [value ...]
# 移除并返回key列表的头元素
LPOP key
# 移除并返回key列表的尾元素
RPOP key
# 返回列表key中指定区间内的元素,区间以偏移量start和stop指定,从0开始
LRANGE key start stop
# 从key列表表头弹出一个元素,没有就阻塞timeout秒,如果timeout=0则一直阻塞
BLPOP key [key ...] timeout
# 从key列表表尾弹出一个元素,没有就阻塞timeout秒,如果timeout=0则一直阻塞
BRPOP key [key ...] timeout
主要包括两种编码方式,ZIPLIST和LINKEDLIST。
当满足如下条件时,用ZIPLIST(压缩列表)编码
list-max-ziplist-value
配置)ZIPLIST底层用压缩列表实现,相邻元素紧凑压缩在一起,可以有效节约内存空间。
当不满足ZIPLIST编码条件,则使用LINKEDLIST(双向链表)编码,是几个STRING对象的链接结构。
ZIPLIST是为了在数据较少情况时节约内存,LINKEDLIST是为了数据多时提高更新效率。
为了优化解决上述问题,引入了QUICKLIST,二者的结合体,在 Redis 3.2 版本之后,List 数据类型底层数据结构就只由 quicklist 实现了,替代了双向链表和压缩列表。
LINKEDLIST原来是单个节点,只能存一个数据,现在单个节点存的是一个ZIPLIST,存放了多个数据。
当数据较少,QUICKLIST节点只有一个时,此时相当于就是一个ZIPLIST。
ZIPLIST优化:
Redis7.0使用LISTPACK(紧凑列表)的编码模式取代了ZIPLIST,本质上都是一种压缩列表。同时在LINKEDLIST中也用LISTPACK替代了ZIPLIST。
LISTPACK是为了解决ZIPLIST连锁更新问题
- ZIPLIST连锁更新是什么,如何造成的
- 当前一个节点大小超过254,会导致后面一个节点中prev值变化膨胀,接连后续变化,发生连锁更新
- LISTPACK是如何解决的
- 引入一个不记录prevlen的变量,element-tot-len存储整个节点除它自身以外的长度,从这个节点的末尾再遍历element-tot-len长度,就能找到这个节点的开始位置。
消息队列在存储消息时,必须要满足三个需求,分别是消息保序、处理重复的消息和保证消息可靠性。
消息保序:使用 LPUSH + RPOP
阻塞读取:使用 BRPOP
重复消息处理
消息的可靠性
List作为消息队列有什么缺陷
Hash 是一个键值对(key - value)集合,其中 value 的形式如: value=[{field1,value1},...{fieldN,valueN}]
。Hash 特别适合用于存储对象。
# 存储一个哈希表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
Hash 类型的底层数据结构是由压缩列表或哈希表实现的:
512
个(默认值,可由 hash-max-ziplist-entries
配置),所有值小于 64
字节(默认值,可由 hash-max-ziplist-value
配置)的话,Redis 会使用压缩列表作为 Hash 类型的底层数据结构;在 Redis 7.0 中,压缩列表数据结构已经废弃了,交由 listpack 数据结构来实现了。
Redis的Set是一个不重复、无序的字符串集合。
一个集合最多可以存储 2^32-1
个元素。
适用于无序集合场景,比如某个用户关注了哪些公众号,这些信息可以放进一个集合当中。Set也提供了查交集、并集的功能,可以很方便地实现共同关注的能力。
# 往集合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]
如果集群元素都是整数,且元素数量不超过512个,就可以用INTSET编码(整数集合),因为INTSET编码比较紧凑,内存占用少,但是查询时使用二分查找(有序集合)。
Set是无序的,IntSet编码是有序的。
如果不满足INTSET的条件,就需要用HASHTABLE(字典),能O(1)时间就能找到一个元素是否存在。
数据指针部分,用的字典存储,能够O(1)查询,适合快速定位的场景。
Set是有序的吗
Set的底层实现是整数集合或字典,前者是有序的,后者是无序的。整体来看,建议不依赖SET的顺序。
Redis Hash是一个field、value都为string的hash表,存储在Redis的内存当中。
适用于O(1)时间字典查找某个field对应数据的场景,比如任务信息的配置,就可以将任务类型设置为filed,任务配置参数为value。
如果满足以下条件使用ZIPLIST(压缩列表):
否则使用HASHTABLE。
ZSet就是有序、不可重复集合,也叫Sorted Set,是一组按关联积分有序的字符串集合。
这里的分数是个抽象概念,任何指标都可以抽象为分数,以满足不同场景。积分相同的情况下,按字典序排序。
# 往有序集合key中加入带分值元素
ZADD key score member [[score member]...]
# 往有序集合key中删除元素
ZREM key member [member...]
# 返回有序集合key中元素member的分值
ZSCORE key member
# 返回有序集合key中元素个数
ZCARD key
# 为有序集合key中元素member的分值加上increment
ZINCRBY 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]
底层编码包括两种,ZIPLIST和SKIPLIST(跳表)+ HT。
同样在数据量比较小的时候,使用ZIPLIST
两个条件任何一条不满足,编码机构就用SKIPLIST + HT。
SKIPLIST是一种可以快速查找的多级链表结构,可以当作是一个具有高级索引的链表
Bitmap,即位图,是一串连续的二进制数组(0和1),可以通过偏移量(offset)定位元素。BitMap通过最小的单位bit来进行0|1
的设置,表示某个元素的值或者状态,时间复杂度为O(1)。
# 设置值,其中value只能是 0 和 1
SETBIT key offset value
# 获取值
GETBIT key offset
# 获取指定范围内值为 1 的个数
# start 和 end 以字节为单位
BITCOUNT key start end
Bitmap 本身是用 String 类型作为底层数据结构实现的一种统计二值状态的数据类型。
String 类型是会保存为二进制的字节数组,所以,Redis 就把字节数组的每个 bit 位利用起来,用来表示一个元素的二值状态,你可以把 Bitmap 看作是一个 bit 数组。
Redis 2.8.9 版本新增的数据类型。
一种用于「统计基数」的数据集合类型,基数统计就是指统计一个集合中不重复的元素个数。
基于概率完成的,不是非常准确,标准误算率是 0.81%。
提供不精确的去重计数,在输入元素的数量或者体积非常非常大时,计算基数所需的内存空间总是固定的、并且是很小的。
# 添加指定元素到 HyperLogLog 中
PFADD key element [element ...]
# 返回给定 HyperLogLog 的基数估算值。
PFCOUNT key [key ...]
# 将多个 HyperLogLog 合并为一个 HyperLogLog
PFMERGE destkey sourcekey [sourcekey ...]
难。
存储地理位置信息,并对存储的信息进行操作。
内部实现使用了Sorted Set集合。
Redis Stream 是 Redis 5.0 版本新增加的数据类型,Redis 专门为消息队列设计的数据类型。
在 Redis 5.0 Stream 没出来之前,消息队列的实现方式都有着各自的缺陷,例如:
基于以上问题,Redis 5.0 便推出了 Stream 类型也是此版本最重要的功能,用于完美地实现消息队列,它支持消息的持久化、支持自动生成全局唯一 ID、支持 ack 确认消息的模式、支持消费组模式等,让消息队列更加的稳定和可靠。
Stream 消息队列操作命令:
给定一个时间点key,到达时间,数据被认为是过期的,由Redis进行回收。
如果不是常驻的数据,设置过期时间,可以有效节约内存。
缓存设置过期时间,保证数据有效性。
设置过期时间之后会有个字典,专门记录这些Ky和过期时间的关系。
过期后删除策略包括
实际中通过惰性删除 + 定期删除二者结合方式进行删除。
定期删除需要注意:
有点类似C++里面智能指针了,在redisObject结构有一个refcount字段,减少到0时,就会触发对象的释放。
注意这里目前是只有encoding为整数,并且在0-9999时才会有用,因为
最大的作用,在内部场景中用refcount进行引用计数,传递参数参数时,避免拷贝。