hello,各位小伙伴们大家好,我是颜书凌,下面给大家讲解一下数据库和缓存的一致性问题,话不多说
1、一致性介绍
一致性就是数据保持一致,在分布式系统中,可以理解为多个节点中数据的值是一致的。
- 强一致性:这种一致性级别是最符合用户直觉的,它要求系统写入什么,读出来的也会是什么,用户体验好,但实现起来往往对系统的性能影响大
- 弱一致性:这种一致性级别约束了系统在写入成功后,不承诺立即可以读到写入的值,也不承诺多久之后数据能够达到一致,但会尽可能地保证到某个时间级别(比如秒级别)后,数据能够达到一致状态
- 最终一致性:最终一致性是弱一致性的一个特例,系统会保证在一定时间内,能够达到一个数据一致的状态。这里之所以将最终一致性单独提出来,是因为它是弱一致性中非常推崇的一种一致性模型,也是业界在大型分布式系统的数据一致性上比较推崇的模型。
2、问题来源
使用redis做一个缓冲操作,让请求先访问到redis,而不是直接访问MySQL等数据库:
读取缓存步骤一般没有什么问题,但是一旦涉及到数据更新:数据库和缓存更新,就容易出现缓存(Redis)和数据库(MySQL)之间的数据一致性问题。
不管是先写MySQL数据库,再删除Redis缓存;还是先删除缓存,再写数据库,都有可能出现数据不一致的情况。
3、举例说明
我这里举两个个例子:
1、先更新MySQL,再更新Redis。
如果更新Redis失败,可能仍然不一致
2、先删除Redis缓存数据,再更新MySQL。
再次查询的时候再将数据添加到缓存中,这种方案能解决1方案的问题,但是在高并发的场景下,性能较低,而且仍然会出现数据不一致的问题,比如线程1删除了Redis缓存数据,正在更新MySQL,此时另外一个查询再次请求,那么就会把MySQL中的老数据又同步到Redis中。
原因:因为读和写实并发的,没法保证顺序,就会出现缓存和数据库的数据不一致的问题
4、解决方案
a. 延迟双删
先删除Redis缓存数据,再更新MySQL,延迟几百毫秒再删除Redis缓存数据,这样就算再更新MySQL时,有其他线程读了MySQL,把老数据读到了Redis中,那么也会被删除掉,从而把数据保持一致。
出现的问题:延迟双删,所延迟的时间非常的难以确定,所以并不推荐延迟双删。即使先修改数据库,在删除缓存,还是有一定的时间会导致读取到旧数据。还有如reids删除失败等一系列问题
b.队列+重试机制
步骤:
更新数据库数据
缓存因为某种原因或者问题删除失败
将需要删除的key发送至消息队列
自己消费消息,获取需要删除的key
继续重试删除操作,直到成功
缺陷:虽然保证了数据的最终一致性,但是会对业务线的代码造成大量的侵入。
c.异步更新缓存(基于订阅binlog的同步机制)
MySQL中产生了新的写入、更新、删除等操作,就可以把binlog相关的消息推送至Redis,Redis再根据binlog中的记录,对Redis进行更新。
其实这种机制,很类似MySQL的主从备份机制,因为MySQL的主从备份也是通过binlog来实现的数据一致性
5、实际应用
使用阿里的一款开源框架canal,通过该框架可以对MySQL的binlog日志进行监控,而canal正是模仿了mysql的slave数据库的备份请求,使的Redis的数据更新达到了相同的效果,MQ的消息中间件可以采用rabbitMQ来实现推送
本篇文章到此就结束啦,希望对你有所帮助