这是我参与「第五届青训营 」伴学笔记创作活动的第 16 天
Redis基本工作原理
Redis实现数据持久化的原理
数据从内存中读写
将数据保存在硬盘上防止重启数据丢失
- 增量数据保存在AOF文件
- 全量数据RDB 文件中
- Redis单线程处理命令的概念
Redis应用案例
掘金连续签到
Key过期 string 数据结构 sds
len alloc flags buf
可以存储字符串 数字 二进制数据
通常和expire配合使用
场景:存储计数 session
消息队列
利用list实现,数据结构quicklist
quicklist 由双向链表和listpack 实现
listpack
listpack 也叫紧凑列表,它的特点就是用一块连续的内存空间来紧凑地保存数据,同时为了节省内存空间,listpack 列表项使用了多种编码方式,来表示不同长度的数据,这些数据包括整数和字符串
<tot-bytes> <num-elements> <element-1> ... <element-N> <listpack-end-byte>
- Total Bytes为整个listpack的空间大小,占用4个字节,每个listpack最多占用4294967295Bytes。
- Num Elem为listpack中的元素个数,即Entry的个数,占用2个字节,值得注意的是,这并不意味着listpack最多只能存放65535个Entry,当Entry个数大于等于65535时,Num Elem被设置为65535,此时如果需要获取元素个数,需要遍历整个listpack。
- Entry为每个具体的元素。
- End为listpack结束标志,占用1个字节,内容为0xFF
计数
HASH数据结构 dict
在较大数据进行迁移时会出现阻塞情况
渐进式rehash
我们知道当HashMap中由于Hash冲突(负载因子)超过某个阈值时,出于链表性能的考虑,会进行Resize的操作。Redis也一样。
在redis的具体实现中,使用了一种叫做渐进式哈希(rehashing)的机制来提高字典的缩放效率,避免 rehash 对服务器性能造成影响,渐进式 rehash 的好处在于它采取分而治之的方式, 将 rehash 键值对所需的计算工作均摊到对字典的每个添加、删除、查找和更新操作上, 从而避免了集中式 rehash 而带来的庞大计算量。
扩展和收缩需要将ht[0]上的所有键值对rehash到ht[1]哈希表中。但是redis的rehash动作并不是一次性完成的,而是分多次、渐进式的完成的。
渐进式rehash步骤:
- 为ht[1]分配空间
- 将字典中的rehashidx设置为0,表示rehash正式开始,rehash期间,不会阻塞CRUD等操作
- 当ht[0]所有的键值对都rehash到ht[1]时,将rehashidx属性设置成-1,表示rehash完成
排行榜
zset数据结构 跳表
结合dict实现排行榜功能
跳表
跳表是可以实现二分查找的有序链表。
跳表插入、删除、查找元素的时间复杂度跟红黑树都是一样量级的,时间复杂度都是O(logn),
我们可以通过较少索引数来减少空间复杂度,但是相应的肯定会造成查找效率有一定下降,我们可以根据我们的应用场景来控制这个阈值,看我们更注重时间还是空间。
按照区间查找数据时,跳表可以做到 O(logn) 的时间复杂度定位区间的起点,然后在原始链表中顺序往后遍历就可以了,非常高效。
在字节跳动,使用Redis有哪些注意事项
-
大Key:Value大于10KB就是大Key,使用大Key将导致Redis系统不稳定
-
热Key:一个Key的QPS特别高,将导致Redis实例出现负载突增,负责均衡流量不均的情况。导致单实例故障
-
慢查询:大Key、热Kye的读写;一次操作过多的Key(mset/hmset/sadd/zadd)
-
导致缓存穿透、缓存雪崩的场景及避免方案