MySQL如何实现事务的ACID

前言

最近在面试,有被问到,MySQL的InnoDB引擎是如何实现事务的,又或者说是如何实现ACID这几个特性的,当时没有答好,所以自己总结出来,记录一下。

事务的四大特性ACID

事务的四大特性ACID分别是,A-原子性(Atomicity),C-一致性(Consistency),I-隔离性(Isolation),D-持久性(Durability)。一致性是最终目的,原子性、隔离性、持久性是为了保证一致性所做的措施。所以我写的顺序并不是按照ACID来写的,将一致性放到了最后,顺序就变成了,ADIC。

原子性(A)

原子性是指一个事务就是一个不可分割的工作单位,要么全部都执行成功,要么全部都执行失败,没有中间状态或是只执行一部分。

MySQL的InnoDB引擎是靠undo log(回滚日志)来实现的,undo log能够保证在事务回滚时,能够撤销所有已经执行成功的SQL。

undo log 属于逻辑日志,它记录的是SQL执行相关的信息。当事务对数据库进行修改时,InnoDB会生成与之对应的undo log。如果事务执行失败或者调用的rollback,导致事务需要回滚,InnoDB引擎会根据undo log中的记录,将数据回滚到之前的样子。
例如在执行insert语句时会生成相关的delete语句的undo log。反之执行delete语句也会生成相关的insert语句的undo log。执行update语句时也是如此,不过update语句在执行undo log回滚时有可能会涉及到MVCC。主要是为了保证在执行undo log的时候的select能看到哪个版本的数据。

持久性(D)

持久性是指事务一旦提交,对数据库的操作就是永久性的,接下来的其他操作和异常故障不应该对它有任何影响。
我们都知道MySQL的数据最终是存放在磁盘中的,所以才会有磁盘的容量大小决定数据容量的大小。但是如果对MySQL的操作都是通过读写磁盘来进行的话,那么光是磁盘的I/O就够把效率大大的拉低了。

所以InnoDB为MySQL提供了缓冲池(Buffer Pool),Buffer Pool中包含了磁盘中部分数据页的映射。
当从数据库读取数据时,会先从Buffer Pool中读取数据,如果Buffer Pool中没有,则从磁盘读取后放入到Buffer Pool中。
当向数据库写入数据时,会先写入到Buffer Pool中,Buffer Pool中更新的数据会定期刷新到磁盘中(此过程称为刷脏)。

虽然Buffer Pool为MySQL的读写提高了效率,但是却也带来了新的问题,那就是如果数据刚更新到Buffer Pool中还没来得及刷新到磁盘中时,MySQL突然宕机了,这就会导致数据丢失,造成事务的持久性无法保证了。
为了解决这个缓存的一致性问题,redo log就出现了。在对Buffer Pool中的数据进行修改的时候通过redo log记录这次操作,当事务提交时会通过fsync接口对redo log进行刷盘。

因为在事务提交时会把redo log是同步在磁盘中的,所以当MySQL出现宕机时,可以从磁盘中读取redo log进行数据的恢复,从而保证了事务的持久性。

redo log 采用的预写的方式记录日志,即先记录日志,再更新Buffer Pool,这样就强行的保证了,数据只要保存在了redo log中就一定会存储到磁盘中了。

这要解释一下,redo log 也是写磁盘,刷脏也是写磁盘,为啥要先记录redo log而不是直接刷脏?

主要原因就是redo log比刷脏快很多。

第一点是,redo log是追加操作日志,是顺序IO;而刷脏是随机IO,因为每次更新的数据不一定是挨着的,也就是随机的。

第二点是,刷脏是以数据页(Page)为单位的(即每次最少从磁盘中读取一页数据到内存,或者最少刷一页数据到磁盘),MySQL默认页大小是16KB,对一个页上的修改,都要整个页都刷到磁盘中;而redo log只包含真正的需要写入磁盘的操作日志。

MySQL还有一个记录操作的日志,叫binlog ,那么redo log和binlog又有什么区别呢?

  • 第一点作用上的区别:

redo log是用来记录更新缓存的,为了保证MySQL就算宕机也不会影响事务的持久性;binlog是用来记录什么时间操作了什么,主要有时间点,可以保证将数据恢复到某个时间点,也有用于主从同步数据的。

  • 第二点层次上的区别:

redo log是存储引擎InnoDB实现的(MyISAM就没有redo log),而binlog是在MySQL服务器层面存在的任何其他存储引擎也有binlog。
存储内容上,redo log是物理日志,基于磁盘的数据页,binlog是逻辑日志,存储的一条执行SQL。

  • 第三点写入时机的区别:

redo log 在默认情况下是在事务提交时,进行刷盘的;可以通过参数:innodb_flush_log_at_trx_commit 来改变策略,可以不用等到事务提交时才进行刷盘。
如:可以设置成每秒提交一次。
binlog是在事务提交时写入。

隔离性(I)

原子性和持久性都是基于单个事务内部的措施,而隔离性是只多个事务之间相互隔离,互不影响的特性。
我们都知道事务的隔离级别中最严谨的是串行化(Serializable),但是隔离性越高,性能就越低,所以一般不使用串行化这个隔离级别。
对于隔离性的,我们要分两种情况进行讨论:

  • 一个事务中的写操作对另一个事务中的写操作的影响;
  • 一个事务中的写操作对另一个事务中的读操作的影响;

首先,事务间的写操作其实是靠MySQL的锁机制来实现隔离的,而事务间的写和读操作是靠MVCC机制来实现的。

锁机制

MySQL中的锁主要有

按照功能分:读锁和写锁;按照作用范围分:表级锁和行级锁;
还有意向锁,间隙锁等。

读锁:又称“共享锁”,是指多个事务可以共享一把锁,都只能访问数据,并不能修改。

写锁:又称“排他锁”,是不能和其他事务共享数据的,如果一个事务获取到了一个数据的排他锁,那么其他事务就不能再获取该行的其他锁,包括共享锁和排他锁。

表级锁:是指会将整个表进行锁定,性能较差,不同存储引擎支持的锁的粒度不同,MyISAM引擎支持表级锁,InnoDB引擎支持表级锁也支持行级锁。

行级锁:会将需要操作的相应行进行锁定,性能好。

意向锁:意向锁是表级锁,如果在一个事务已经对一个表中的某个数据加上了排他锁或共享锁,那么就可以加上意向锁,这样当下一个事务来进行锁表的时候发现已经存在意向锁了,就会先被阻塞,如果不加意向锁的话,第二个事务来锁表的时候需要一行一行的遍历查看是否有数据已经被锁住了。

间隙锁:间隙锁是为了防止产生幻读而加的锁,加在不存在的空闲空间,可以是两个索引记录之间,也可能是第一个索引记录之前或最后一个索引之后的空间(但是并不包含当前记录)。这样就保证了在间隙锁执行的时候,新增的数据会阻塞,保证了一个事务中的两次查询获得的记录数都是一致的。

Next-Key Lock:Next-Key Lock是行级锁和间隙锁的结合产生的锁,因为间隙锁是不会锁住当前记录的而Next-Key Lock是会将当前记录也锁住的。

例如:如果一个表中有三条数据分别是:

id name number
1 小明 16
2 小红 17
3 小张 20
4 小王 20

那么在执行SQL:select * from table where number = 17 for update 时间隙锁会锁住,number的区间是(16,17),(17,20),但是Next-Key Lock的锁住的是:
16,17),(17,20)区间加间隙锁,同时number=17加记录锁。

锁机制保障了多个事务间的写操作的隔离,而多个事务间的读和写操作的保证是需要通过MVCC机制来保证的。

MVCC机制

MVCC全称是【Multi-Version ConCurrency Control】即多版本控制协议。

MVCC的主要是靠在每行记录上增加隐藏列和使用undo log来实现的,隐藏列主要包括,改行数据创建的版本号(递增的),删除时间,指向undo log的指针等。

那么MVCC是如何保证读写隔离的呢?主要是通过快照读和当前读两个操作。

  • 快照读:

MVCC为了保证并发的效率,在进行读取数据的时候是不加锁的,在执行select的时候(不带锁的普通select),会先读取当前数据的版本号,如果在select还没返回结果时,有事务将此行数据进行了修改,那么版本号就会比执行select的时候的大,所以为了保证select读取数据的一致性,就只会读取小于或等于当前版本的数据,这个历史版本的数据就是从undo log中获取到的。

  • 当前读:

当执行insert、update、delete的时候,是读取的当前最新的版本数据,并且会给当前记录加上锁,用来保证在操作的时候不会被别的事务将版本号进行修改。

像普通的select就是快照读即读取的有可能就是数据的历史版本。

insert、update、delete、select ... lock in share mode 和select ... for update 读取的就是当前读,即读取的都是数据的最新版本。

其实将隔离级别设置为Serializable也是可以实现读写隔离的,但是并发效率会比低很多,所以一般用的很少,但是MVCC是读不加锁的,只有在写的时候才会加锁,从而提高的并发的效率。

通过MVCC机制保证了多个事务间的读写隔离,从而实现了事务的隔离性。

一致性(C)

一致性是指在事务执行前后,数据的一致性,事务前后数据完整性没有破坏,并且都是合法的数据状态。

  • 其中一致性的指标有:

索引的完整(唯一索引,不重复等),数据列的完成(字段类型,长度,大小符合要求),外键约束等。

  • 实现一致性的措施:

保证原子性,持久性,隔离性,如果这些特性都无法保证,那么一致性就也无法保证了。从数据库层面来看,除了前面那几个特性的保证外,对字段的一致性是有保证措施的,例如整型的字符不能传入,字符串、时间等格式,字符串的长度不能超过列的限制。但是在应用层面也是需要开发者自己来保证的,
例如:从A转账给B一部分金额,那么就要保证,从A从将金额扣除多少就要去给B增加多少金额,如果只扣除A的金额,而没有增加B的金额,是无法保证一致性的。

另外,MySQL还通过两阶段提交事务,保证了redo log和binlog之间的数据一致性问题。

通过上面介绍持久性的时候解释了,redo log和binlog的区别了,在区别中的第三条有说到,在默认情况下,事务提交时,既写redo log 有写binlog那么他们是如何协调一致性的呢?事务提交成功以写入哪个日志为准呢?
MySQL通过两阶段提交来保证这两个日志的数据一致性。

  • 第一阶段提交,

将redo log提交到磁盘,并将状态改为prepare状态,binlog不做任何操作。

  • 第二阶段提交,

1、生成事务操作的binlog,并将binlog写入到磁盘中。

2、调用引擎的提交事务接口,将redo log的状态从prepare改为commit,事务提交完成。
通过上面这两阶段提交保证了事务数据的一致性。
当事务提交时redo log处于prepare阶段时,发生MySQL宕机或崩溃,则会执行事务回滚。
当事务提交redo log处于commit阶段时,发生了崩溃会执行事务恢复,本机事务通过redol og进行恢复,而如果是主从数据库的话,在commit阶段,会根据binlog对从库进行数据恢复。
这就是以写入binlog成功为提交事务成功的依据。因为一般在崩溃恢复的时候都是用binlong进行恢复的,如果还未生成binlog,只写入了redo log。在恢复的时候redo log恢复的是一个版本的数据,而通过bin log恢复的从库数据会是之前的一个时间点的binlog版本的数据,这样数据就导致不一致了。

总结

MySQL事务的ACID,一致性是最终目的。
保证一致性的措施有:

  • A原子性:靠undo log来保证(异常或执行失败后进行回滚)。
  • D持久性:靠redo log来保证(保证当MySQL宕机或停电后,可以通过redo log最终将数据保存至磁盘中)。
  • I隔离性:事务间的读写靠MySQL的锁机制来保证隔离,事务间的写操作靠MVCC机制(快照读、当前读)来保证隔离性。
  • C一致性:事务的最终目的,即需要数据库层面保证,又需要应用层面进行保证,并且MySQL底层通过两阶段提交事务保证了事务持久化时的一致性。

以上就是MySQL如何实现事务的ACID的详细内容,更多关于MySQL实现事务的ACID的资料请关注我们其它相关文章!

(0)

相关推荐

  • 解析MySQL8.0新特性——事务性数据字典与原子DDL

    前言 事务性数据字典与原子DDL,是MySQL 8.0推出的两个非常重要的新特性,之所以将这两个新特性放在一起,是因为两者密切相关,事务性数据字典是前提,原子DDL是一个重要应用场景. MySQL 8.0之前的数据字典 MySQL 8.0之前的数据字典,主要由以下三部分组成: (1)操作系统文件 db.opt:数据库元数据信息 frm:表元数据信息 par:表分区元数据信息 TRN/TRG:触发器元数据信息 ddl_log.log:DDL过程中产生的元数据信息 (2)mysql库下的非InnoD

  • Mysql中事务ACID的实现原理详解

    引言 照例,我们先来一个场景~ 面试官:"知道事务的四大特性么?" 你:"懂,ACID嘛,原子性(Atomicity).一致性(Consistency).隔离性(Isolation).持久性(Durability)!" 面试官:"你们是用mysql数据库吧,能简单说说innodb中怎么实现这四大特性的么?" 你:"我只知道隔离性是怎么做的balabala~~" 面试官:"还是回去等通知吧~" OK,回到正题

  • 利用mysql事务特性实现并发安全的自增ID示例

    项目中经常会用到自增id,比如uid,最简单的方法就是用直接用数据库提供的AUTO_INCREMENT,但是如果用户量非常大,几千万,几亿然后需要分表存储的时候呢,这种方案就搞不定了,所以最好有一个全局的自增ID的生成器,不管是否分表,都能从生成器中获取到全局自增的ID. 实现方法应该有很多,不过所有的方案都需要解决一个问题,就是保证在高并发的情景下,数据获取依然正确,每次获取的ID都不会重复. 这里我分享两种利用mysql的innodb的事务特性来实现的方案,一种是实现过了的,另一种没有试验过

  • MySQL InnoDB如何保证事务特性示例详解

    前言 如果有人问你"数据库事务有哪些特性"?你可能会很快回答出原子性.一致性.隔离性.持久性即ACID特性.那么你知道InnoDB如何保证这些事务特性的吗?如果知道的话这篇文章就可以直接跳过不看啦(#^.^#) 先说结论: redo log重做日志用来保证事务的持久性 undo log回滚日志保证事务的原子性 undo log+redo log保证事务的一致性 锁(共享.排他)用来保证事务的隔离性 重做日志 redo log 重做日志 redo log 分为两部分:一部分是内存中的重做

  • 讲解MySQL中的事务特性

    一个事务是一个连续的一组数据库操作,就好像它是一个单一的工作单元进行.换言之,永远不会是完整的事务,除非该组内的每个单独的操作是成功的.如果在事务的任何操作失败,则整个事务将失败. 实际上,会俱乐部许多SQL查询到一个组中,将执行所有的人都一起作为事务的一部分. 事务的特性: 事务有以下四个标准属性的缩写ACID,通常被称为: 原子性: 确保工作单元内的所有操作都成功完成,否则事务将被中止在故障点,和以前的操作将回滚到以前的状态. 一致性: 确保数据库正确地改变状态后,成功提交的事务. 隔离性:

  • MySQL如何实现事务的ACID

    前言 最近在面试,有被问到,MySQL的InnoDB引擎是如何实现事务的,又或者说是如何实现ACID这几个特性的,当时没有答好,所以自己总结出来,记录一下. 事务的四大特性ACID 事务的四大特性ACID分别是,A-原子性(Atomicity),C-一致性(Consistency),I-隔离性(Isolation),D-持久性(Durability).一致性是最终目的,原子性.隔离性.持久性是为了保证一致性所做的措施.所以我写的顺序并不是按照ACID来写的,将一致性放到了最后,顺序就变成了,AD

  • MySQL关系型数据库事务的ACID特性与实现

    目录 1. 事务的 ACID 特性详 2. MySQL 事务的实现 3. Gorm 事务的使用 4. Spring 事务的使用 1. 事务的 ACID 特性详 ACID 是为保证事务(transaction)是正确可靠的,所必须具备的四个特性: 原子性(Atomicity):事务中的操作同时成功或者失败. 一致性(Consistency):数据库事务不能破坏数据的完整性以及业务逻辑上的一致. 隔离性(Isolation):一个事务不影响其他事务的运行效果. 持久性(Durability):事务完

  • MySQL事务的ACID特性以及并发问题方案

    目录 一.事务的概念 二.ACID特性 三.事务并发存在的问题 四.事务相关命令 一.事务的概念 一个事务是由一条或多条对数据库操作的SQL语句所组成的一个不可分割的单元,只有当事务中所有操作都正常执行完了,整个事务才会被提交给数据库,如果有部分事务处理失败,那么事务就要回滚到最初的状态,因此,事务要么全部执行成功,要么全部失败. 所以要记住事务几个基本概念,如下: 事务是一组SQL语句的执行,要么全部成功,要么全部失败,不能出现部分成功,部分失败的结果,保证事务执行的原子操作.事务的所有SQL

  • MySql事务及ACID实现原理详解

    目录 逻辑架构和存储引擎 自动提交 特殊操作 ACID 特性 原子性 持久性 隔离性 脏读.不可重复读和幻读 事务隔离级别 MVCC 一致性 逻辑架构和存储引擎 自动提交 MySQL 中默认采用的是自动提交(autocommit)模式,如下所示: 在自动提交模式下,如果没有 start transaction 显式地开始一个事务,那么每个 sql 语句都会被当做一个事务执行提交操作. 通过如下方式,可以关闭 autocommit;需要注意的是,autocommit 参数是针对连接的,在一个连接中

  • 全面了解MySql中的事务

    最近一直在做订单类的项目,使用了事务.我们的数据库选用的是MySql,存储引擎选用innoDB,innoDB对事务有着良好的支持.这篇文章我们一起来扒一扒事务相关的知识. 为什么要有事务? 事务广泛的运用于订单系统.银行系统等多种场景.如果有以下一个场景:A用户和B用户是银行的储户.现在A要给B转账500元.那么需要做以下几件事: 1. 检查A的账户余额>500元: 2. A账户扣除500元: 3. B账户增加500元: 正常的流程走下来,A账户扣了500,B账户加了500,皆大欢喜.那如果A账

  • MySQL四种事务隔离级别详解

    本文实验的测试环境:Windows 10+cmd+MySQL5.6.36+InnoDB 一.事务的基本要素(ACID) 1.原子性(Atomicity):事务开始后所有操作,要么全部做完,要么全部不做,不可能停滞在中间环节.事务执行过程中出错,会回滚到事务开始前的状态,所有的操作就像没有发生一样.也就是说事务是一个不可分割的整体,就像化学中学过的原子,是物质构成的基本单位. 2.一致性(Consistency):事务开始前和结束后,数据库的完整性约束没有被破坏 .比如A向B转账,不可能A扣了钱,

  • 简单介绍MySQL中的事务机制

    从一个问题开始 最近银行这个事情闹的比较厉害啊,很多储户的钱放在银行,就不翼而飞了,而银行还不管不问,说是用户的责任,打官司,用户还能输了,这就是"社会主义".咱还是少发牢骚,多种树,莫谈国事. 说到银行存钱,就不得不说一下从银行取钱这件事情,从ATM机取钱这件简单的事情,实际上主要分为以下几个步骤: 登陆ATM机,输入密码: 连接数据库,验证密码: 验证成功,获得用户信息,比如存款余额等: 用户输入需要取款的金额,按下确认键: 从后台数据库中减掉用户账户上的对应金额: ATM吐出钱:

  • MySql 知识点之事务、索引、锁原理与用法解析

    本文实例讲述了MySql 知识点之事务.索引.锁原理与用法.分享给大家供大家参考,具体如下: 事务 事务概念 事务就是一组原子性的SQL查询,或者说一个独立的工作单元.如果数据库引擎执行一组操作语句,那么久执行所有的操作,如果其中有任何一条崩溃或其他原因无法执行,所有语句将不会执行.也就是说事务内的语句,要么全部执行成功,要么全部执行失败. 事务特性ACID 原子性(atomicity) 一个事务被视为最小工作单元,不可拆分,整个事务所有的操作要么全部提交成功,要么全部失败回滚,不可只执行部分.

  • MySQL索引与事务定义到使用详解

    目录 1.索引的本质 2.索引的使用 2.1查看索引 2.2创建索引 2.3删除索引 3.索引的数据结构 3.1B树 3.2B+树 4.事务 4.1事物的回滚(rollback) 4.2事务的四大特性(ACID) 4.2.1 原子性 4.2.2 一致性 4.2.3 持久性 4.2.4 隔离性 5.并发引起的问题 5.1 "读脏数据" 5.2 "不可重复读" 5.3 "幻读" 6.MySQL的隔离级别 6.1 read uncommitted 6.

随机推荐