为什么需要缓存更新?
使用缓存后,数据可能同时保存在数据库与缓存当中。如果数据库的数据改变,而缓存中的数据没有改变,就会导致数据不一致的问题。
内存淘汰 | 超时剔除 | 主动更新 | |
---|---|---|---|
概述 | redis自带的内存淘汰机制,当内存不足时自动淘汰部分数据 | 手动给缓存的数据添加TTL时间,到期后自动删除 | 编写业务逻辑,更新数据库数据后同步更新缓存 |
解决数据一致性的问题 | 差 | 一般 | 好 |
使用成本 | 无 | 低 | 高 |
关于内存淘汰机制解决数据一致性:
若只依靠内存淘汰机制,可能会出现这种问题:内存淘汰机制一直未执行,所以缓存中的数据一直未更新,而数据库中的数据已经改变。
不同策略合适的业务场景:
主动更新目前我了解的三种实现方法:
三种方法中,个人认为最好的就是第一种,实现简单且能较好的解决数据一致性问题。但是在同时操作数据库和缓存时,有以下三个问题:
删除缓存还是更新缓存?
更新缓存
:每次更新数据库都要更新缓存,会增加写操作,很可能会写多读少,消耗资源来进行无效写操作。
删除缓存
:更新数据库时,让缓存失效,当再次查询时再更新缓存。
如何保证缓存与数据库的操作同时成功或失败?(原子性问题)
单体系统
:将缓存和数据库放在同一个事务中
分布式系统
:利用TTC
等分布式事务方案
先操作缓存还是先操作数据库?(线程安全问题)
假设缓存中有数据v=10,现在修改了v=20。分别以两种操作方式来讨论:
先删缓存再操作数据库:
理想情况:
1. 线程1删除缓存
2. 线程1数据库更新数据v=20
3. 线程2查询数据,缓存未命中
4. 线程2将数据库中的v=20写入缓存
异常情况:
1. 线程1删除缓存,线程1结束
2. 线程2查询数据未命中
3. 线程2从数据库读取v=10写入缓存
4. 线程1更新数据库数据v=20
数据库数据和缓存数据不一致,发生概率高,因为redis操作数据库快,而数据库操作慢
先操作数据库再删除缓存:
理想情况:
异常情况:
1. 缓存中的数据失效
2. 线程1查询缓存未命中,查询数据库,将v=10传递给用户,线程1结束
3. 线程2更新数据库,v=20写入数据库
4. 线程2删除缓存,但是这个缓存已经是失效的了,线程2结束
5. 线程2写入缓存v=20
这种情况发送的较少,需要以下条件:
- 两个线程并行执行
- 线程1查询时,恰好缓存失效
- 线程1写缓存时,恰好切换到了线程2
- 线程2恰好更新数据库
两种情况都可能会发生线程安全问题,但是先操作数据库再操作缓存这种方案发生线程安全的可能性更小。
总结: