缓存更新策略
内存淘汰 | 超时剔除 | 主动更新 | |
---|---|---|---|
说明 | 利用Redi的内存淘汰机制,当内存不足时自动淘汰部分数据,下次查询时更新缓存 | 给缓存数据添加TTL(即缓存存在时间)的时间,到期后自动删除缓存,下次查询时更新缓存 | 在修改数据库的同时,更新缓存 |
一致性 | 差 | 一般 | 好 |
维护成本 | 无 | 低 | 高 |
业务使用场景:
- 低一致性需求:使用内存淘汰机制。
- 高一致性需求:主动更新,并以超时剔除作为兜底方案。
主动更新策略方案
- (推荐)由缓存的调用者,在更新数据库的同时更新缓存。
- 缓存与数据库整合作为一个服务,由服务来维护一致性。调用者调用该服务,无需关心缓存一致性问题。
- 调用者只操作缓存,由其他线程异步的将要缓存持久化到数据库,保证最终的一致性。
在更新数据库的同时更新缓存方案
在更新数据库的同时更新缓存需要考虑以下问题:
(1)删除缓存还是更新缓存?
- 删除缓存(推荐):更新数据库时让缓存失效,查询时再更新缓存
- 更新缓存:每次更新数据库都要更新缓存,无效写操作较多
(2)如何保证缓存操作与数据库操作同时失败与成功?
- 单体系统:将缓存与数据库操作放在一个事务
- 分布式系统:利用TCC等分布式事务
(3)先操作数据库还是缓存?
一、先删除缓存。再操作数据库
正常业务场景:
缓存数据:20;数据库数据:20
- 线程A:删除缓存,更新数据库为10
此时:缓存数据:空;数据库数据:10
- 线程B:查询缓存,未命中,查询数据库,写入缓存
此时:缓存数据:10;数据库数据:10
异常业务场景:
缓存数据:20;数据库数据:20
- 线程A:删除缓存
此时:缓存数据:空;数据库数据:20
- 线程B:查询缓存,未命中,查询数据库,写入缓存
此时:缓存数据:20;数据库数据:20
- 线程A:更新数据库10
此时:缓存数据:20;数据库数据:10
这种情况就造成了缓存与数据库不一致的情况。
因为操作数据库的时间远大于删除缓存的时间,这种情况出现的可能性会比较大。
二、(推荐)先操作数据库,再删除缓存
正常业务场景:
缓存数据:20;数据库数据:20
- 线程A:更新数据库10,删除缓存
此时:缓存数据:空;数据库数据:10
- 线程B:查询缓存未命中,查询数据库,写入缓存
异常业务场景:
缓存数据:空;数据库数据:20
- 线程A:查询缓存,未命中,查询数据库20
此时:缓存数据:空;数据库数据:20
- 线程B:更新数据库10,删除缓存
此时:缓存数据:空;数据库数据:10
- 线程A:写入缓存20
此时:缓存数据:20;数据库数据:10
这种情况就造成了缓存与数据库不一致的情况。
为什么更推荐使用这种方式,因为作数据库的时间远大于删除缓存的时间,这种情况出现的可能性相对于上面一种要小。
缓存更新策略的最佳实践方案
(1)低一致性要求:使用redis自带的内存淘汰机制
(2)高一致性要求:主动更新,并以超时剔除作为兜底方案
1.读操作
- 缓存命中则直接返回
- 缓存未命中则查询数据库,并写入缓存,设定超时时间
2.写操作
- 先写数据库,然后再删除缓存
- 要确保数据库与缓存之间的原子性