麒麟v10 上部署 TiDB v5.1.2 生产环境优化实践
811
2023-04-03
分布式缓存数据库一致性问题
缓存由于其高并发和高性能的特征,已经在项目中被广泛应用。
使用缓存的同时,也带来了缓存和数据库一致性问题。对于缓存数据库一致性问题,有很多解决方案,没有最完美的方案,只有适合自身业务的尽可能完美的方案。
缓存读取和更新过程:
查询时一般先查询缓存,如果缓存命中的话,那么直接将数据返回。
如果缓存中没有数据(如失效,或者根本没设置数据),那么,应用程序先从数据库中查询数据,如果不为空,则将数据放在缓存中。
一致性问题出现原因:
更新操作需要更新数据库和缓存,而更新数据库和缓存不是原子性的,不能放在一个事务中操作。
一致性问题解决方案:
数据一致性问题是分布式系统中必然存在的问题,数据的一致性可以分为:
①:强一致性:时时刻刻保持一致
②:最终一致性:允许短暂的不一致,但是最后还是一致的
要实现缓存和数据库的强一致性,需要借助于负载的分布式一致性协议,倒不如不用缓存。因此缓存和数据库保证的是最终一致性,我们能做的是,在保证最终一致性的前提下,如果把数据不一致带来的影响降到最低。
既然更新操作才可能会出现缓存和数据库不一致的情况,那么更新时,怎么处理缓存和数据库呢?先更新数据库后更新缓存?先更新数据库后更新缓存?或者先淘汰缓存后更新数据库?
为什么没有先更新缓存后更新数据库?
1):如果更新数据库失败,那么就造成了数据不一致
先更新数据库后更新缓存的问题:
1):两个线程并发更新数据库再更新缓存可能出现缓存更新顺序问题(造成缓存数据不对)
2):如果更新频繁,读少的情况,那么缓存也被频繁更新,造成不必要的开销
3):如果缓存的值是需要经过一系列复杂计算的,那么每次都去更新缓存无疑是浪费性能的
先删缓存后更新数据库的问题:
1):线程A删除缓存更新完数据库前,线程B没有命中缓存,从数据库中查询到了更新前的值存入缓存中
解决方案:延时双删策略(推荐使用)
即先删除缓存,再更新数据库,休眠一段时间,再删除缓存
伪代码如下:
public void write(String key,Object data){ redis.delKey(key); db.updateData(data); Thread.sleep(1000); redis.delKey(key); }
为什要休眠1秒钟?为了将这1秒内造成的脏数据删除,可能有线程读取到了更新前的旧数据还未来得及写入缓存
休眠的时间多少如何确定?评估自身项目读数据业务逻辑的耗时,在这基础了加100ms即可。可以确保脏数据已经写入缓存中
读写分离怎么办?
也是采用延时双删策略,休眠时间确保完成主从同步
为了避免休眠造成吞吐量降低,可以将第二次删除作为异步操作
第二次删除失败怎么办?
删除在更新期间写入缓存的旧值失败
解决方案:将需要删除的key发送到消息队列,然后自己消费消息,获得需要删除的key,继续重试删除操作,直到成功。
先更新数据库后删缓存
即Cache Aside Pattern,即缓存旁路模式
失效:应用程序先从缓存中取数据,没有取到,则从数据库中取数据,成功后,放到缓存中
命中:应用程序从缓存中取到数据,然后返回
更新:先把数据存到数据库中,成功后,再删除缓存
问题:线程成功更新了数据库之后,还没来得及删除缓存,此时线程挂了,则会导致其他线程读取的都是脏数据
END.
版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。