原标题:高并发下数据库和缓存一致性的保证
对于缓存的使用,从理论上来说,既然想要使用缓存就肯定存在数据在极短时间内的不一致性,如果追求极强一致性,例如下单商品的价格之类是不可以采用获取缓存的方式,所以在缓存的架构中更多是寻找一种最终一致性的解决方案,对应不同的应用场景也需要寻找不同的解决方案。
大致网上会有几种简单的方案:
1.先更新缓存,后更新数据库
2.先更新数据库,后更新缓存
3.先删除缓存,后更新数据库
4.先更新数据库,后删除缓存
5.先删除缓存,后更新数据库,再删除缓存
1.先更新缓存,后更新数据库
这种策略纯属凑数,应该没人会这么使用
2.先更新数据库,后更新缓存
这种策略存在最大的问题是多线程导致出现的脏数据
T1线程先更新了数据库 —> T2线程再更新数据库 -> T2线程更新了缓存 -> T1线程更新了缓存
此时DB中的数据是T2线程更新的数据,但缓存中确实T1线程更新的缓存,出现了数据库和缓存的不一致
另外还存在一种问题就是,如果写的几率比读的几率大,那每次写的线程,还需要额外承担输入缓存的业务,从性能上来看得不偿失。
3.先删除缓存,后更新数据库
也是在多线程中可能导致脏数据
T1写线程删除缓存 -> T2读线程获取不到缓存 -> T2读线程重新从DB构建缓存 -> T1更新DB
此时DB中的数据是T1写线程更新的数据,而缓存中的数据是T2读线程在T1提交之前构建的数据,出现数据库和缓存不一致性
解决这个问题可以让T1写进程,再更新DB后,再删除一次缓存,这个其实是双删策略,其实就是第5条
4.先更新数据库,后删除缓存
此策略是让读线程从缓存中获取,如果缓存不存在或失效,从db获取,然后再写回去缓存。
T1写线程直接更新DB,然后删除掉缓存,如果现在有三个线程 T1写 T2写 T3读
T1写写成更新DB -> T1删除缓存 -> T3读线程获取不到缓存,读取DB获取数据 -> T2写线程更新DB -> T2删除缓存 -> T3把获取到的数据写回缓存
此时DB的数据是T2更新的数据,而缓存确实T3在T2更新DB前从DB获取的数据,出现数据库和缓存不一致,不过这是一种理论的存在,这种理论在于T3很快获取DB数据,而又很慢的会写缓存,这种理论却是是存在的,不过可以说几率确实比前面2种小的多得多
如果非得解决有几种解决的思路
T2写线程更新完DB,可以稍等下去删除缓存,不过这是不可靠的
减少缓存有效期,用缓存失效来达到最终一致性,(在考虑到极低的概率出现不一致性的问题,其实这是最低成本也是最方便的解决方案,当然根据业务而定)
此处还存在另一个问题,网上有人提出,就是第一步操作成功,第二部操作失败,这个其实是分布式中的分布式事务常常出现的问题,也是根据业务而定
删除缓存可以通过 1。线程自身判断,重复尝试 2.把删除失败交付给队列,让另外一个队列机制去尽最大努力尝试 3.抛出异常流,实施报警,让人为介入。
另外还有一种是阿里的canal,是直接订阅mysql的binlog到相对的缓存或者nosql中去,并没有使用过。
陈于喆
http://www.icoco.tech/高并发下数据库和缓存一致性的保证/返回搜狐,查看更多
责任编辑: