五种基础数据结构
redis 的所有数据结构都以唯一的 key 字符串作为名称,通过唯一的 key 获取相对应的 value 数据。不同的数据类型的差异就在于 value 的结构不一样。
- string(字符串)
- list(列表)
- hash(字典)
- set(集合)
- zset(有序集合)
string
string 是最简单的数据结构,其内部就是一个字符数组。string 是动态字符串,相当于 ArrayList,当字符串长度小于 1M 时扩容都是加倍现有的空间。当字符串长度大于 1M 时每次扩容只会增加 1M 的空间。字符串长度最大为 512M。
【键值对操作】
127.0.0.1:6379> set name codehole
OK
127.0.0.1:6379> get name
"codehole"
127.0.0.1:6379> exists name
(integer) 1
127.0.0.1:6379> del name
(integer) 1
127.0.0.1:6379> get name
(nil)
127.0.0.1:6379>
【批量键值对操作】
- mset key value [key value ...]
- mget key [key ...]
127.0.0.1:6379> set name1 codehole
OK
127.0.0.1:6379> set name2 holycoder
OK
127.0.0.1:6379> mget name1 name2 name3
1) "codehole"
2) "holycoder"
3) (nil)
127.0.0.1:6379> mset name1 boy name2 girl name3 unknown
OK
127.0.0.1:6379> mget name1 name2 name3
1) "boy"
2) "girl"
3) "unknown"
127.0.0.1:6379>
4.4 rehash
随着操作的不断执行,哈希表保存的键值对会增加或减少。为了让哈希表的负载因子维持在合理范围,需要对哈希表的大小进行扩展或收缩,即通过执行rehash(重新散列)来完成:
-
为字典的ht[1]哈希表分配空间:
如果执行的是扩展操作,ht[1]的大小为第一个大于等于ht[0].used * 2 的2^n
如果执行的是收缩操作,ht[1]的大小为第一个大于等于ht[0].used的2^n
-
将保存在ht[0]中的所有键值对rehash到ht[1]上。rehash是重新设计的计算键的哈希值和索引值
-
释放ht[0],将ht[1]设置为ht[0],并为ht[1]新建一个空白哈希表
哈希表的扩展与收缩
满足一下任一条件,程序会自动对哈希表执行扩展操作:
- 服务器目前没有执行BGSAVE或BGREWRITEAOF,且哈希表负载因子大于等于1
- 服务器正在执行BGSAVE或BGREWRITEAOF,且负载因子大于5
其中负载因子的计算公式:
# 负载因子 = 哈希表已保存节点数量 / 哈希表大小
load_factor = ht[0].used / ht[0].size
注:执行BGSAVE或BGREWRITEAOF过程中,Redis需要创建当前服务器进程的子进程,而多数操作系统都是用写时复制来优化子进程的效率,所以在子进程存在期间,服务器会提高执行扩展操作所需的负载因子,从而尽可能地避免在子进程存在期间扩展哈希表,避免不避免的内存写入,节约内存。
渐进式rehash
将ht[0]中的键值对rehash到ht[1]中的操作不是一次性完成的,而是分多次渐进式的:
- 为ht[1]分配空间
- 在字典中维持一个索引计数器变量rehashidx,设置为0,表示rehash工作正式开始
- rehash期间,每次对字典的增删改查操作,会顺带将ht[0]在rehashidx索引上的所有键值对rehash到ht[1],rehash完成之后,rehashidx属性的值+1
- 最终ht[0]会全部rehash到ht[1],这是将rehashidx设置为-1,表示rehash完成
渐进式rehash过程中,字典会有两个哈希表,字典的增删改查会在两个哈希表上进行。