1 为什么需要缓存?
读多写少且更新频率低的场景,通过增加缓存层,提高查询吞吐量和降低响应时间。
2 缓存的缺点
2.1 缓存层和持久化层的一致性问题(数据内容不一致,变更非实时同步)。
2.2 缓存失效引发持久化层压力增大
2.3 内存成本高
3 缓存选择
更新频率
数据一致性要求
存储量级(大量redis,少量本地缓存)
4 常见缓存
4.1 Cache-Aside
读请求旁路写入缓存,写请求删除缓存
优点:
写时删除而不是写时更新缓存,更省内存。
相比于先删缓存后更新db,减小db和cache不一致的概率。
避免多次连续写的并发问题。
加分布式锁可以实现强一致性
不足:
删除缓存后,重新更新缓存前。大量读请求进来,对DB造成压力。(读DB前加分布式锁,降低DB读压力)
缓存不一致问题仍然存在(可能是读了从库或操作时间顺序不同等原因,可以设置一个过期时间或加分布式锁DB+cache操作原子化)
4.2 监听binlog
适用于对最终一致性要求高,读流量大,写流量小。
优点:
- 保证缓存最终一致性(避免多个消费者的消息同步问题 可以按顺序消费mq,比如按照主键id顺序消费。
或者使用分布式锁并比较更新时间实现缓存更新)
对读操作无入侵,不需要再更新缓存。
适合构建纯读缓存服务。
写缓存逻辑错误时,可以通过mq实现消息回放。
缺点:
- 引入中间件增加复杂度,实效性取决于mq是否积压
4.3 Write-Through 直接写缓存
直写模式, DB和缓存更新封装在一起。
优点:
通过事物保证强一致性
对读操作无入侵
缺点:
- 更新效率低(两个都成功才算成功,增加事物执行时间)
4.4 Write-Behind 先写缓存
[图片上传失败...(image-1be9d4-1671794849998)]
读写都在缓存,异步持久化
优点:
- 写吞吐量大(适合写频繁场景,如秒杀)
缺点:
- 一致性差
5 缓存准确性
1 核心数据,读数据值分布比例埋点监控(比如枚举值比例,空和非空值比例)。
2 核心数据周期全量刷表,进行数据重建。
3 核心数据校验,db和cache缓存数据后台比较。
4 核心数据有生产mq积压报警。
5 数据重建:缓存生产逻辑错误时刷数据脚本,并发刷表重建缓存。
6 非重要数据,可以通过过期时间更新缓存。
6 总结
写少读多 cache-aside 监听binlog
写多读多 Write-Through
写极端多 Write-Behind
一致性要求低cache-aside
最终一致性要求较高 监听binlog
强一致性要求高 cache-aside+分布式锁 Write-Through