想起遇到过的缓存与数据库的问题和一些思考,在这里做一些系统性的分析和总结,讨论一下对数据库与缓存的一致性问题及解决方案。
1,先更新数据库再更新缓存
可能出现的问题:
a). A更新数据库(或发现缓存失效而查询数据库)
b). B更新数据库
c). B更新缓存
d). A更新缓存(覆盖了B的缓存)
出现数据不一致的问题,且问题发生可能性较小。(如果A更新数据库较耗时)
另外,当更新数据库成功但更新缓存失败时,也会出现缓存脏数据,这时需要等待下一次更新操作来覆盖脏数据。
解决方案:
a). 当有请求更新数据库时加读写锁并在缓存中填充占位符,当最终更新缓存后释放锁,保证每次只会有一个请求进行更新操作。(一致性强但有性能影响)
b). 异步延时(令耗时久的操作延时更久)进行更新缓存的操作,投递到消息中间件尽量保证顺序。
2,先更新缓存再更新数据库
可能出现的问题:
a). A更新缓存(或发现缓存失效而查询数据库到应用)
b). B更新缓存
c). B更新数据库
d). A更新数据库(或A将a步骤拉取的数据存到缓存)
出现数据不一致的问题,且问题发生可能性较大。(操作缓存的速度很快,所以很难确定c、d操作的顺序)
另外,当操作缓存成功但更新数据库失败时,也会出现缓存脏数据,这时需要等待下一次更新操作来覆盖脏数据。
解决方案:
a). 异步延时(令耗时久的操作延时更久)进行更新数据库的操作,投递到消息中间件尽量保证顺序。
b). 此类问题出现问题的情况比较多且容易,比较难解决,最好的解决方案是不要采用这种方式。
3,先更新数据库再令缓存失效
可能出现的问题:
a). A发现缓存失效而查询数据库
b). B更新数据库
c). B令缓存失效
d). A将a步骤拉取的数据存到缓存
出现数据不一致的问题,且问题发生的可能性小。(必须满足的条件是 1.此时刚好缓存失效 2.A,B对同样的数据操作,存在行级锁的情况下也会先执行完查询才能执行更新,而c、d的顺序则是由应用层的业务代码耗时来决定)
另外,当更新数据库成功但操作缓存失败时,也会出现缓存脏数据。
解决方案:
a). 异步延时进行缓存失效的操作,根据不同业务场景耗时情况来控制延时时间,可以尽量保证最小时间存在脏数据。
4,先令缓存失效再更新数据库
可能出现的问题:
a).B令缓存失效
b).A发现缓存失效而查询数据库
c).B更新数据库
d).A将a步骤拉取的数据存到缓存
出现数据库不一致的问题,且问题发生的可能性较大。(只要d在a操作后时就会出现问题,可能出现的情况太多)
另外,当操作缓存成功但数据库操作失败时,令更多的请求打到数据库上,增加了数据库压力。
解决方案:
a). 当有请求更新数据库时加读写锁并在缓存中填充占位符,当最终令缓存失效后释放锁,保证每次只会有一个请求进行更新操作。(一致性强但有性能影响)
b). 此类问题出现问题的情况比较多且容易,比较难解决,最好的解决方案是不要采用这种方式。
通过分析,得出在数据库、缓存双写尽量保证数据一致性的比较好的选择应该是先更新数据库再令缓存失效 。
理由:
1). 出现一致性问题的情况较少且难发生
2). 解决方案较容易实现
另外,对于不同业务上的解决方案也应该有所不同,毕竟不同业务对于数据处理的便利性、性能和数据一致性的要求不同。