百分百源码网-让建站变得如此简单! 登录 注册 签到领金币!

主页 | 如何升级VIP | TAG标签

当前位置: 主页>网站教程>数据库> MySQL事务之ACID特性(详解)
分享文章到:

MySQL事务之ACID特性(详解)

发布时间:09/01 来源:未知 浏览: 关键词:
事务是MySQL等关系型数据库不同于NoSQL的重要方面,是包管数据一致性的重要手段。本文将第一介绍MySQL事务相关的根基概念,然后介绍事务的ACID特性,并剖析其实现道理。

一、根基概念

事务(Transaction)是拜访和更新数据库的程序施行单元;事务中大概包括一个或多个sql语句,这些语句要末都施行,要末都不施行。作为一个关系型数据库,MySQL支撑事务,本文介绍基于MySQL5.6。

第一回忆一下MySQL事务的根基知识。

1. 逻辑架构和储备引擎

图片来源:https://blog.csdn.net/fuzhongmin05/article/details/70904190

如上图所示,MySQL效劳器逻辑架构从上往下可以分为三层:

(1)第一层:处置客户端连接、授权认证等。

(2)第二层:效劳器层,负责查询语句的解析、优化、缓存乃至内置函数的实现、储备历程等。

(3)第三层:储备引擎,负责MySQL中数据的储备和提取。MySQL中效劳器层不治理事务,事务是由储备引擎实现的。MySQL支撑事务的储备引擎有InnoDB、NDB Cluster等,其中InnoDB的使用最为广泛;其他储备引擎不支撑事务,如MyIsam、Memory等。

如无非凡说明,后文中描写的内容都是基于InnoDB。

2. 提交和回滚

典型的MySQL事务是如下操纵的:


start transaction;
……  #一条或多条sql语句
commit;

其中start transaction标识事务开端,commit提交事务,将施行结果写入到数据库。假如sql语句施行显现问题,会调取rollback,回滚所有已经施行成功的sql语句。当然,也可以在事务中直接使用rollback语句停止回滚。

主动提交

MySQL中默许采纳的是主动提交(autocommit)模式,如下所示:

在主动提交模式下,假如没有start transaction显式地开端一个事务,那么每个sql语句都会被当做一个事务施行提交操纵。

通过如下方式,可以关闭autocommit;需要留意的是,autocommit参数是针对连接的,在一个连接中修改了参数,不会对其他连接发生影响。

假如关闭了autocommit,则所有的sql语句都在一个事务中,直到施行了commit或rollback,该事务完毕,同时开端了别的一个事务。

非凡操纵

在MySQL中,存在一些非凡的命令,假如在事务中施行了这些命令,会立刻强迫施行commit提交事务;如DDL语句(create table/drop table/alter/table)、lock tables语句等等。

不外,常用的select、insert、update和delete命令,都不会强迫提交事务。

3. ACID特性

ACID是衡量事务的四个特性:

  • 原子性(Atomicity,或称不成分割性)
  • 一致性(Consistency)
  • 隔离性(Isolation)
  • 耐久性(Durability)

依照严厉的标准,只要同时知足ACID特性才是事务;但是在各大数据库厂商的实现中,真正知足ACID的事务少之又少。例如MySQL的NDB Cluster事务不知足耐久性和隔离性;InnoDB默许事务隔离级别是可反复读,不知足隔离性;Oracle默许的事务隔离级别为READ COMMITTED,不知足隔离性……因此与其说ACID是事务必需知足的前提,不如说它们是衡量事务的四个维度。

下面将具体介绍ACID特性及其实现道理;为了便于懂得,介绍的次序不是严厉依照A-C-I-D。

二、原子性

1. 定义

原子性是指一个事务是一个不成分割的工作单位,其中的操纵要末都做,要末都不做;假如事务中一个sql语句施行失败,则已施行的语句也必需回滚,数据库退回到事务前的状态。

2. 实现道理:undo log

在说明原子性道理此前,第一介绍一下MySQL的事务日志。MySQL的日志有许多种,如二进制日志、错误日志、查询日志、慢查询日志等,此外InnoDB储备引擎还供给了两种事务日志:redo log(重做日志)和undo log(回滚日志)。其中redo log用于包管事务耐久性;undo log则是事务原子性和隔离性实现的根基。

下面说回undo log。实现原子性的关键,是当事务回滚时能够撤销所有已经成功施行的sql语句。InnoDB实现回滚,靠的是undo log:当事务对数据库停止修改时,InnoDB会生成对应的undo log;假如事务施行失败或调取了rollback,致使事务需要回滚,便可以利用undo log中的信息将数据回滚到修改此前的模样。

undo log属于逻辑日志,它记载的是sql施行相关的信息。当发生回滚时,InnoDB会按照undo log的内容做与此前相反的工作:关于每个insert,回滚时会施行delete;关于每个delete,回滚时会施行insert;关于每个update,回滚时会施行一个相反的update,把数据改回去。

以update操纵为例:当事务施行update时,其生成的undo log中会包括被修改行的主键(以便知道修改了哪些行)、修改了哪些列、这些列在修改前后的值等信息,回滚时便可以使用这些信息将数据复原到update此前的状态。

三、耐久性

1. 定义

耐久性是指事务一旦提交,它对数据库的改动就应当是永远性的。接下来的其他操纵或故障不该该对其有任何影响。

2. 实现道理:redo log

redo log和undo log都属于InnoDB的事务日志。下面先聊一下redo log存在的背景。

InnoDB作为MySQL的储备引擎,数据是存置在磁盘中的,但假如每次读写数据都需要磁盘IO,效力会很低。为此,InnoDB供给了缓存(Buffer Pool),Buffer Pool中包括了磁盘中部分数据页的映射,作为拜访数据库的缓冲:当从数据库读取数据时,会第一从Buffer Pool中读取,假如Buffer Pool中没有,则从磁盘读取后放入Buffer Pool;当向数据库写入数据时,会第一写入Buffer Pool,Buffer Pool中修改的数据会按期刷新到磁盘中(这一历程称为刷脏)。

Buffer Pool的使用大大提高了读写数据的效力,但是也带了新的问题:假如MySQL宕机,而此时Buffer Pool中修改的数据还没有刷新到磁盘,就会致使数据的丧失,事务的耐久性没法包管。

于是,redo log被引入来解决这个问题:当数据修改时,除了修改Buffer Pool中的数据,还会在redo log记载这次操纵;当事务提交时,会调取fsync接口对redo log停止刷盘。假如MySQL宕机,重新启动时可以读取redo log中的数据,对数据库停止复原。redo log采纳的是WAL(Write-ahead logging,预写式日志),所有修改先写入日志,再更新到Buffer Pool,包管了数据不会因MySQL宕机而丧失,从而知足了耐久性要求。

既然redo log也需要在事务提交时将日志写入磁盘,为什么它比直接将Buffer Pool中修改的数据写入磁盘(即刷脏)要快呢?主要有以下两方面的缘由:

(1)刷脏是随机IO,由于每次修改的数据位置随机,但写redo log是追加操纵,属于次序IO。

(2)刷脏是以数据页(Page)为单位的,MySQL默许页大小是16KB,一个Page上一个小修改都要整页写入;而redo log中只包括真正需要写入的部分,无效IO大大减少。

3. redo log与binlog

我们知道,在MySQL中还存在binlog(二进制日志)也可以记载写操纵并用于数据的复原,但二者是有着基本的不一样的:

(1)作用不一样:redo log是用于crash recovery的,包管MySQL宕机也不会影响耐久性;binlog是用于point-in-time recovery的,包管效劳器可以基于时间点复原数据,此外binlog还用于主从复制。

(2)层次不一样:redo log是InnoDB储备引擎实现的,而binlog是MySQL的效劳器层(可以参照 文章前面临MySQL逻辑架构的介绍)实现的,同时支撑InnoDB和其他储备引擎。

(3)内容不一样:redo log是物理日志,内容基于磁盘的Page;binlog的内容是二进制的,按照binlog_format参数的不一样,大概基于sql语句、基于数据本身或者二者的混合。

(4)写入时机不一样:binlog在事务提交时写入;redo log的写入时机相对多元:

  • 前面曾提到:当事务提交时会调取fsync对redo log停止刷盘;这是默许状况下的战略,修改innodb_flush_log_at_trx_commit参数可以改动该战略,但事务的耐久性将没法包管。
  • 除了事务提交时,还有其他刷盘时机:如master thread每秒刷盘一次redo log等,这样的好处是不必然要比及commit时刷盘,commit速度大大加快。

四、隔离性

1. 定义

与原子性、耐久性侧重于研讨事务本身不一样,隔离性研讨的是不一样事务之间的彼此影响。隔离性是指,事务内部的操纵与其他事务是隔离的,并发施行的各个事务之间不克不及互相关扰。严厉的隔离性,对应了事务隔离级别中的Serializable (可串行化),但实际利用中出于机能方面的思考很少会使用可串行化。

隔离性追求的是并发情形下事务之间互不干扰。简便起见,我们仅思考最简便的读操纵和写操纵(临时不思考带锁读等非凡操纵),那么隔离性的商量,主要可以分为两个方面:

  • (一个事务)写操纵对(另一个事务)写操纵的影响:锁机制包管隔离性
  • (一个事务)写操纵对(另一个事务)读操纵的影响:MVCC包管隔离性

2. 锁机制

第一来看两个事务的写操纵之间的彼此影响。隔离性要求统一时刻只能有一个事务对数据停止写操纵,InnoDB通过锁机制来包管这一点。

锁机制的根本道理可以概括为:事务在修改数据此前,需要先获得响应的锁;获得锁之后,事务便可以修改数据;该事务操纵期间,这部分数据是锁定的,其他事务假如需要修改数据,需要等候当前事务提交或回滚后开释锁。

行锁与表锁

依照粒度,锁可以分为表锁、行锁乃至其他位于二者之间的锁。表锁在操纵数据时会锁定整张表,并发机能较差;行锁则只锁定需要操纵的数据,并发机能好。但是由于加锁本身需要耗损资源(获得锁、检查锁、开释锁等都需要耗损资源),因此在锁定数据较多状况下使用表锁可以节约大量资源。MySQL中不一样的储备引擎支撑的锁是不一样的,例如MyIsam只支撑表锁,而InnoDB同时支撑表锁和行锁,且出于机能思考,绝大多数状况下使用的都是行锁。

怎样查看锁信息

有多种办法可以查看InnoDB中锁的状况,例如:


select * from information_schema.innodb_locks; #锁的概略
show engine innodb status; #InnoDB团体状态,其中包罗锁的状况

下面来看一个例子:


#在事务A中施行:
start transaction;
update account SET balance = 1000 where id = 1;
#在事务B中施行:
start transaction;
update account SET balance = 2000 where id = 1;

此时查看锁的状况:

show engine innodb status查看锁相关的部分:

通过上述命令可以查看事务24052和24053占用锁的状况;其中lock_type为RECORD,代表锁为行锁(记载锁);lock_mode为X,代表排它锁(写锁)。

除了排它锁(写锁)之外,MySQL中还有同享锁(读锁)的概念。由于本文重点是MySQL事务的实现道理,因此对锁的介绍到此为止,后续会专门写文章剖析MySQL中不一样锁的不同、使用处景等,欢迎关注。

介绍完写操纵之间的彼此影响,下面计议写操纵对读操纵的影响。

3. 脏读、不成反复读和幻读

第一来看并发状况下,读操纵大概存在的三类问题:

(1)脏读:当前事务(A)中可以读到其他事务(B)未提交的数据(脏数据),这种现象是脏读。举例如下(以账户余额表为例):

(2)不成反复读:在事务A中前后两次读取统一个数据,两次读取的结果不一样,这种现象称为不成反复读。脏读与不成反复读的不同在于:前者读到的是其他事务未提交的数据,后者读到的是其他事务已提交的数据。举例如下:

(3)幻读:在事务A中依照某个前提前后两次查询数据库,两次查询结果的条数不一样,这种现象称为幻读。不成反复读与幻读的不同可以通俗的懂得为:前者是数据变了,后者是数据的行数变了。举例如下:

4. 事务隔离级别

SQL标准中定义了四种隔离级别,并规定了每种隔离级别下上述几个问题可否存在。一样来说,隔离级别越低,系统开销越低,可支撑的并发越高,但隔离性也越差。隔离级别与读问题的关系如下:

在实际利用中,读未提交在并发时会致使许多问题,而机能相关于其他隔离级别提高却很有限,因此使用较少。可串行化强迫事务串行,并发效力很低,只要当对数据一致性要求极高且可以接受没有并发时使用,因此使用也较少。因此在大多数数据库系统中,默许的隔离级别是读已提交(如Oracle)可反复读(后文简称RR

可以通过如下两个命令离别查看全局隔离级别和本次会话的隔离级别:

InnoDB默许的隔离级别是RR,后文会重点介绍RR。需要留意的是,在SQL标准中,RR是没法幸免幻读问题的,但是InnoDB实现的RR幸免了幻读问题。

5. MVCC

RR解决脏读、不成反复读、幻读等问题,使用的是MVCC:MVCC全称Multi-Version Concurrency Control,即多版本的并发操纵和谈。下面的例子很好的表现了MVCC的特点:在统一时刻,不一样的事务读取到的数据大概是不一样的(即多版本)——在T5时刻,事务A和事务C可以读取到不一样版本的数据。

MVCC最大的长处是读不加锁,因此读写不冲突,并发机能好。InnoDB实现MVCC,多个版本的数据可以共存,主如果依托数据的潜藏列(也可以称之为标志位)和undo log。其中数据的潜藏列包罗了该行数据的版本号、删除时间、指向undo log的指针等等;当读取数据时,MySQL可以通过潜藏列推断可否需要回滚并寻到回滚需要的undo log,从而实现MVCC;潜藏列的具体格局不再展开。

下面结合前文提到的几个问题离别说明。

(1)脏读

当事务A在T3时间节点读取zhangsan的余额时,会发明数据已被其他事务修改,且状态为未提交。此时事务A读取最新数据后,按照数据的undo log施行回滚操纵,得到事务B修改前的数据,从而幸免了脏读。

(2)不成反复读

当事务A在T2节点第一次读取数据时,会记载该数据的版本号(数据的版本号是以row为单位记载的),假设版本号为1;当事务B提交时,该行记载的版本号增添,假设版本号为2;当事务A在T5再一次读取数据时,发明数据的版本号(2)大于第一次读取时记载的版本号(1),因此会按照undo log施行回滚操纵,得到版本号为1时的数据,从而实现了可反复读。

(3)幻读

InnoDB实现的RR通过next-key lock机制幸免了幻读现象。

next-key lock是行锁的一种,实现相当于record lock(记载锁) + gap lock(间隙锁);其特点是不仅会锁住记载本身(record lock的功效),还会锁定一个范畴(gap lock的功效)当然,这里我们计议的是不加锁读:此时的next-key lock并不是真的加锁,只是为读取的数据增添了标志(标志内容包罗数据的版本号等);准确起见姑且称之为类next-key lock机制。还是之前面的例子来说明:

当事务A在T2节点第一次读取0<id<5数据时,标志的不只是id=1的数据,而是将范畴(0,5)停止了标志,这样当T5时刻再次读取0<id<5数据时,便可以发明id=2的数据比此前标志的版本号更高,此时再结合undo log施行回滚操纵,幸免了幻读。

6. 总结

概括来说,InnoDB实现的RR,通过锁机制、数据的潜藏列、undo log和类next-key lock,实现了必然程度的隔离性,可以知足大多数场景的需要。不外需要说明的是,RR虽然幸免了幻读问题,但是究竟不是Serializable,不克不及包管完全的隔离,下面是一个例子,大家可以本人验证一下。

五、一致性

1. 根本概念

一致性是指事务施行完毕后,数据库的完全性束缚没有被毁坏,事务施行的前后都是合法的数据状态。数据库的完全性束缚包罗但不限于:实体完全性(如行的主键存在且独一)、列完全性(如字段的类型、大小、长度要相符要求)、外键束缚、会员自定义完全性(如转账前后,两个账户余额的和应当不变)。

2. 实现

可以说,一致性是事务追求的终究目标:前面提到的原子性、耐久性和隔离性,都是为了包管数据库状态的一致性。此外,除了数据库层面的保证,一致性的实现也需要利用层面停止保证。

实现一致性的办法包罗:

  • 包管原子性、耐久性和隔离性,假如这些特性没法包管,事务的一致性也没法包管
  • 数据库本身供给保证,例如不同意向整形列插入字符串值、字符串长度不克不及超越列的限制等
  • 利用层面停止保证,例如假如转账操纵只扣除转账者的余额,而没有增添接收者的余额,不管数据库实现的多么完善,也没法包管状态的一致

六、总结

下面总结一下ACID特性及其实现道理:

  • 原子性:语句要末全施行,要末全不施行,是事务最中心的特性,事务本身就是以原子性来定义的;实现主要基于undo log

  • 耐久性:包管事务提交后不会由于宕机等缘由致使数据丧失;实现主要基于redo log

  • 隔离性:包管事务施行尽大概不受其他事务影响;InnoDB默许的隔离级别是RR,RR的实现主要基于锁机制、数据的潜藏列、undo log和类next-key lock机制
  • 一致性:事务追求的终究目标,一致性的实现既需要数据库层面的保证,也需要利用层面的保

引荐学习:MySQL教程

以上就是MySQL事务之ACID特性(详解)的具体内容,更多请关注百分百源码网其它相关文章!

打赏

打赏

取消

感谢您的支持,我会继续努力的!

扫码支持
扫码打赏,你说多少就多少

打开支付宝扫一扫,即可进行扫码打赏哦

百分百源码网 建议打赏1~10元,土豪随意,感谢您的阅读!

共有151人阅读,期待你的评论!发表评论
昵称: 网址: 验证码: 点击我更换图片
最新评论

本文标签

广告赞助

能出一分力是一分吧!

订阅获得更多模板

本文标签

广告赞助

订阅获得更多模板