从零蛋开始学mysql小册_事务

本系列学习笔记旨在作者准备数据库方面面试用,有点临阵磨枪的感觉,我学习MySQL是参考的掘金小册的《MySQL 是怎样运行的:从根儿上理解 MySQL》和《MySQL 是怎样使用的:从零蛋开始学习 MySQL》,学习笔记里将核心概念和高频考点总结一下,图都是从小册里摘取的,如果涉及侵权可随时撤销,如果大家是零基础学习MySQL,强烈推荐上面的两本小册。

1、什么是事务?

数据库将现实世界里的对数据的操作(比如取钱、存钱等)映射到数据库的事务中,事务(transcation)就是一个或者多个数据库的操作,这些操作满足事务的四大特性。

1.1 事务的状态

事务的状态有以下5种:活动的(active)

部分提交的(partially committed)

失败的(failed)

中止的(aborted)

提交的(committed)

下面具体介绍事务的5种状态。

(1)活动的(active)

事务对应的数据库操作正在执行过程中,此时事务处于活动的状态。

(2)部分提交的(partially committed)

事务对应的一些列数据库的操作已经执行完毕,还没有将执行结果从数据库刷新到磁盘中,此时事务处于部分提交的状态。

(3)失败的(failed)

当事务处在活动的或者部分提交的状态时,可能遇到了某些错误(数据库自身错误、操作系统错误或者节点下电了)而无法继续执行,或者人为停止当前事务的执行,此时事务处于失败的状态。

(4)中止的(aborted)

数据库状态变为失败的状态时,会进行回滚操作,即将数据库恢复到事务执行前的状态,回滚后事务处于中止的状态。

(5)提交的(committed)

当一个处在部分提交的状态的事务将事务执行的结果从内存刷新到磁盘后,此时事务处于提交的状态。

状态转移图如下,图摘自链接1。事务的状态转移图

1.2 事务相关的基本sql语句

开启事务

BEGIN[WORK]

... 这里执行事务包含的数据库操作的sql语句

提交事务

COMMIT[WORK]

举个例子:

mysql> BEGIN;

Query OK, 0 rows affected (0.00 sec)

mysql> UPDATE account SET balance = balance - 10 WHERE id = 1;

Query OK, 1 row affected (0.02 sec)

Rows matched: 1 Changed: 1 Warnings: 0

mysql> UPDATE account SET balance = balance + 10 WHERE id = 2;

Query OK, 1 row affected (0.00 sec)

Rows matched: 1 Changed: 1 Warnings: 0

mysql> COMMIT;

Query OK, 0 rows affected (0.00 sec)

1.3 MySql支持事务的存储引擎

MySQL常见的额存储引擎有InnoDB、MyISAM和Memory等,目前只有InnoDB和NDB存储引擎支持事务,且对事务支持的程度不同。本文介绍的事务都是InnoDB存储引擎支持的事务。

2、事务的四大特性(ACID)

什么是事务的特性?上面讲到数据库将现实世界里涉及数据的处理操作映射到数据库中就是事务,现实世界里操作数据肯定要遵循一些状态转换的规则,这些规则映射到数据库的世界中就是事务的特性(即事务要遵循的状态转换规则)。事务的四大特性如下:原子性(Atomicity)

隔离性(Isolation)

一致性(Consistency)

持久性(Durability)

下面具体介绍一下这4大特性。

(1)原子性(Atomicity)

原子性是针对于一组操作说的,这组操作有原子性,是说这组操作,要么都发生,要么都不发生。

举例:A账户向B账户转100元,A账户金额减少100元、B账户金额增加100元这两个操作要么都发生,要么都不发生,这两个操作具备原子性。

(2)隔离性(Isolation)

事务之间的运行是互不影响的。

举例:事务A改变账户a的金额,事务B读取账户a的金额,在事物A没有提交前,事务B读取的a账户的金额依然是改变前的金额。

(3)一致性(Consistency)

事务的运行不会改变数据库的一致性约束。

举例:现有完整性约束a+b=10,事务的发生改变了a的值,就一定要改变b的值,使事务结束后依然满足a+b=10的约束。

需要注意的是:事务的原子性和隔离性都是保证事务一致性的一种手段。

(4)持久性(Durability)

事务一旦提交了,它所作出的修改会永久地保存在数据库上,即使数据库宕机也不会丢失。

3、事务并发产生的问题

介绍事务的隔离界别前,先介绍一下事务并发会导致异常问题的4种场景,按照问题严重性从大到小排序,分别是脏写(Dirty Write)、脏读(Dirty Read)、不可重复读(Non-Repeatable Read)和幻读(Phantom)。图来自链接1。

(1)脏写(Dirty Write)

如果一个事务修改了另一个未提交的事务修改过的数据,就造成了脏写。脏写示意图

如上图,Session B rollback后,把Session A的修改也给回滚了,Session A发现自己明明做了修改并提交成功,但实际上记录并没有更新,这就是脏写。

(2)脏读(Dirty Read)

如果一个事务读到了另一个未提交事务修改过的数据,就造成了脏读。脏读示意图

如上图,Session A读到了Session B更新的数据“关羽”,然后Session A COMMIT,之后Session B ROLLBACK,那么Session A相当于读取到了一个不存在的数据“关羽”,这就是脏读。

(3)不可重复读(Non-Repeatable Read)

事务A对一条记录不断的查询,事务B对这条记录不断的UPDATE,导致每次事务A查询时得到的记录都是不一样的,就造成了不可重复读。不可重复读示意图

如上图,Session A这边没有做UPDATE,仅是做SELECT,站在Session A的角度每次SELECT时数据应该都是不变的,但由于Session B一直在做UPDATE,Session A发现我明明没有做UPDATE,但每次SELECT的结果就是不一样,这就是不可重复读。

(4)幻读(Phantom)

事务A对一条记录不断的查询,事务B同时向这张表里INSERT符合事务A查询条件的记录,导致每次事务A按照相同的条件查询时得到的记录数目都是不一样的,就造成了幻读。幻读示意图

关于幻读需要注意两点:

(a)上图中,如果不是INSERT而是DELETE,删除记录使按统一条件查询时得到的记录数减小,这样算幻读么?不算!幻读强调后读取到了之前没有读到的记录,所以仅对INSERT成立;

(b)关于幻读和不可重复读的区别:不可重复读是另一个事务同时在UPDATE,而幻读是另一个事务同时在INSERT,因此不可重复读的结果强调每次读取时的记录内容不同,幻读的结果强调每次读取记录时都读取到了之前没读取到的记录,即记录数在递增。

那MySQL针对上述四种并发问题场景的解决方案是什么呢?

MVCC + 锁 + redo\undo?这里需要看完后面的知识后再回过来补充!

4、事务的隔离级别

数据库的服务端肯定存在同一时刻接收到来自不同客户端的访问请求,这就是所谓的事务的并发,跟Java多线程概念上有相同的地方。与Java里锁的机制类似,当有多个事务同时访问(尤其是并发写的情况)一份记录时,应该通过某些机制让这些事务串行执行以此保证事务的隔离性,但服务器又想同一时刻尽可能多地处理访问请求,因此数据库设立了不同的隔离级别,牺牲部分隔离性而获得性能上的提升。

关于隔离级别,SQL标准制定了四种隔离级别,MySQL中对这四种隔离级别的支持可能跟其他数据库有差异。

4.1 SQL标准的四种隔离级别READ UNCOMMITTED 未提交读

READ COMMITTED 已提交读

REPEATABLE READ 可重复读

SERIALIZABLE 可串行化

前面提到过为了追求性能,SQL会牺牲一部分并发安全性,SQL标准中规定,针对不同的隔离级别,并发事务可以发生不同严重程度的问题,具体如下:SQL四种隔离级别

图中隔离级别从上到下依次提高,且由于脏写问题太严重,无论哪种隔离级别都不允许脏写的情况发生,对上图再唠叨一下:READ UNCOMMITTED隔离级别下,可能发⽣脏读、不可重复读和幻读问题;

READ COMMITTED隔离级别下,不可能发生脏读,但可能发⽣不可重复读和幻读;

REPEATABLE READ隔离级别下,不可能发生脏读和不可重复读,但可能发生幻读;

SERIALIZABLE隔离级别下,各种问题都不可能发⽣。

4.2 MySql支持的事务隔离级别

不同数据库对SQL标准规定的四种事务隔离级别支持不一样,Oracle就只⽀持READ COMMITTED和SERIALIZABLE隔离级别,MySQL四种隔离级别均支持,但针对REPEATABLE READ隔离级别,MySQL是不允许幻读产生的。

MySQL默认的事务隔离级别为REPEATABLE READ,可以通过SQL语句修改数据库支持的事务隔离级别:

SET [GLOBAL|SESSION] TRANSACTION ISOLATION LEVEL level;

level的可选值有4个,如下:

level: {

REPEATABLE READ

| READ COMMITTED

| READ UNCOMMITTED

| SERIALIZABLE

}

更多的可以参考链接1。

5、 MVCC机制

MVCC(Multi-Version Concurrency Control,多版本并发控制)是指在使用READ COMMITTED、REPEATABLE READ这两种隔离级别的事务在执行SELECT操作时访问记录的版本链的过程,从而使不同事务的读写操作能够并发执行,提升系统性能。MVCC机制的核心是在做SELECT操作前会生产一个ReadView,READ COMMITTD在每⼀次进⾏普通SELECT操作前都会⽣成⼀个ReadView,⽽REPEATABLE READ只在第⼀次进⾏普通SELECT操作前⽣成⼀个ReadView,之后的查询操作都重复使⽤这个ReadView。

通过ReadView找到符合条件的记录版本(历史版本是由undo⽇志构建的),其实就像是在⽣成ReadView的那个时刻做了⼀次时间静⽌(就像⽤相机拍了⼀个快照),因此利用MVCC机制读取数据又叫快照读,也叫一致性读。

5.1 版本链

事务每次对记录进行修改,都会记录一条undo日志,每条undo日志就是关于这条记录的一个版本,这些undo日志串联起来形成一个链表结构,就称为当前记录的版本链。下面具体讲一下版本链的由来。

对于InnoDB存储引擎来说,聚簇索引的记录都包含两个必要的隐藏列:trx_id:每次一个事务对聚簇索引的记录进行修改时,都会把事务id写给该记录的trx_id隐藏列;

roll_pointer:某条记录的版本链是该记录的历史版本通过单链表的形式连接而成的,roll_pointer相当于链表的尾指针,指向上一条历史版本。

版本链的示意图如下:版本链结构示意图

版本链的头结点就是当前记录的最新版本值。

5.2 ReadView

对于READ COMMITTED、REPEATABLE READ隔离级别来说,需要确定版本链中哪个版本是当前事务可见,为此提出了ReadView的概念,ReadView由下面四部分组成:m_ids:表示在生成ReadView时当前系统中活跃的读取事务的事务id列表;

min_trix_id:表示在生成ReadView时当前系统中活跃的读写事务中最小的事务id,即m_ids列表中最小值;

max_trix_id:表示在生成ReadView时系统中应该分配给下一个事务的id值;

creator_trx_id:表示生成ReadView的事务id,注意:只有对表中的记录做改动时(执行INSERT、DELETE和UPDATE时)才会为事务分配一个事务id,且这个事务id是递增的,否则在一个只读事务中的事务id都默认为0。

基于ReadView,访问某条记录时,按照下面的步骤判断该记录的当前版本对当前事务是否可见:如果被访问的版本的trx_id值与ReadView中的creator_trx_id值相等,说明当前事务正在访问它自己修改过的版本,因此当前版本对当前事务可见;

如果被访问的版本的trx_id值小于ReadView中的min_trix_id,说明该版本对应的事务在生成ReadView之前就已经提交,因此当前版本对当前事务可见;

如果被访问的版本的trx_id值大于ReadView中的max_trix_id,说明该版本对应的事务在生成ReadView后开启,因此当前版本对当前事务不可见;

如果被访问的版本的trx_id值在ReadView中的min_trix_id和max_trix_id之间,就需要判断trx_id值是否在ReadView的m_id列表中,如果在,说明生成ReadView时该版本对应的事务还是活跃的,因此当前版本对当前事务不可见;如果不在,说明生成ReadView时当前版本对应的事务已经提交,当前版本对当前事务可见。

MVCC机制中,一条记录会对应一个版本链,判断当前版本链中哪个版本对当前事务可见时,需要遍历版本链,对版本链上的每一条undo日志(即记录的一个版本)按上面步骤判断版本的可见性,如果某一个版本对当前事务不可见,就顺着版本链找到上一个版本,如果到链表的尾部都不可见,说明当前记录对当前事务完全不可见,查询结果就不包含当前记录。

具体的举例可以参考链接1中小册的内容。

READ COMMITTED隔离级别的事务在每次查询开始前都会生成一个ReadView,而REPEATABLE READ隔离级别的事务仅在第一次读取数据时生成一个ReadView。这样的机制会使REPEATABLE READ隔离级别的事务能够避免不可重复读这个并发问题,即两次查询时记录值相同(与此同时另一个事务在UPDATE该记录)。

6、事务的传播机制

事务传播行为是Spring框架独有的事务增强特性,他不属于事务实际提供方数据库行为,看完spring再回来补吧。

7、事务相关的面试题

(1)什么是事务?

(2)事物的四大特性(ACID)有哪些?

(3)事务的并发场景有哪些?

(4)事务的隔离级别有哪些?

(5)事务的传播行为?

(6)什么是嵌套事务?

很多答案就在文章里,我就不复制粘贴了,看链接3也行。

参考掘金小册​juejin.im陈死狗:Spring 事务传播行为​zhuanlan.zhihu.com4912b2faf420ed8887662a68581dba34.png动力节点:2020年前必须掌握的数据库面试问题​zhuanlan.zhihu.com5c1a7bc43f24d34bd41589bac2b2739a.png

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值