redis-缓存和数据库的数据一致性:问题和解决方案

5,568 阅读6分钟

问题

把redis作为缓存使用已经是司空见惯,但是使用redis后也可能会碰到一系列的问题,尤其是数据量很大的时候,经典的几个问题如下:

(一)缓存和数据库间数据一致性问题

分布式环境下(单机就不用说了)非常容易出现缓存和数据库间的数据一致性问题,针对这一点的话,只能说,如果你的项目对缓存的要求是强一致性的,那么请不要使用缓存。我们只能采取合适的策略来降低缓存和数据库间数据不一致的概率,而无法保证两者间的强一致性。合适的策略包括 合适的缓存更新策略,更新数据库后要及时更新缓存、缓存失败时增加重试机制,例如MQ模式的消息队列。


总结
1.强一致性
不建议使用缓存。
如果一定要使用,那只能采取一些策略,尽量避免,但是仍然不是根本性的解决问题。因为只要是两个进程之间同步数据,肯定是有延迟的,只是延迟时间的长短、延迟数据的多少的问题。

2.弱/最终一致性
可以使用


一般情况下来说
1.高可用只确保最终一致性,不确保强一致性
2.如果要强一致性,数据读走数据库
注:当然写,也是走数据库。


解决方案
两种
1.采用延时双删策略
2.异步更新缓存(基于订阅binlog的同步机制)

juejin.cn/post/684490…

双删且延时 + 设置数据过期时间

public void write( String key, Object data )
{
	redis.delKey( key ); //第一次删除
	db.updateData( data ); //更新数据
	Thread.sleep( 500 ); //睡眠延迟
	redis.delKey( key ); //再次删除
}

具体的步骤就是:
1.先删除缓存
2.再写数据库
3.休眠500毫秒
4.再次删除缓存


1.Redis和数据库之间的数据一致性问题解决方案。
博主说了一种方法,大致是这样的:将某个库上的某个key要发生的写操作,记录在缓存中,并设置“经验主从同步时间”的缓存超时时间为500ms,这时间是指数据库主备同步的时间不会超过500ms,但是也有可能发生超过500ms的现象,当然可以设置的更久一点,只会偶尔发生最终一致性问题,大多数时候可以保证强一致性。由于一般数据库是读写分离的,写的时候将写的操作在数据库中执行并存入缓存设置超时时间500ms,当读的时候,先查缓存Redis,如果有相关记录则从Redis中返回,如果没有则说明已经同步到负责读的数据库中了,可以直接从读数据库中读取数据,这样也能做到读写数据库的分离。//妈的,不知道这傻叉说啥 有关数据一致性问题可以参考《浅析数据一致性》。

作者:朱小厮 来源:CSDN 原文:blog.csdn.net/u013256816/… 版权声明:本文为博主原创文章,转载请附上博文链接!

读取binlog后分析 ,利用消息队列, 推送更新各台的redis缓存数据

1.mysql数据库 本身的主从复制/同步,也是基于binlog日志来做的,从而确保数据一致性

2.缓存与mysql的数据一致性 也可以基于这个思路做。具体实现如下: 1)写一个程序专门读取和分析log,得到写入数据 2)把写入数据写入消息中间件 3)写一个程序订阅消息中间件,得到最新数据 4)最后,更新各台机器的redis


这种方案有现成的开源中间件 备注说明:上述的订阅binlog程序在mysql中有现成的中间件叫canal,可以完成订阅binlog日志的功能。至于oracle中,博主目前不知道有没有现成中间件可以使用。另外,重试机制,博主是采用的是消息队列的方式。如果对一致性要求不是很高,直接在程序中另起一个线程,每隔一段时间去重试即可,这些大家可以灵活自由发挥,只是提供一个思路。


作者:彼得潘大叔 链接:www.zhihu.com/question/36… 来源:知乎 著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

说个大概吧,我们热数据基本都是redis,增删改都是操作mysql,对于读是保存到redis,这样就涉及到数据同步操作,同步操作分为两大块,我们的叫法是,一个是全量(将全部数据一次写入到redis,时间几小时不等),一个是增量(实时更新)。这里说的是增量,主要问题是即时性,因为增删改都是直接操作mysql变更都在MySQL(这里高并发的问题是用分库分表加外层的负载均衡) 所以我们的方向是读取binlog然后分析 ,利用消息推送到某服务器A,再进行分析,然后更新各台redis,消息推送工具用的是rabbitMQ,可设定某表的变更推送(分三类update insert delate 包含变更前后的数据),这里有个问题是:mysql数据操作太频繁产生的推送可能会很多,所以分析处理脚本处理速度一定要跟得上(我用Python写,前期多线程(坑),后来改成多进程),还有一个问题是,对于mysql-redis的数据关系映射设定不要太复杂,一表对一表就行,数据组合交给业务层做,这样分析处理脚本不会太多负担,处理速度更快,而且操作redis也更简单,redis每个对应mysql数据表的可使用多端口多实例,redis是单线程而且这样对于redis的主从和负载均衡有利,

题外话:对于服务器A 可以再给其它服务做一个数据表增量变更数据获取接口,利用数据纬度,获取时间段的变更数据。

追加,对于订单类部分,都是完全使用mysql,这个做好数据服务器,DB,table,分区,的拆分就好了,看并发请求越多拆分越多。

上面说太多都是屁话,其实就是MySQL binlog增量订阅消费+消息队列+处理并把数据更新到redis

一个简单的例子。 github.com/liukelin/ca…


参考 juejin.cn/post/684490…

缓存双写?

参考

juejin.cn/post/684490… //写的很好。就是常见的两种解决方案 coolshell.cn/articles/17… //耗子哥写的,说常见的解决方案有点问题 segmentfault.com/a/119000001… //文章很长

www.cnblogs.com/rjzheng/p/9… //孤独烟 烟哥写的

mp.weixin.qq.com/s?__biz=MjM…

mp.weixin.qq.com/s?__biz=MjM…

cloud.tencent.com/developer/n…

www.javazhiyin.com/22969.html