MySQL数据库:事务和ACID实现原理

MySQL数据库:事务和ACID实现原理

一、什么是事务:

数据库的事务是并发控制的基本单位,是指逻辑上的一组操作,要么全部执行,要么全部不执行。

1、事务的特性:

(1)原子性:事务是一个不可分割的工作单元,事务里的操作要么都成功,要么都失败,如果事务执行失败,则需要进行回滚。

(2)隔离性:事务的所操作的数据在提交之前,对其他事务的可见程度。

(3)持久性:一旦事务提交,它对数据库中数据的改变就是永久的。

(4)一致性:事务不能破坏数据的完整性和业务的一致性。例如在转账时,不管事务成功还是失败,双方钱的总额不变。

 

二、事务ACID特性的实现原理:

1、原子性:

原子性是通过MySQL的回滚日志undo log来实现的:当事务对数据库进行修改时,InnoDB会生成对应的undo log;如果事务执行失败或调用了rollback,导致事务需要回滚,便可以利用undo log中的信息将数据回滚到修改之前的样子。

undo log (回滚日志):是采用段(segment)的方式来记录的,每个undo操作在记录的时候占用一个undo log segment。在数据更改操作时,记录了相对应的undo log的目的在于:

  • 保证数据的原子性,记录事务发生之前的一个版本,用于回滚;
  • 通过mvcc + undo log实现Innodb事务可重复读和读已提交隔离级别。

2、隔离性:

隔离性是事务所操作的数据在提交之前,对其他事务的可见程度。

2.1、事务隔离级别:

为保证在并发环境下读取数据的完整性和一致性,数据库提供了四种事务隔离级别,隔离级别越高,越能保证数据的完整性和一致性,但对高并发性能影响也越大,执行效率越低。(四种隔离级别从上往下依次升高)

读未提交:允许事务在执行过程中,读取其他事务尚未提交的数据;

读已提交:允许事务在执行过程中读取其他事务已经提交的数据;

可重复读(默认级别):在同一个事务内,任意时刻的查询结果都是一致的;

读序列化:所有事务逐个依次执行,每次读都需要获取表级共享锁,读写会相互阻塞。

2.2、如果不考虑事务的隔离性,在事务并发的环境下,可能存在问题有

(1)更新丢失:两个或多个事务操作相同的数据,然后基于选定的值更新该行时,由于每个事务都不知道其他事务的存在,就会发生丢失更新问题:最后的更新覆盖了其他事务所做的更新。

(2)脏读:指事务A正在访问数据,并且对数据进行了修改(事务未提交),这时,事务B也使用这个数据,后来事务A撤销回滚,并把修改后的数据恢复原值,B读到的数据就与数据库中的数据不一致,即B读到的数据是脏数据。

(3)不可重复读:在一个事务内,多次读取同一个数据,但是由于另一个事务在此期间对这个数据做了修改并提交,导致前后读取到的数据不一致;

(4)幻读:在一个事务中,先后两次进行读取相同的数据(一般是范围查询),但由于另一个事务新增或者删除了数据,导致前后两次结果不一致。

① 不可重复读和幻读的区别:

不可重复读侧重于读取到其他事务修改的数据,幻读侧重于读取到其他事务新增或者删除的数据。

② 可以采用锁机制来解决不可重复读和幻读:

对于不可重复读,只需对操作的数据添加行级锁,防止操作的数据发生变化;而对于幻读,需要添加表级锁,将整张表锁定,防止新增或者删除数据。

不同的事务隔离级别,在并发环境会存在不同的并发问题:

√:可能出现的情况 

×:不会出现该情况

脏读

不可重复读

幻读

读序列化

×

×

×

可重复读

×

×

读已提交

×

读未提交

2.3、事务隔离性的实现原理:

为了实现事务隔离,数据库延伸出了数据库锁,其中Innodb事务的隔离级别是由锁机制和MVVC(多版本并发控制)实现的:

2.3.1、Mysql锁机制:

MySQL锁机制的基本工作原理就是:事务在修改数据库之前,需要先获得相应的锁,获得锁的事务才可以修改数据;在该事务操作期间,这部分的数据是锁定,其他事务如果需要修改数据,需要等待当前事务提交或回滚后释放锁。

(锁机制的更多说明可以参考另一篇博客:https://blog.csdn.net/a745233700/article/details/84504209

通过对InnoDB不同锁类型的特性分析,可以利用锁解决脏读、不可重复读、幻读:

  • 排它锁解决脏读

  • 共享锁解决不可重复读

  • 临键锁解决幻读

2.3.2、Multiversion concurrency control (MVCC 多版本并发控制):

InnoDB的MVCC是通过在每行记录后面保存两个隐藏的列来实现的,一个保存了行的事务ID(每次提交事务,事务ID会自增),一个保存了行的回滚段指针

MySQL数据库:事务和ACID实现原理

每开始一个新的事务,都会自动递增产生一个新的事务ID。事务开始时刻会把该事务ID放到当前事务影响的行事务ID字段中,而 DB_ROLL_PTR 指向该行回滚段的指针,该行记录上的所有版本数据,都在undo log回滚日志中通过链表形式组织,所以该值实际指向undo log中该行的历史记录链表。

在并发访问数据库时,对正在事务中的数据做MVCC多版本的管理,以避免写操作阻塞读操作,并且可以通过比较版本解决幻读。

MVCC只在 可重复度 和 读已提交 两个隔离级别下才会工作,其中,MVCC实质就是通过保存数据在某个时间点的快照来实现的。 

快照读:Innodb快照读,数据的读取将由 cache(原本数据) + undo(事务修改前的数据) 两部分组成

当前读:SQL读取的数据是最新版本。通过锁机制来保证读取的数据无法通过其他事务进行修改

在可重复读的隔离级别下,MVCC具体操作:

(1)SELECT操作:InnoDB遵循以下两个规则:

  • 只查找数据行的事务ID小于或等于当前事务ID的版本,这样可以确保事务读取的行,要么是在事务开始前已经存在的,要么是事务自身插入或者修改过的记录。
  • 行的删除版本要未被定义,读取到事务开始之前状态的版本,这可以确保事务读取到的行,在事务开始之前未被删除。只有同时满足的两者的记录,才能返回作为查询结果。

(2)INSERT:InnoDB为新插入的每一行保存当前事务编号作为行版本号。

(3)DELETE:InnoDB为删除的每一行保存当前事务编号作为行删除标识。

(4)UPDATE:InnoDB为插入一行新记录,保存当前事务编号作为行版本号,同时保存当前事务编号到原来的行作为行删除标识。

保存这两个额外系统版本号,使大多数读操作都可以不用加锁。这样设计使得读数据操作很简单,性能很好,并且也能保证只会读取到符合标准的行,不足之处是每行记录都需要额外的存储空间,需要做更多的行检查工作,以及一些额外的维护工作。

3、持久性:

持久性的实现关键在于redo log日志在执行SQL时会保存已执行的SQL语句到一个指定的Log文件,当执行recovery时重新执行redo log记录的SQL操作。

3.1、redo log日志:

当向数据库写入数据时,执行过程会首先写入Buffer Pool,Buffer Pool中修改的数据会定期刷新到磁盘中(这一过程叫做刷盘),这整一过程称为redo log。redo log 分为:

  • Buffer Pool内存中的日志缓冲(redo log buffer),该部分日志是易失性的;
  • 磁盘上的重做日志文件(redo log file),该部分日志是持久的。

Buffer Pool的使用可以大大提高了读写数据的效率,但是也带了新的问题:如果MySQL宕机,而此时Buffer Pool中修改的数据在内存还没有刷新到磁盘,就会导致数据的丢失,事务的持久性无法保证。

为了确保事务的持久性,在当事务提交时,会调用fsync接口对redo log进行刷盘, (即redo log buffer写日志到磁盘的redo log file中 ),刷新频率由 innodb_flush_log_at_trx_commit变量来控制的:

  • 0: 每秒刷新缓冲池中的数据写入到磁盘中的,当系统崩溃,会丢失1秒钟的数据 ;
  • 1: 事务每次提交的时候,就把缓冲池中的数据刷新到磁盘中;
  • 2:提交事务的时候,把缓冲池中的数据写入磁盘文件对应的 os cache 缓存里去,而不是直接进入磁盘文件。可能 1 秒后才会把 os cache 里的数据写入到磁盘文件里去。

4、一致性:

一致性指的是事务不能破坏数据的完整性和业务的一致性 :

  • 数据的完整性: 实体完整性、列完整性(如字段的类型、大小、长度要符合要求)、外键约束等

  • 业务的一致性:例如在银行转账时,不管事务成功还是失败,双方钱的总额不变。

那是如何保证数据一致性的?其实数据一致性是通过事务的原子性、持久性和隔离性来保证的:

  • 原子性:语句要么全执行,要么全不执行,是事务最核心的特性,事务本身就是以原子性来定义的;主要基于undo log实现
  • 持久性:保证事务提交后不会因为宕机等原因导致数据丢失;主要基于redo log实现
  • 隔离性:保证事务执行尽可能不受其他事务影响;InnoDB默认的隔离级别是RR,RR的实现主要基于锁机制(包含next-key lock)、MVCC(包括数据的隐藏列、基于undo log的版本链、ReadView)
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/100012.html原文链接:https://javaforall.cn

【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛

【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...

(0)
blank

相关推荐

  • 2014—多校训练2(ZCC Loves Codefires)

    2014—多校训练2(ZCC Loves Codefires)

  • 频谱仪无线信号测试_无线信号检测仪app

    频谱仪无线信号测试_无线信号检测仪appAirMagnetSpectrumXT可实时探测并确定大量非WLAN干扰源,该干扰源会干扰和降低WLAN网络性能。设备或干扰源名单包括蓝牙设备、数字和模拟无绳电话、传统和变频微波炉、无线游戏控制器、数字视频转换器、婴儿监视器、RF干扰发射台、雷达、运动探测器和zigbee设备等等。用户也可获得干扰源的详细信息,包括峰值和平均功率、首次和最后看到的时间、中心频率、受影响的信道、干…

  • java int最大值和最小值_excel中求最大值和最小值

    java int最大值和最小值_excel中求最大值和最小值Java中Integer的最大值和最小值.JavaByte的最大值和最小值.Javafloat的最大值和最小值.Javalong的最大值和最小值.

  • 语义分割模型精度排名_场景语义分割

    语义分割模型精度排名_场景语义分割憨批的语义分割1——基于Mobile模型的segnet讲解学习前言什么是Segnet模型segnet模型的代码实现1、主干模型Mobilenet。2、segnet的Decoder解码部分代码测试学习前言最近开始设计新的领域啦,语义分割也是图像处理一个非常重要的应用方向,我查了很多资料苦于如何入门,接下来给大家讲讲里面比较基础的segnet模型。在下一个BLOG我会跟大家讲怎么训练自己的segn…

  • MySQL里有2000w数据,redis中只存20w的数据,如何保证redis中的数据都是热点数据?「建议收藏」

    MySQL里有2000w数据,redis中只存20w的数据,如何保证redis中的数据都是热点数据?

  • 考研数学真题用谁的_蓝桥杯编程题

    考研数学真题用谁的_蓝桥杯编程题⭐️引言⭐️大家好,我是执梗,蓝桥杯的报名快接近尾声,如果有兄弟还没报名不了解比赛,缺少视频讲解和真题资源的一定要阅读一下我的这篇蓝桥全解析——蓝桥全解析。为了帮助兄弟们更好准备比赛,我特意选取了蓝桥往年真题中许多能体现出蓝桥经典题型的题目——如暴力遍历、动态规划等等。有帮助的兄弟们一定要收藏一下,后面会陆续更新。????博客首页:执梗的博客????欢迎关注????点赞????收藏⭐️留言????❤️:热爱Java学习,期待一起交流!????作者水平很有限,如果发现错误

发表回复

您的电子邮箱地址不会被公开。

关注全栈程序员社区公众号