Redis
缓存穿透
-
定义:redis查询一个不存在的数据,导致每次都查询数据库
-
解决方案:
-
如果查询的数据为空,在redis对应的key缓存空数据,并设置短TTL。
因为缓存穿透通常是因为被恶意用不存在的查询参数进行压测攻击,所以采取这种方式会导致大量的无用空间被占用,所以不推荐。
-
布隆过滤器,因为哈希冲突,所以可能误判(计算出的下标位置会出现重复,导致不存在的元素被错误地判定为存在),实现框架:
Redisson
、Guava
。-
bitmap
(位图):一个以bit为单位的数组,只存储二进制01 -
原理:对于一个给定的 key,通过多个不同的 hash 函数 计算得到多个 hash 值,每个 hash 值对 m(bitmap 的大小) 进行 取模运算,得到对应的
bitmap
下标位置,并将这些位置的 bit 值设置为 1。读取时,只需要判断其对应的多个下标位置的元素值是否都为1即可 -
如何控制误判率?调整参数:
bitmap
数组长度m、哈希函数个数k、元素数量n -
支持删除操作吗?不支持,除非使用计数型布隆过滤器(整数数组,存计数值,即添加操作+1,删除操作-1,如果所有计数值都大于0时,元素才可能存在)
-
布隆过滤器相比使用
set
的优点?-
空间占用: 布隆过滤器相比
set
可以显著节省空间,因为它不存储完整的元素,而是通过哈希映射到bit位查询速度: 布隆过滤器通过少量哈希计算(O(k))就能快速判断元素是否存在,在元素数量非常多时,可能比set数据更快,因为set可能需要遍历链表/红黑树
-
bitmap
都是01数据,适合集合运算(交、并、补)
-
-
-
缓存击穿
-
定义:给一个key设置过期时间,当key过期时,恰好对这个key有大量并发请求,在重建缓存这段时间可能导致数据库压力过大
-
如何检测?监控 Redis 的
keyspace_hits(命中缓存次数)
和keyspace_misses(未命中缓存次数)
,计算命中率 -
解决方案:
- 互斥锁(保证强一致性):在Redis查询缓存未命中时,添加互斥锁,然后查询数据库并重建缓存,再释放锁
- 逻辑过期+互斥锁(高效):对热点key不设置过期时间,而是给对应的数据添加一个过期时间的字段。过程:
- 在查询缓存时发现逻辑时间已经过期,此时添加互斥锁
- 开启新子线程:查询数据库并重建缓存,释放互斥锁
- 主线程和其他发起请求的线程不需要等待该子线程(trylock),而是直接返回过期数据
缓存雪崩
- 定义:在同一时段大量的缓存key同时失效或者Redis服务宕机,导致数据库压力过大
- 解决方案:
- key失效:给不同key的TTL的原值加上随机值
- 服务宕机:Redis集群
- 通用:降级限流(nginx或spring cloud gateway),多级缓存
双写一致性
-
定义:数据库中的数据需要与Redis中的数据保持一致
-
问题:
- 先删除缓存再操作数据库:在删除完缓存到操作数据库这段期间,另一个线程重建完缓存。
- 先操作数据库再删除缓存:在查询完数据库到重建缓存这段期间,另一个线程修改了数据库并删除缓存。
-
高一致性实现方案:
-
延迟双删:删除缓存,修改数据库,延迟一会再删除缓存,延迟的原因是等待数据库master同步到slave 。该方法的问题是延迟时间很难确定。
-
读数据时添加共享锁:其他线程可共享读操作
写数据时添加排他锁:阻塞其他线程读写操作
-
-
弱一致性实现方案:
- MQ:修改数据库时,发布消息到mq中,由消费者来删除缓存,只能保证redis操作的可靠性
- Canal:监听mysql的binlog(日志文件),把更新后的日志同步到redis里面