MySQL是如何保证数据一致性的?

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

前言

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

MySQL保证的一致性

在这之前先划清一下界限,看一下MySQL保证的是哪里的一致性。

拿一个最简单的转账例子,用户A向用户B转1000元,正常的sql是这样的

update account set balance=balance-4000 where user='A' and balance >= 4000;
update account set balance=balance+4000 where user='B';

示例表数据如下
MySQL是如何保证数据一致性的?,大白话聊MySQL,mysql,数据库
如果最终用户A账户没有扣4000,而用户B账户多了4000,总金额也无缘无故的多了4000。这个时候就造成了数据不一致了。出现这个问题可能存在几个原因:

  1. 在MySQL客户端执行sql时没有做校验。如果用户A余额并不足以4000,由于没有校验,两条sql都会成功执行,用户B就会凭空多出4000。
  2. 两条sql并不在同一事务中。可能sql1执行失败了,sql2执行成功,由于不再一个事务中导致用户B多了4000。
  3. 在MySQL内部执行时因为某些故障而出现了不一致情况。

很显然,第三点是需要MySQL解决处理的。而第一点是属于MySQL客户端的逻辑BUG,第二点会存在客户端在使用事务时不遵循规则的情况,都属于外部因素,MySQL不可控

所以,MySQL保证的一致性是:在一个事务中的DML(增删改)操作。尽管DML本身可能存在问题。

MySQL发生不一致环节

划清界限后再分析一下在DML执行过程中,哪个环节会发生数据不一致。 以上面的sql为例,假设已经进行过校验且在同一事务。

并发冲突

在执行第一条sql时,「执行器」会通过条件user='A' and balance >= 4000在「存储引擎」获取到符合条件的记录,然后进行balance扣减操作。(不知道这个流程的可以看下前面的文章)

如果这个时候存在并发现象,扣减操作可能会执行多次,这个balance肯定就不是预想中的结果了,也就发生数据不一致了。如下图

MySQL是如何保证数据一致性的?,大白话聊MySQL,mysql,数据库

当3个update请求同一时间调用存储引擎对同一数据页更新后,正常情况下,balance值应该为0。但是因为并发操作,balance的值可能会被修改为-1000或者-2000等其他值,这样的bug显然是不可被接受的。

有并发经验的应该都知道需要通过锁资源可以避免这个情况,InnoDB也是通过加来处理的。

redolog不完整

通过上文可以知道,InnoDB是通过「双写缓冲」、「Redo Log」等机制保证数据不丢失的。

这种情况下,假设第一条sql执行成功并且对应的redo log已经被刷新到磁盘中,但是第二条sql执行失败或者MySQL服务宕机导致其redolog未刷新到磁盘,那么在下次启动恢复时,就会发生数据不一致了。如下图

MySQL是如何保证数据一致性的?,大白话聊MySQL,mysql,数据库

sql示例的第一条执行结果通过redolog恢复了,但是第二条的redolog随着宕机丢失了,于是乎造成了数据的不一致。(redo log的刷盘机制和构建脏页可以通过上文进行了解。)

对于这种情况,InnoDB是通过上文提到的「Undo Log」来解决的。

binlog&redolog不一致

我们知道,binlog中记录了所有对数据更新的原始sql,以便数据备份恢复、主从复制。与redolog不一样的是binlog属于MySQL server层,而redolog是InnoDB的机制,用于故障恢复,两者并不冲突,这里不过多赘述。

虽然不冲突,但是要保证两者在事务提交后都可以持久化到磁盘,不然就会在主从复制的时候出现数据不一致现象,如下图

MySQL是如何保证数据一致性的?,大白话聊MySQL,mysql,数据库

只要binlog和redolog有一方没有同步持久到磁盘都会发生类似现象。针对这种情况MySQL是通过两阶段提交解决的。

以上就是DML在执行过程中可能出现不一致的环节(没有想到的欢迎评论交流)。接下来具体看一下InnoDB针对以上几种情况是如何处理解决,从而保证数据一致性的。

MySQL解决不一致方案

加锁解决并发冲突

锁没有什么好说的,innoDB根据隔离级别决定是否用锁(当然,还有server层的表锁什么的这里不展开)。这里就演示下在隔离级别REPEATABLE-READ下,锁在SQL执行中的具体作用和效果。

当在第一个事务中执行 update account set balance=balance-4000 where user='A' and balance >= 4000; 时,其他事务不能对user为’A’的记录进行更新。如下图,当第二个事务窗口执行 update account set balance=balance-1000 where user='A' and balance >= 1000; 时会被阻塞住,直到第一个事务提交或者超时。

MySQL是如何保证数据一致性的?,大白话聊MySQL,mysql,数据库

这个时候可以通过 select * from sys.innodb_lock_waits ; 查看一下锁的相关信息

MySQL是如何保证数据一致性的?,大白话聊MySQL,mysql,数据库

这里的locked_type是RECORD,也就是行记录锁,还有一个是间隙锁。

间隙锁的作用是保证某个范围内的数据在锁定情况下不会发生任何变化。比如,当第一个事务执行update account set balance=balance-100 where id between 7 and 9;后,第二个事务在执行INSERT INTO account (id, user,balance) VALUES (8, 'ABD',5000);时会阻塞,但是执行 INSERT INTO account (id, user,balance) VALUES (16, 'ABDD',5000);会成功执行,因为插入id为16的行数据不会影响到7~9之间的数据。这个时候去查看select * from sys.innodb_lock_waits;时会发现waiting_lock_mode值为 X,GAP(间隙)

MySQL是如何保证数据一致性的?,大白话聊MySQL,mysql,数据库

所以说,锁避免了事务的并发访问导致的数据不一致。

undolog解决redolog不完整

InnoDB在因sql执行失败或者MySQL服务宕机导致redolog不完整从而出现数据不一致是这么解决的:

  1. 在更新数据页之前,InnoDB会先将数据当前的状态记录在「Undo Log」中。
  2. 之后,再将更新后的相关数据记录到「Redo Log」中。

这样的话,不论出现哪种情况都可以通过undo log将数据回滚并保持一致,这个就是经常提到的原子性以及「回滚」操作。

就如上图(redo log不完整环节),加上Undo log之后数据状态如下图

MySQL是如何保证数据一致性的?,大白话聊MySQL,mysql,数据库

图中加了行记录的隐藏字段事务ID和回滚指针以及undo log页和undo的redo。

undo log 记录的就是user='A’和‘B’事务提交前的数据,各为4000。

redo log 中会记录所有的更新操作,包括undo,因为undo记录的也是更新语句。需要说一下,这里记录的undo是演示使用,对于一条update操作,真正的undo会记录一条delete和一条insert操作,原因上文有介绍。

为什么redo log会记录undo

undo log是以页为单位,跟随页的刷新机制,会存在丢失的情况,所以在记录undo后也会将该undo记录到redo,避免undo丢失,一旦undo丢失就回滚不了了。

有了undo log后,假设第二条sql执行失败,这个时候就会通过行记录中的事务ID(txidx)和回滚指针(roll_pointx、roll_pointx1)去undolog中找对应的回滚操作(如图中的 ‘**回滚指针’**箭头),最终将事务回滚保证原子性和一致性。

针对上图的状态,如果发生宕机,那么在重新MySQL服务时,会有两个操作:

  1. 会先通过redo log构建「脏页」。
  2. 根据redo log中记录的事务提交状态来决定是否回滚。

如图

MySQL是如何保证数据一致性的?,大白话聊MySQL,mysql,数据库

当前user='A’的事务状态为prepare,所以需要进行回滚操作。回滚流程是这样的:

  1. 根据数据中该记录的事务ID(txidx)在undolog中找对应的回滚操作。
  2. 发现事务ID有两个undo操作,user='A’和‘B’的。
  3. 执行undo操作,将数据页中的记录回滚至事务提交前状态。

最终的结果就是user='A’和‘B’的balance会回滚到4000。

所以说,undo避免了事务或者宕机的异常导致的数据不一致。

XA两阶段提交解决binlog和redolog的不一致

redo log中的事务状态不仅在这里起到作用,在binlog和redolog的一致上,同样是通过这个状态来判断并且决定是否需要回滚。

这个就不得不说到MySQL的XA两阶段提交协议了,在这之前,我一直以为XA是运用到MySQL与外部应用的,没想到是应用在MySQL内部的。不过分布式事务嘛,原理基本上都一样,想要深入了解的可以看《分布式事务及解决方案》,这里就不过多赘述了。

XA的两阶段分别是prepare和commit,在事务提交前,redolog中记录的状态都是prepare,当事务提交后,该状态就会被更新为commit,同时将XID写入到对应的binlog中并刷新到磁盘。如下图

MySQL是如何保证数据一致性的?,大白话聊MySQL,mysql,数据库

这样的话,如果发生宕机,下次启动时可以根据redolog中的状态以及XID去binlog中查找,如果存在意味着两者一致,不存在就进行回滚操作。

所以说,XA两阶段提交保证了binlog和redolog逻辑一致,从而避免主从节点的数据不一致。

总结

MySQL一致性的保证基本上涉及到InnoDB存储引擎的各个组件,「Buffer Pool」、「Log Buffer」、「Redo Log」、「Undo Log」等,还有DML操作的流程、锁、故障恢复等功能。最后再总结下MySQL是如何保证一致性的。文章来源地址https://www.toymoban.com/news/detail-777835.html

  1. 对于并发操作带来的数据不一致性问题,InnoDB通过锁来解决。
  2. 对于可能会发生的redolog不完整的情况,InnoDB通过Undo Log来解决。
  3. 对于redolog&binlog不一致带来的主从节点数据不一致,MySQL是通过XA两阶段提交来解决。

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

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

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

相关文章

  • MySQL 和 Redis 如何保证数据一致性,通过MySQL的binlog实现

    1、简介         MySQL 和 Redis 如何保证数据一致性,目前大多讨论的是先更新Redis后更新MySQL,还是先更新MySQL 后更新Redis,这两种方式在实际的应用场景中都不能确保数据的完全一致性,在某些情况下会出现问题,本文介绍使用 Canal 工具,通过将自己伪装成MySQL的从节点,读

    2024年02月02日
    浏览(42)
  • [Etcd]分布式系统中如何使用乐观锁保证Mysql和Etcd数据最终一致性

    在写业务代码时,很多时候需要保证数据存储在不同中间件中的一致性。以笔者为例,就遇到了需要将mysql中已存储的数据转存到etcd中,同时还要考虑到并发场景下如何保证数据最终一致性的问题。 该问题形象地表示的话,可以将时间线展开如下 服务A1更新db数据为 {\\\"key1\\\":

    2024年02月02日
    浏览(35)
  • Redis与MySQL双写一致性如何保证

    前言 在分布式系统中,数据一致性是一个重要的问题。当我们使用Redis和MySQL这两种不同的数据库时,如何保证它们之间的双写一致性是一个需要解决的难题。本文将探讨Redis与MySQL双写一致性的保证方法。 什么是双写一致性? 指的是当我们更新了数据库的数据之后redis中的数

    2024年02月09日
    浏览(31)
  • 从kafka如何保证数据一致性看通常数据一致性设计

    在数据库系统中有个概念叫事务,事务的作用是为了保证数据的一致性,意思是要么数据成功,要么数据失败,不存在数据操作了一半的情况,这就是数据的一致性。在很多系统或者组件中,很多场景都需要保证数据的一致性,有的是高度的一致性。特别是在交易系统等这样

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

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

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

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

    2024年01月23日
    浏览(45)
  • 如何保证缓存和数据库的数据一致性

    若数据库更新成功,删除缓存操作失败,则此后读到的都是缓存中过期的数据,造成不一致问题。 同删除缓存策略一样,若数据库更新成功缓存更新失败则会造成数据不一致问题。 若缓存更新成功数据库更新失败, 则此后读到的都是未持久化的数据。因为缓存中的数据是易

    2023年04月19日
    浏览(39)
  • 如何保证ES和数据库的数据一致性?

    在业务中,我们通常需要把数据库中的数据变更同步到ES中,那么如何保证数据库和ES的一致性呢?通常有以下几种做法: 双写 在代码中,对数据库和ES进行双写,并且先操作本地数据库,后操作ES,而且还需要把两个操作放到一个事务中:  在以上逻辑中,如果写数据库成功

    2024年04月28日
    浏览(30)
  • Redis---数据库和缓存如何保证一致性?

    用「读 + 写」请求的并发的场景来分析: 假如某个用户数据在缓存中不存在,请求 A 读取数据时从数据库中查询到年龄为 20,在未写入缓存中时另一个请求 B 更新数据。它更新数据库中的年龄为 21,并且清空缓存。这时请求 A 把从数据库中读到的年龄为 20 的数据写入到缓存

    2024年01月24日
    浏览(39)
  • Redis如何保证缓存和数据库一致性?

    现在我们在面向增删改查开发时,数据库数据量大时或者对响应要求较快,我们就需要用到Redis来拿取数据。 Redis:是一种高性能的内存数据库,它将数据以键值对的形式存储在内存中,具有读写速度快、支持多种数据类型、原子性操作、丰富的特性等优势。 优势: 性能极高

    2024年01月16日
    浏览(51)

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

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

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

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

二维码1

领取红包

二维码2

领红包