本文介绍Redis的常用数据类型的底层实现。
1. 底层数据结构
1.1 SDS简单动态字符串
SDS简单动态字符串是String的底层实现。SDS简单动态字符串维护了字段len用来记录字符串长度,故而返回长度为O(1);维护了字段free记录空闲空间长度,用来实现惰性空间释放。
两个主要优势:
- 空间预分配:SDS简单动态字符串被修改后,程序不仅会为SDS分配所需要的必须空间,还会分配额外的未使用空间。
- 惰性空间释放:当对String进行删除或缩短操作时,程序并不会直接回收多余内存,而是将多余字符置为空字符,使用 free 字段将这些字节数量记录下来不释放,后面如果需要 append 操作时,则直接使用 free 中未使用的空间,减少了内存的分配。当添加字符串时,会判断空闲空间是否够用,若不够用则需要扩容,如果目前的字符串小于1M,则直接扩容双倍,如果目前的字符串大于1M,则直接添加1M。
1.2 zipList 压缩列表
压缩列表是 List、Hash以及Sorted Set 三种数据类型底层实现之一。
当一个列表只有少量数据的时候,并且每个列表项要么就是小整数值,要么就是长度比较短的字符串,那么 Redis 就会使用压缩列表来做列表键的底层实现。这样内存紧凑,节约内存。
1.3 linkedList 双向链表
List以前底层实现是linkedList+zipList,后来使用快表来代替前者。
1.4 quickList 快表
quicklist 是 ziplist 和 linkedlist 的混合体,它将 linkedlist 按段切分,每一段使用 ziplist 来紧凑存储,多个 ziplist 之间使用双向指针串接起来。
1.5 skipList 跳表
是Sorted Set的底层实现之一。可以理解为带多层索引的链表。最底层是有序链表,并增加了多层级索引,通过索引位置的区间查找,快速定位数据。
1.6 intSet 整数集合
当一个集合只包含整数值元素,并且这个集合的元素数量不多时,Redis 就会使用整数集合作为集合键的底层实现,节省内存。
1.7 hashTable 哈希表
Hash的底层实现,使用链地址法解决冲突,渐进式rehash扩容(复制出另外一个hash表,重新计算hashcode,分多次渐进完成,在此期间删除查找更新等操作会在两个hash表上进行,第一个没找到就会去第二个找,但是新增操作一定是在新的hash表上进行)。
2. 数据类型对应实现
| 类型 | 编码 |
|---|---|
| STRING | INT(整型)、EMBSTR(SDS)、RAW(SDS) |
| LIST | QUICKLIST(快表) |
| SET | INTSET(整数集合)、HASHTABLE(哈希表) |
| ZSET | SKIPLIST(跳表)、ZIPLIST(压缩列表) |
| HASH | ZIPLIST(压缩列表)、HASHTABLE(哈西表) |