这是我参与「第三届青训营 -后端场」笔记创作活动的第2篇笔记。 基本类型:
string(字符串)、hash(哈希)、list(列表)、set(集合)、zset(有序集合)
高级类型:
geospatial、hyperloglog、bitmap
String
简介:String是Redis最基础的数据结构类型,它是二进制安全的,可以存储图片或者序列化的对象,值最大存储为512M。
简单使用举例: set key value、get key
等应用场景:共享session、分布式锁,计数器、限流。
内部编码有3种,int(8字节长整型)/embstr(小于等于39字节字符串)/raw(大于39个字节字符串)
struct sdshdr{
unsigned int len; // 标记buf的长度
unsigned int free; //标记buf中未使用的元素个数
char buf[]; // 存放元素的坑
}
空间预分配
空间预分配用于优化 SDS 的字符串增长操作:当 SDS 的 API 对一个 SDS 进行修改,并且需要对 SDS 进行空间扩展的时候,程序不仅会为 SDS 分配修改所必要的空间,还会为 SDS 分配额外的未使用空间。
- 如果修改完 SDS 后,SDS 的长度(len)小于 1 MB,那么程序分配和 len 属性同样大小的未使用空间,这时(扩展后) SDS len 属性的值将与 free 的值相同 。比如修改后,SDS 的 len 改为了 13 字节,那么程序也会预分配 13 字节给 free 属性,SDS 的 buf 数组的实际长度将变成 13B+13B+1B('/0')=27 字节。
- 如果修改完 SDS 后,SDS 的长度将大于 1 MB,那么程序会分配 1 MB 的未使用空间。比如修改后,SDS 的 len 改为 30 MB,那么程序会分配 1 MB 的未使用空间,SDS 的 buf 数组的实际长度将变成 30MB + 1MB + 1B。
通过这种空间预分配策略,Redis可以减少连续执行字符串涉及长度修改操作所需的内存重分配次数 。
惰性空间释放
惰性空间释放用于优化 SDS 的字符串缩短操作:当 SDS 的 API 需要缩短 SDS 保存的字符串时,程序并不立即使用内存重分配来回收缩短后多出来的字节,而是使用 free属性 将这些字节的数量记录起来,并等待将来使用。
二进制安全
C 字符串中的字符必须符合某种编码(比如 ASCII),并且除了字符串的结尾外,字符串里面不能包含空字符('/0'),否则最先被程序读入的空字符将被误认为是字符串结尾,这些限制使得 C 字符串只能保存文本数据,而不能保存像图片、音频、视频、压缩文件这样的二进制数据 。如上图,假如是 C 字符串的函数的话只会识别到 "Redis",而忽略之后的 "Cluster"。
虽然数据库一般用于保存文本数据,但使用数据库来保存二进制数据的场景也不少见,因此,为了确保 Redis 可以适用于各种不同的场景,SDS 的 API 是 二进制安全 的(binary-safe),所有 SDS API 都会以处理二进制的方式来处理 SDS 存放的 buf 数组里的数据,程序不会对其中的数据做任何限制、过滤或者假设, 数据在写入时是什么样的,它被读取时就是什么样的 。
总结
| C字符串 | SDS |
|---|---|
| 获取字符串长度的复杂度为O(N) | 获取字符串长度的复杂度为O(1) |
| API是不安全的,可能会造成缓冲区溢出 | API是安全的,不会造成缓冲区溢出 |
| 修改字符串长度N次必然需要执行N次内存重分配 | 修改字符串长度N次最多需要执行N次内存重分配 |
| 只能保存文本数据 | 可以保存文本或者二进制数据 |
Hash
简介:在Redis中,哈希类型是指v(值)本身又是一个键值对(k-v)结构
简单使用举例:hset key field value 、hget key field
内部编码:ziplist(压缩列表) 、hashtable(哈希表)
应用场景:缓存用户信息等。
List
简介:列表(list)类型是用来存储多个有序的字符串,一个列表最多可以存储2^32-1个元素。
简单实用举例:lpush key value [value ...] 、lrange key start end
内部编码:ziplist(压缩列表)、linkedlist(链表)
应用场景: 消息队列,文章列表
- lpush+lpop=Stack(栈)
- lpush+rpop=Queue(队列)
- lpsh+ltrim=Capped Collection(有限集合)
- lpush+brpop=Message Queue(消息队列)
Set
- 简介:集合(set)类型也是用来保存多个的字符串元素,但是不允许重复元素
- 简单使用举例:sadd key element [element ...]、smembers key
- 内部编码:intset(整数集合)、hashtable(哈希表)
- 注意点:smembers和lrange、hgetall都属于比较重的命令,如果元素过多存在阻塞Redis的可能性,可以使用sscan来完成。
- 应用场景: 用户标签,生成随机数抽奖、社交需求。
Zset
- 简介:已排序的字符串集合,同时元素不能重复
- 简单格式举例:zadd key score member [score member ...],zrank key member
- 底层内部编码:ziplist(压缩列表)、skiplist(跳跃表)
- 应用场景:排行榜,社交需求(如用户点赞)。
高级数据类型
- Geo:Redis3.2推出的,地理位置定位,用于存储地理位置信息,并对存储的信息进行操作。
- HyperLogLog:用来做基数统计算法的数据结构,如统计网站的UV。
- Bitmaps :用一个比特位来映射某个元素的状态,在Redis中,它的底层是基于字符串类型实现的,可以把bitmaps成作一个以比特位为单位的数组。\