Day9 Redis补充
过期键缓存清除策略
-
清除策略
- 惰性删除(被动)当读写一个已经过期的key时,会直接删除掉这个key,然后返回空,但如果一个key一直没有访问,就会一直存在内存里
- 定期删除(主动) redis每隔100ms触发一次定时任务,随机抽取一批设置了过期时间的key,发现已过期就直接删掉,未过期的保留
- 策略删除 当内存达到maxmemory上线时,触发内存淘汰机制
-
内存淘汰策略,共8种
maxmemory-policy默认是noeviction,推荐使用volatile-lru,如果访问模式稳定,volatile-lfu的命中率会更高些,一定要设置maxmemory,否则redis到达机器内存上限时,内存数据会开始频繁跟磁盘发生交换
- volatile-ttl 设置了过期时间的key,根据过期时间先后删除,优先淘汰剩余存活时间短的
- volatile-random 对设置了过期时间的key随机淘汰
- volatile-lru 对设置了过期时间的key,使用LRU(最近最少使用)算法淘汰
- volatile-lfu 对设置了过期时间的key,使用LFU(最少频次使用)算法淘汰
- allkeys-random 对所有key进行随机淘汰
- allkeys-lru 对所有key,使用LRU(最近最少使用)算法淘汰
- allkeys-lfu 对所有key,使用LFU(最少频次使用)算法淘汰
- noeviction 不处理,拒绝写入并返回OOM command not allowed
-
LRU与LFU
- LRU 最近最少使用,最近没访问过的先淘汰。
- 对于访问频次很少但最近有访问过的,短期内不会删除,会污染缓存
- LFU 最少频次使用,访问次数最少的先淘汰。
- LRU 最近最少使用,最近没访问过的先淘汰。
HotKey
指在有限时间内被高频访问的key,可分为有预期的热点和无预期的热点
- 有什么危害?
- 容易引起请求排队,集群模式下流量会严重倾斜,严重时可能导致服务瘫痪
- 怎样算热点key?
-
参考阿里云定义

-
- 怎么发现?
- 业务预判
- redis-cli —hotkeys命令
- 客户端埋点
- Proxy代理层收集
- 如何优化?
-
热点key拆分
-
多级缓存 在redis前加一层本地缓存
本地缓存可以减少网络请求,提高性能,缓解远程缓存压力;但是空间大小有限,不支持大量数据存储,重启程序数据会丢失,也有可能跟远程缓存数据不一致
-
读写分离 读请求放在从节点
-
限流降级 限制请求流量,保系统优先
-
JDHotKey热点探测系统
- 核心目标
- 实时性 热点发现延迟小于等于1s
- 低开销 对业务代码无入侵
- 高吞吐 支持百万级QPS的key流量分析
- 自动应对 发现热点key后自动推送到本地缓存
- 动态适应 热点变化快速收敛或释放
- 架构方案
- Agent代理
- 内嵌client客户端,无需独立部署,随client启动
- client用于拦截Redis调用,采集key访问数据
- 使用时间滑动窗口统计每个key的访问频次
- 高频过滤,只上报疑似key,避免海量普通key上报
- 本地缓存防护
- 一旦收到热key通知,自动缓存到本地
- 写操作走原链路,可配合本地缓存实效广播
- Center中心
-
worker集群 汇总client上报数据,实时计算热点阈值,判定热key并下发热key
热key判定算法 动态阈值:基于历史流量自动调整 突发检测:使用指数加权移动平均识别流量突增 防抖机制:避免频繁上下线 热key下发 通过websocket/tcp实时推送,支持分级热度
-
etcd集群 存储规则配置,worker ip,热key列表,提供监听订阅
-
- Dashboard控制台
- 可视化配置热key匹配策略
- 监控结果
- Agent代理
- 实现能力
- 毫秒级保护
- 熔断降级 center不可用时,agent自动退化为本地简单限流
- 分级热度 热度策略差异化

Redis扩展
-
RedisJson
提供对json数据的原生支持,可以对json中的数据进行增删改查等操作
- 相比于string的优势
- 存储json数据性能更高,底层以二进制格式存储,相比文本格式,读写性能高,也更节省内存
- 采用树状结构存储json,可以快速访问子元素
- 生态集成度高
- 相比于string的优势
-
BloomFilter
- 只能加数据,不能删数据,有变动时只能重建
- BF.RESERVE bf 0.01 1000 NONSCALING
-
CuckooFilter
- 相比于BloomFilter,新增了删除指令 指令以CF开头
- CF.RESERVE cf 1000 BUSKETSIZE 2 MAXITERATIONS 20 EXPANSION 1
Redis常用类型底层数据结构
可以使用object encoding key查看底层实现类型
-
String字符串
基本的数据单元,可以存储字符串、整数或者浮点数
底层用SDS(简单动态字符串Simple Dynamic String)小数据量编码使用embstr/int类型,大数据量编码使用raw类型,切换阈值为44


-
List列表
简单的列表,最多存储40亿个成员,底层使用双向链表,两端操作性能高,随机读写性能低
使用要注意大key问题
小数据量使用listpack(7之前是ziplist)大数据量使用quicklist,切换阈值为512个元素或单元素超64字节
ziplist 是一块连续内存,把所有元素紧凑地挨在一起存,省内存空间但不适合大量数据
每个entry都要记录上一个节点的长度,如果前面插入了一个大元素,那么后面数据的prev_entry_length就都得扩容,会触发连锁更新

listpack 一种紧凑型序列化数据结构,把数据直接按字节序列存储,用于替代ziplist
主要差别就是Entry内部结构发生改变 listpack不再记录前一个entry的长度,而是记录自己的长度

quicklist 是用双向链表串联的一堆ziplist/listpack,将连续的大量数据分散,解决更新时需要操作大量节点的痛点,此外quicklist还支持对中间节点做LZF压缩

-
Hash哈希表
简单的键值对集合,每个键值对可以储存多个字段,最多存储40亿个成员
相比string操作消耗更小,也能节省存储空间,但使用时要注意大key问题,过期时间不能作用在field上,有局限性
小数据量使用listpack,大数据量使用hashtable,切换阈值为512字段或单值超64字节
-
Set集合
一个无序集合,最多存储40亿个成员
小数据量使用intset/listpack 大数据量用hashtable,新增、删除、查找复杂度都是O(1),切换阈值为512个元素或含非整数
-
ZSet有序集合
一个有排序的集合,最多存储40亿个成员
小数据量使用listpack,大数据量使用skiplist+hashtable,切换阈值为128个元素或单值超64字节
skiplist 就是多层链表,底层保存全部元素,上层是下层的子集
随机层级概率算法,每次循环有25%的概率加一层,redis7最多32层,能够存储2^64个元素 第一层 75%的节点 第二层 25%的节点 第三层 6.25%的节点 第n层 0.25^(n-1)的节点









