今天面试被问到Redis和MySQL如何保证数据一致性的问题

您好,Redis和MySQL之间的数据一致性问题确实很烦人。
我在之前的项目中也遇到过陷阱。
我提到的解决方案非常全面。
我给你说几点我的理解:
---
上周有客户问我,他们的系统使用Redis做缓存,MySQL做主数据存储。
结果,用户更改了订单信息,页面仍然显示旧信息。
当时我就知道数据同步有问题。

我自己陷入的陷阱是在使用“先刷新数据库然后刷新缓存”时。
有一次系统崩溃,数据库更新成功,但缓存没有及时删除。
系统出现后,用户又做了一次改变。
此时,缓存仍然是旧的,数据库中包含新值,这立即引起混乱。

关于您提到的解决方案:
1 先刷新数据库,再刷新缓存:
前提是Redis更新必须极快,或者失败重试机制必须能够容忍。
我们之前也尝试过这样的做法,发现更新Redis的延迟有时会比数据库的延迟还要大。
尤其是在写入多个场景时,Redis队列会停止。

高并发下确实很容易出现脏读。
一次测试居然读取了其他用户的旧数据,这让我很震惊。

2 先删除缓存,再更新数据库:
这是最常见的技巧,许多电子商务系统都使用它。
但正如我所说,高并发下很容易出现问题。
例如,删除缓存后,用户立即检查发现没有数据,所以他首先添加了一个默认值。
结果数据库还没有更新,就变得乱七八糟了。

我们曾经使用过这个解决方案,但在进行更改后我们没有测试并发性。
结果双十一期间发生了重大事故,整个网站的数据全部过时了。

3 最终的一致性解决方案:
消息队列(RocketMQ等):这种方式确实稳定,但成本较高。
我们引入RocketMQ后,时不时会出现消息积压的情况,调试期很糟糕。
而且,消息延迟有时会需要几秒钟,这对于需要立即反馈的企业来说并不方便。

Channel组件:我们经常使用它,它的实时性能非常好。
但监控通道日志却是一件令人头疼的事情。
上次发生错误导致通道停止。
Redis 花了很长时间才更新数据库才收到消息。
我曾被用户投诉斥责过。

我目前的观点是:

不要迷恋简单的场景。
先刷新数据库然后删除缓存就可以了,不过要加上重试和超时。
最近测试发现Redis键删除超时实际上需要两分钟,这是不寻常的。

该通道用于核心业务,实时性较差,但至少稳定。
我们的价格变动活动就是基于此。
更改价格后,用户可以在5 秒内刷新并看到新的价格。

高并发场景下添加互斥锁,速度较慢但相当安全。
有一次我在做一个活动,我立马就锁了。
虽然她被困住了,但没有任何错误。
现在想想,还是很贵,直接烧服务器了。

最后我想提一个建议。
不要只看规划,要看实际环境。
我们之前已经测试过。
也是1 0000QPS。
使用Message Queue,CPU 上升到2 00%,但是使用Canal,还好。
所以:

如果有足够的内存,使用Canal而不是改变消息队列。

只要公司能够容忍延迟,它就实现了最终一致性,而不是实时。

加钱来获得一个好的服务器,但无论如何你最终都会烧钱。

无论如何,你可以找到答案。
我仍然想知道是否可以使用链锁来改善价格变化活动......

Redis和MySQL如何保持数据一致性?

这是一个危险:Redis和MySQL直接进行事务同步会造成性能瓶颈。

不要相信它:不要相信同一个事务,以保证Redis和MySQL的一致性。

不要:使用中间件或应用层逻辑来处理数据一致性,而不是仅仅依赖数据库事务。

Redis 和 MySQL 数据一致性详解

老实说,我在理解Redis和MySQL之间的数据一致性时遇到了很多陷阱。
在高并发场景下,直接攻击MySQL实际上会出现问题。
不但响应慢,还容易崩溃。
目前,使用Redis进行缓存是一种标准操作,但缓存和数据库是两个不同的东西。
同步数据成为一项技术任务。

首先我们来说说为什么会出现不一致的情况。
事实上,有两种典型场景。
第一种是“先清缓存,再写入数据库”。
想一想:你刚刚删除了Redis中的数据,还没来得及将新数据写入MySQL,另一个进程检查缓存,发现缓存是空的。
它直接进入数据库检索旧数据并将其填回到缓存中。
当您最终完成写入新数据时,您意识到旧值仍在缓存中并且出现了问题。
我以前在做电子商务系统的时候就遇到过这样的事情。
用户刚刚下单,页面仍然显示库存为1 00,过了几秒才更新到0,用户表示太神奇了。

第二个选项是:“先写入数据库,然后清除缓存”。
数据已写入MySQL,但在清除缓存步骤期间线程突然卡住,缓存保留了旧数据。
这种情况经常发生。
在我的一个项目中,我突然在半夜发现数据不一致。
经过一番排查,发现是清除缓存时出现异常。
在这两种情况下,它要么读取旧的缓存,要么读取空的缓存,这是行不通的。

就解决方案而言,有两个主要想法。

第一个是延迟双重删除。
过程相当有趣:先清除缓存,写入数据库,然后让线程休眠几百毫秒(具体时间取决于系统负载),然后再次清除缓存。
为什么要午睡?由于写入数据库和清除缓存之间可能存在时间延迟,因此休眠较长时间可能会删除潜在的错误数据。
不过需要注意的是,这需要设置合理的缓存过期时间。
只要不超时,最终的数据就会被核对。
我尝试了这种方法,在低并发时效果很好,但在高并发时仍然会出现不一致的情况。
我当时不明白为什么。
也许是因为写入数据库太慢,短暂的睡眠还不够?
第二个是基于Binlog的异步更新。
这个想法要先进得多。
您启用 MySQL 的 binlog,然后拥有一个订阅 binlog 并在收到更新后将更新写入 Redis 的服务。
这相当于给数据库添加了一个实时推送。
我见过一个使用这种方法的实时推荐系统的案例。
MySQL更新用户行为数据后,几毫秒内就会同步缓存,效果确实不错。
然而,这需要额外的消息队列系统,虽然比延迟双重删除更昂贵,但也明显更可靠。

这两种选择都有自己的权衡。
惰性双删除很容易实现,不需要额外的系统,但可能无法承受极高的并发。
异步binlog更新可以保证最终一致性,适合实时性要求较高的场景,但架构复杂。
我后来接的项目,我选择了Binlog的方案。
虽然初期投入较高,但长远来看就省心多了。
但请注意,消息积压或延迟也可能导致 Binlog 订阅区出现问题。
我没有亲自管理这个领域。
我记得数据有几百毫秒长,但是我建议你检查一下具体的性能。