1、引言
数据库压力过大时,会对数据库做读写分离,一个主库多个从库,
当读压力过大时,又可以将要读的内容放在缓存中,比如放到redis中,mysql是将数据存在磁盘中,redis是存在内存里,这一点上redis读取数据要比mysql快几个数量级,
同时内存也比磁盘贵几个数量级。。。
那么,如何要保证redis和mysql中的数据一致,便是本文要讲的内容。
2、读数据
这个的流程没有多大讨论性,根据有限的缓存容量缓存最热的数据,经常读的数据被写入到缓存中,冷门的数据也会从缓存中淘汰(详见LRU等缓存淘汰算法)
一般采用这么个逻辑:
判断数据是否在缓存,若在:直接读取;若不在:从mysql中读取数据,再将数据写入redis。
3、写数据
(1)先删redis中的数据,再更新mysql中的数据
如果在删缓存数据之后,有线程读到了未更新的数据(可能是并发问题、主从延迟等),然后将未更新的数据写入到了缓存中,产生了严重的数据不一致。
因为写操作慢于读操作,发生概率较大。
(2)先更新mysql中的数据,再删redis中的数据
问题1:这种数据也有(1)方案中的一个相似问题,如下图:
但分析一下发现这个概率是极低的,1、需要是缓存中没有读到数据的情况,2、读写线程撞在了一起,理论上读数据要比写数据快得多,写入缓存就更快了,发生上图中情况的概率很低。
对于这个低概率问题的还是有点担心的,可以考虑适当延长数据库更新操作和删除缓存操作的间隔时间,比如将删除缓存操作放入到异步队列中执行。
问题2:删除缓存可能失败
可以在异步队列中增加一个重试机制
4、异步队列的思考
异步队列的增加无疑会增加系统的复杂性,且会增加维护难度。
在生产中,一般由后台管理服务进行修改操作,前端服务来读取数据展示给用户看,在大公司中两个服务还会由不同的团队来维护。
做一个更新操作后,,可能该数据涉及到的比如商品页面缓存要删,广告页面缓存要删等等,,,维护更加复杂了。。。。
不如引入消息队列中间件,后台管理服务只充当生产者,将删除操作放入消息队列就啥都不用管了,其它服务来充当消费者。(解耦)