谈了千百遍的数据一致性

这篇具有很好参考价值的文章主要介绍了谈了千百遍的数据一致性。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

今天来说一个老生常谈的问题,来看一个实际案例:

现有业务中往往都会通过缓存来提高查询效率,降低数据库的压力,尤其是在分布式高并发场景下,大量的请求直接访问Mysql很容易造成性能问题。

有一天老板找到了你......

老板:听说你会缓存?

你:来看我操作。

你设计了一个最常见的缓存方案,基于这种方案,开始对用户积分功能进行优化,但当你睡的正酣时,系统悄悄进行了下面操作:

1、线程A根据业务会把用户id为1的积分更新成100

2、 线程B根据业务会把用户id为1的积分更新成200

3、在数据库层面,由于数据库用锁来保证了ACID,线程A和线程B不存在并发情况,,无论数据库中最终的值是100还是200,我们都假设正确

4、假设线程B在A之后更新数据库,则数据库中的值为200

5、线程A和线程B在回写缓存过程中,很可能会发生线程A在线程B之后操作缓存的情况(因为网络调用存在不确定性),这个时候缓存内的值会被更新成100,发生了缓存和数据库不一致的情况。

第二天早上你收到了用户投诉,怎么办?人工修改积分值还是删库跑路?

凡是处于不同物理位置的两个操作,如果操作的是相同数据,都会遇到一致性问题,这是分布式系统不可避免的一个痛点。

1 什么是数据一致性?

数据一致性通常讲的主要是数据存储系统,主从mysql、分布式存储系统等,如何保证数据一致性,

比如说主从一致性,副本一致性,保证不同的时间或者相同的请求访问这种主从数据库时访问的数据是一致性的,不会这次访问是结果A下次是结果B。

2 CAP定理

说到数据一致性,就必须说CAP定理。

CAP定理是2000年由Brewer提出的,他认为分布式系统在设计和部署时,面临3个核心问题:

Consistency:一致性。数据库ACID操作是在一个事务中对数据加以约束,使得执行后仍处于一致状态,而分布式系统在进行更新操作时所有的用户都应该读到最新值。

Availability:可用性。每一个操作总是能够在一定时间内返回结果。结果可以是成功或失败,一定时间是给定的时间。

Partition Tolerance:分区容忍性。考虑系统效能和可伸缩性,是否可进行数据分区。

CAP定理认为,一个提供数据服务的存储系统无法同时满足数据一致性、数据可用性、分区容忍性。

为什么?如果采用分区,分布式节点之间就需要进行通信,涉及到通信,就会存在某一时刻这一节点只完成一部分业务操作,在通信完成的这一段时间内,数据就是不一致的。如果要保证一致性,就要 在通信完成的这段时间内保护数据,使得对访问这些数据的操作都不可用。

反过来思考,如果想保证一致性和可用性,那么数据就不能够分区。一个简单的理解就是所有的数据就必须存放在一个数据库里面,不能进行数据库拆分。这个对于大数据量、高并发的互联网应用来说,是不可接受的。

3 数据一致性模型

基于CAP定理,一些分布式系统通过复制数据来提高系统的可靠性和容错性,也就是将数据的不同副本存放在不同的机器。常用的一致性模型有:

强一致性: 数据更新完成后,任何后续访问将会返回最新的数据。这在分布式网络环境几乎不可能实现。

弱一致性:系统不保证数据更新后的访问会得到最新的数据。客户端获取最新的数据之前需要满足一些特殊条件。

最终一致性:是弱一致性的一种特例,保证用户最终能够读取到某操作对系统特定数据的更新。

4 如何保证数据一致性?

针对刚开始的问题,如果加以思考,你可能会发现不管是先写MySQL数据库,再删除Redis缓存;还是先删除缓存,再写库,都有可能出现数据不一致的情况。

(1)先删除缓存

1、如果先删除Redis缓存数据,然而还没有来得及写入MySQL,另一个线程就来读取;

2、这个时候发现缓存为空,则去Mysql数据库中读取旧数据写入缓存,此时缓存中为脏数据;

3、然后数据库更新后发现Redis和Mysql出现了数据不一致的问题。

(2)后删除缓存

1、如果先写了库,然后再删除缓存,不幸的写库的线程挂了,导致了缓存没有删除;

2、这个时候就会直接读取旧缓存,最终也导致了数据不一致情况;

3、因为写和读是并发的,没法保证顺序,就会出现缓存和数据库的数据不一致的问题。

解决方案1:分布式锁

在平时开发中,利用分布式锁可能算是比较常见的解决方案了。利用分布式锁把缓存操作和数据库操作封装为逻辑上的一个操作可以保证数据的一致性,具体流程为:

1、每个想要操作缓存和数据库的线程都必须先申请分布式锁;

2、如果成功获得锁,则进行数据库和缓存操作,操作完毕释放锁;

3、如果没有获得锁,根据不同业务可以选择阻塞等待或者轮训,或者直接返回的策略。

流程见下图:

利用分布式锁是解决分布式事务的一种方案,但是在一定程度上会降低系统的性能,而且分布式锁的设计要考虑到down机和死锁的意外情况。

解决方案2:延迟双删

在写库前后都进行redis.del(key)操作,并且设定合理的超时时间。

伪代码如下:

public void write( String key, Object data ){
  redis.delKey( key );
  db.updateData( data );
  Thread.sleep( 500 );
  redis.delKey( key );
}



具体步骤:

1、先删除缓存

2、再写数据库

3、休眠500毫秒(这个根据读取的业务时间来定)

4、再次删除缓存

来看之前的案例在这种方案下的情景:

T1线程线删除缓存再更新db , T1线程更新db完成之前T2线程如果读取到db旧的数据, 会再把旧的数据写入Redis缓存。

此时T1线程延迟一段时间后再删除Redis缓存操作. 当其他线程再读取缓存为null时会查询db最新数据重新进行缓存, 保证了Mysql和Redis缓存的数据一致性。

在此基础上,缓存也要设置过期时间,来保证最终数据的一致性。 只要缓存过期,就去读数据库然后重新缓存。

这种双删+缓存超时的策略,最差的情况是在缓存过期时间内发生数据存在不一致,而且写的时候增加了耗时。

但是这种方案还会出现一个问题,如何保证写入库后,再次删除缓存成功?

如果删除失败,还有可能出现数据不一致的情况。这时候需要提供一个重试方案。

解决方案3:异步更新缓存(基于Mysql binlog的同步机制)

1、涉及到更新的数据操作,利用Mysql binlog 进行增量订阅消费;

2、将消息发送到消息队列;

3、通过消息队列消费将增量数据更新到Redis上。

这样的效果是:

读取Redis缓存:热数据都在Redis上;

写Mysql:增删改都是在Mysql进行操作;

更新Redis数据:Mysql的数据操作都记录到binlog,通过消息队列及时更新到Redis上。

这样一旦MySQL中产生了新的写入、更新、删除等操作,就可以把binlog相关的消息推送至Redis,Redis再根据binlog中的记录,对Redis进行更新。

其实这种机制,很类似MySQL的主从备份机制,因为MySQL的主备也是通过binlog来实现的数据一致性。

方案2中的重试方案就可以借助方案3,启动一个订阅程序订阅数据库的binlog,提取所需要的数据和key,另起代码获取这些信息。如果尝试删除缓存失败,就发送消息给消息队列,重新从消息队列获取数据,重试删除操作。

参考文档:

  • https://mp.weixin.qq.com/s/k38MZRAGmZ8EhDB5DcVhhQ
  • https://baijiahao.baidu.com/s?id=1678826754388688520&wfr=spider&for=pc
  • https://www.php.cn/faq/415782.html
  • https://blog.csdn.net/u013256816/article/details/50698167
  • https://blog.csdn.net/My_Best_bala/article/details/121977033?spm=1001.2101.3001.6661.1&utm_medium=distribute.pc_relevant_t0.none-task-blog-2defaultCTRLISTRate-1.pc_relevant_antiscan&depth_1-utm_source=distribute.pc_relevant_t0.none-task-blog-2defaultCTRLISTRate-1.pc_relevant_antiscan&utm_relevant_index=1

感谢阅读~

作者:京东零售 李泽阳

来源:京东云开发者社区 转载请注明来源文章来源地址https://www.toymoban.com/news/detail-681352.html

到了这里,关于谈了千百遍的数据一致性的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • Mongo数据一致性浅析

    根据 CAP 理论的一致性(Consistency)问题, 即在读写发生在不同节点的情况下,怎么 保证每次读取都能获取到最新写入的数据。这个一致性即是我们今天要讨论的MongoDB 可调一致性模型中的一致性 ,区别于单机数据库系统中经常提到的 ACID 理论中的一致性。 可调性具体指的

    2024年02月09日
    浏览(50)
  • 深入理解高并发下的MySQL与Redis缓存一致性问题(增删改查数据缓存的一致性、Canal、分布式系统CAP定理、BASE理论、强、弱一致性、顺序、线性、因果、最终一致性)

    一些小型项目,或极少有并发的项目,这些策略在无并发情况下,不会有什么问题。 读数据策略:有缓存则读缓存,然后接口返回。没有缓存,查询出数据,载入缓存,然后接口返回。 写数据策略:数据发生了变动,先删除缓存,再更新数据,等下次读取的时候载入缓存,

    2024年03月20日
    浏览(40)
  • Redis 原理缓存过期、一致性hash、雪崩、穿透、并发、布隆、缓存更新策略、缓存数据库一致性

    redis的过期策略可以通过配置文件进行配置 redis会把设置了过期时间的key放在单独的字典中,定时遍历来删除到期的key。 1).每100ms从过期字典中 随机挑选20个,把其中过期的key删除; 2).如果过期的key占比超过1/4,重复步骤1 为了保证不会循环过度,导致卡顿,扫描时间上限

    2024年02月08日
    浏览(43)
  • 通过kafka学习数据一致性

    数据从主节点(leader)复制到从节点(follower)的过程中,由于网络延迟、节点故障或其他原因 可能导致从节点未能及时获取或处理主节点的数据变更,从而产生数据不一致 消息提交涉及多个阶段,包括生产者发送消息、消息被写入日志、消息被复制到从节点等。 如果在这

    2024年02月19日
    浏览(29)
  • 缓存和数据库一致性

    项目的难点是如何保证缓存和数据库的一致性。无论我们是先更新数据库,后更新缓存还是先更新数据库,然后删除缓存,在并发场景之下,仍然会存在数据不一致的情况(也存在删除失败的情况,删除失败可以使用异步重试解决)。有一种解决方法是延迟双删的策略,先删

    2024年01月17日
    浏览(33)
  • MySQL是如何保证数据一致性的?

    通过上文《MySQL是如何保证数据不丢失的?》可以了解DML的操作流程以及数据的持久化机制。对于一个数据库而言,除了数据的持久性、不丢失之外,一致性也是非常重要的,不然这个数据是没有任何意义的。在使用MySQL时,数据不一致的情况也可能出现,所以,本文就来看看

    2024年02月03日
    浏览(45)
  • 使用双异步后,如何保证数据一致性?

    大家好,我是哪吒。 在上一篇文章中,我们 通过双异步的方式导入了10万行的Excel ,有个小伙伴在评论区问我, 如何保证插入后数据的一致性呢? 很简单,通过对比Excel文件行数和入库数量是否相等即可。 那么,如何获取异步线程的返回值呢? 我们可以通过给异步方法添加

    2024年01月23日
    浏览(49)
  • 一致性哈希(哈希环)解决数据分布问题

    哈希算法是程序开发过程中最广泛接触到的的算法之一,典型的应用有安全加密、数据校验、唯一标识、散列函数、负载均衡、数据分片、分布式存储。前些天遇到用一致性哈希(哈希环)的场景,不过我细想一下,对这个知识点好像了解过,但是又没太深印象,说不出具体

    2024年02月04日
    浏览(41)
  • ZooKeeper是如何保证数据一致性的?

             目录 一、分布式一致性原理 二、ZooKeeper架构         2.1 ZAB 协议操作顺序性         2.2 领导者选举         成员身份         成员状态         领导者选举 三、总结         在分布式系统里的多台服务器要对数据状态达成一致,其实是一件很有难度和挑

    2024年04月11日
    浏览(36)
  • 缓存和数据库一致性问题分析

    目录 1、数据不一致的原因 1.1 并发操作 1.2 非原子操作 1.3 数据库主从同步延迟 2、数据不一致的解决方案 2.1 并发操作 2.2 非原子操作 2.3 主从同步延迟 2.4 最终方案 3、不同场景下的特殊考虑 3.1 读多写少的场景 3.2 读少写多的场景 导致缓存和数据库数据不一致的原因有三个

    2024年02月14日
    浏览(33)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包