MySQL 各级别事务的实现机制
MySQL 各级别事务的实现机制
在处理cnctp项目已合包裹状态同步的问题时,发现读包裹状态和对包裹状态的更新不在一个事务内,我提出是否会因为消息并发导致状态一致性问题。在和同事讨论的过程中,我们开始论及事务隔离级别与其实现,以及修改隔离级别是否会对其他会话和事务造成影响等问题。由于学而忘习,对这个问题谈得比较模糊,没有以有力的论据说服其他人,因此整理了一下相关文档记录于此。
谈事务的实现前,先说一下InnoDB(MySQL 5.7)中事务相关的锁:
Shared And Exclusive Locks. InnoDB的标准行锁有共享锁(Shared lock)和排它锁(eXclusive lock)两种,S锁通常用于数据读取,X锁用于读和写。持有S锁的事务允许其他事务获得S锁和X锁;持有X锁的事务不允许其他事务获得S锁或X锁(在事务隔离级别低于Repeatable Read时,X锁允许其他事务在读取数据时获得S锁)。S/X锁作为行级别的锁,也可以通过LOCK TABLE ... READ和LOCK TABLE ... WRITE语句作用于表上。
Intention Lock. Intention Lock同样分 Intention Shared Lock(IS)和 Intention eXclusive Lock(IX)。在事务获取数据的S锁前,需要获得对应表的IS锁或IX锁;在事务获取数据的X锁前,需要获得对应表的IX锁。当事务无法获取作为表锁的意向锁时,就不会尝试获取S/X锁。意向锁的存在意义是告诉请求的事务,在表的级别,已经有事务有对表中某些数据持有S/X锁的意向。如果请求的事务需要获取表级锁(如:ALTER TABLE),且该表级锁与已有的意向锁冲突(如表级X锁与IS锁),则无法获取目标的表锁。这样的设计避免事务再去遍历整个表以判断要获取的锁是否与已存在的S/X锁冲突。
Record Lock. 记录锁是作用于索引记录上的锁,即使表不存在索引,InnoDB也会为查询条件创建隐形的聚簇索引,并对该索引记录加上记录锁。根据读写场景不同,记录锁本身可能是S锁或是X锁。
Gap Lock. 区间锁,在条件的索引判断的开区间内(判断条件为=时,开区间为判等的索引记录和上一条数据索引记录之间),不允许没获得该锁的事务插入数据。若使用的索引为unique索引,则区间锁不生效,但unique字段为组合索引字段之一时,区间锁依然生效。区间锁只是锁住了Insert语句的执行,并不影响Update语句,因此大部分时候,区间锁只是在Repeatable Read的事务级别上起作用,直接解决幻读问题。若事务隔离级别从Repeatable Read降级,或是更改 innodb_locks_unsafe_for_binlog 变量的值为1,区间锁失效。
Next-Key Lock. Next-Key Lock是Record Lock + Gap Lock的结合,即锁住索引记录本身和索引记录的前区间,构成一个前开后闭区间。InnoDB默认事务级别为Repeatable Read,即是使用Next-Key Lock避免其它事务引起前区间的幻影行和记录本身的更新。在标准的事务隔离级别中,顾名思义,RR应该是避免脏读和不可重复读,但MySQL通过Next-Key Lock,在RR级别避免了幻读,因此MySQL中,Serializable相对于RR,并不额外避免幻读,也不会真的让所有事务串行化执行,而是在RR的基础上,对纯SELECT语句加上LOCK IN SHARE MODE。这种行为让事务从MVCC实现变成了基于二段锁的读写,但非串行化执行依然可能出现死锁。
Insert Intention Lock. 插入意向锁,这是一种特殊的区间锁,只在Insert语句生成,它阻止事务向已持有区间锁的区间插入数据。前面我们说区间锁可以阻止其他事务向已加锁的区间插入数据,但执行相关WHERE子句时,相关区间只是获得了区间锁,判断能否插入数据,则是通过插入意向锁。两个同区间的插入意向锁互不影响,允许不同键值数据的插入,就像同区间的区间锁互不影响。
MySQL事务隔离级别
事务的ACID性质:
A: Atomicity 原子性。事务内的操作要么全部执行,要么无一执行,事务像原子一样不可细分为多部分分开执行。redo log和undo log是MySQL实现原子性的保障,事务COMMIT后,形成redo log,这些日志负责将事务的变更写入磁盘,同时undo log将记录对应的反向变更,当用户执行ROLLBACK或redo log执行失败时,undo log负责将已执行到磁盘的变更还原。从实现来说,实现事务的原子性即是实现回滚。
C: Consistency 一致性。事务提交后才对其他事务可见,即事务一旦提交,其他事务查看到的结果应该一致,事务一旦回滚,其他事务也只能看到回滚前的状态。一致性着重强调系统的错误恢复能力,MySQL通过double write buffer来实现这一能力。事务提交后,尚未同步到磁盘的数据称为脏页,它们将先被写入double write buffer中,在积累一定数量后,才以顺序访问的方式写入磁盘,以提高写入效率。如果写入过程崩溃,MySQL在重启后可以通过读取double write buffer中的数据进行数据恢复。
I: Isolation 隔离性。多个事务的执行相互独立,执行期间导致的变更彼此不可见。就隔离性而言,事务有四个通用的标准,即Read Uncommitted, Read Committed, Repeatable Read和Serializable. 通用定义中,RU会造成脏读,RC避免了脏读但可能发生不可重复读,RR避免不可重复读却可能发生幻读,S级别要求所有事务串行化执行,即避免了所有数据冲突和死锁。但对于MySQL来说,它的实现机制使得在RR级别,数据已经避免了幻读;而即使在Serializable级别,事务也并非串行化执行。这点在介绍Next-Key Lock时有解释。
D: Durability 持久性。一旦事务提交,所有变更立即写入磁盘进行持久化。MySQL事务提交后,通过redo log写入double write buffer,在将脏页写入磁盘时,根据磁盘的硬件特性,会选择合适的同步操作执行原子性的数据写入,这里的原子性由磁盘保证,我们也不做深究。所以MySQL对持久性的保障,是基于redo log和double write buffer的容错恢复能力,并不是数据提交即写入磁盘。
ACID是性质,Lock和MVCC是实现方式。从隔离性的描述,我们知道事务的隔离级别分为Read Uncommitted, Read Committed, Repeatable Read和Serializable,InnoDB通过MVCC实现了Consistent Nonlocking Read,避免了对大多数读操作加S锁。MVCC的实现虽然能提高读的效率,但锁并不能被完全替代。
Read Uncommitted 读写互不阻塞,不使用锁,而MVCC也不保证多次读取到的数据一定是一致的,即可能读取到别的事务提交的版本。
Read Committed 通过Consistent Nonlocking Read(MVCC)实现读不阻塞写,每次读取对于行的最新快照,所以可能导致不可重复读;对于加锁读和写操作,InnoDB获取行锁,但不获取行前的Gap Lock,因此可能导致幻读
Repeatable Read 同样通过Consistent Nonlocking Read读取数据,但读数据时检查事务版本与首次读取的版本相同,因此避免了不可重复读;对于加锁读和写操作,InnoDB根据使用的索引情况对数据加锁,如果使用unique key,InnoDB仅获取行锁,如果是其他索引,则获取Gap Lock或Next-Key Lock,故而也能避免幻读。
Serializable 在RR的基础上,对所有SELECT语句隐士地加上LOCK IN SHARE MODE,在autocommit开启时,如果其它事务没有改变目标行,则不会阻塞同获得S锁的读操作。
MySQL通过这些手段实现事务,通过这些简略的描述我们不难发现,理论和实践并不是完全一致的,实践为了追求性能,往往会做一些对理论定义的妥协。例如MySQL实现事务的原子性,作为多步执行的事务并不像真正的原子一样,是一个不可切分的单元,只是在MySQL实现了回滚机制后,我们可以认为事务的多步操作是一个整体,像一个原子一样不可分割;而Redis为了追求更高的性能,甚至在设计层面抛弃了回滚机制,只保证所有多步执行的操作在执行时不被别的事务中断,但如果事务中的一个步骤执行出错或者Redis Server崩溃,事务并不会回滚到执行前的状态。这些性能需求,是为了满足现实世界不同的应用场景而产生的,实践通过采用高性能但可能出错的方案,结合各种容错机制,做到性能和理论间的平衡。
对于文章开头提及的一个问题,即一个事务的隔离级别是否会影响链接数据库的其他会话和事务的隔离级别,我们在解释了事务隔离级别的实现后,没有发现有影响的地方。部分更改数据库配置的行为,如改变innodb_locks_unsafe_for_binlog或autocommit的值,可能影响锁的行为,从而影响其他事务,但这些修改均不是在Java程序中完成的,单纯使用Spring的Transactional注解指定访问事务的隔离级别,不能改变这些配置项。
关于一致性的定义,从不同的角度似乎能给出不同的解释,欢迎讨论。
如有误欢迎指出。
MySQL 各级别事务的实现机制的更多相关文章
- MYSQL数据库重点:事务与锁机制
一.事务 一组连续的数据库操作,每一次操作都成功,整个事务就成功,只要有一步出错,整个事务就失败: MySQL事务与存储引擎相关 1.MyISAM:不支持事务,用于只读程序提高性能 2.InnoDB: ...
- mysql中不同事务隔离级别下数据的显示效果--转载
事务是一组原子性的SQL查询语句,也可以被看做一个工作单元.如果数据库引擎能够成功地对数据库应用所有的查询语句,它就会执行所有查询,如果任何一条查询语句因为崩溃或其他原因而无法执行,那么所有的语句就都 ...
- 浅谈mysql中不同事务隔离级别下数据的显示效果
事务的概念 事 务是一组原子性的SQL查询语句,也可以被看做一个工作单元.如果数据库引擎能够成功地对数据库应用所有的查询语句,它就会执行所有查询,如果任何一条查 询语句因为崩溃或其他原因而无法执行,那 ...
- MySQL 事务与锁机制
下表展示了本人安装的MariaDB(10.1.19,MySQL的分支)所支持的所有存储引擎概况,其中支持事务的有InnoDB.SEQUENCE,另外InnoDB还支持XA事务,MyISAM不支持事务. ...
- 谈谈MySQL支持的事务隔离级别,以及悲观锁和乐观锁的原理和应用场景?
在日常开发中,尤其是业务开发,少不了利用 Java 对数据库进行基本的增删改查等数据操作,这也是 Java 工程师的必备技能之一.做好数据操作,不仅仅需要对 Java 语言相关框架的掌握,更需要对各种 ...
- MySQL事物(一)事务隔离级别和事物并发冲突
数据库的操作通常为写和读,就是所说的CRUD:增加(Create).读取(Read).更新(Update)和删除(Delete).事务就是一件完整要做的事情.事务是恢复和并发控制的基本单位.事务必须始 ...
- Mysql 四种事务隔离介绍以及锁机制
还有很多不太懂,这里收集几份大佬文章“飞机票”,待我整理好了,再好好写一篇文章吧. MySQL的四种事务隔离级别 https://www.cnblogs.com/huanongying/p/70215 ...
- 深入理解Mysql索与事务隔离级别
1. 概述 1.1 定义 锁是计算机协调多个进程或线程并发访问某一资源的机制. 在数据库中,除了传统的计算资源(如CPU.RAM.I/O等)的争用以外,数据也是一种供需要用户共享的资源.如何保证数据并 ...
- 面试必问的MySQL锁与事务隔离级别
之前多篇文章从mysql的底层结构分析.sql语句的分析器以及sql从优化底层分析, 还有工作中常用的sql优化小知识点.面试各大互联网公司必问的mysql锁和事务隔离级别,这篇文章给你打神助攻,一飞 ...
随机推荐
- hdu 1372 骑士从起点走到终点的步数 (BFS)
给出起点和终点 求骑士从起点走到终点所需要的步数 Sample Inpute2 e4 //起点 终点a1 b2b2 c3a1 h8a1 h7h8 a1b1 c3f6 f6 Sample OutputT ...
- 关于Spring MVC 中地址栏访问 /WEB-INF下的.jsp
WEB-INF是对资源的保护,直接在地址栏访问WEB-INF目录下的页面,会显示404,关于为什么要把页面放在WEB-INF下,可以自行百度 在这里我是用SpringMVC 对WEB-INF目录下的页 ...
- POJ 3190 Stall Reservations【贪心】
POJ 3190 题意: 一些奶牛要在指定的时间内挤牛奶,而一个机器只能同时对一个奶牛工作.给你每头奶牛的指定时间的区间(闭区间),问你最小需要多少机器.思路:先按奶牛要求的时间起始点进行从小到大排序 ...
- java:根据利润表计算奖金所得
代码实现: public class Hello { public static void main(String srgs[]) { Lirun(100); Lirun2(100); } publi ...
- Codeforces Round #468 (Div. 2, based on Technocup 2018 Final Round)
A.B都是暴力搞一搞. A: #include<bits/stdc++.h> #define fi first #define se second #define mk make_pair ...
- BZOJ2843 极地旅行社 LCT
欢迎访问~原文出处——博客园-zhouzhendong 去博客园看该题解 题目传送门 - BZOJ2843 题意概括 有n座岛 每座岛上的企鹅数量虽然会有所改变,但是始终在[0, 1000]之间.你的 ...
- 推箱子 HDU1254 (bfs)
较难的bfs 有两种方法做 一种双重bfs: 主bfs是箱子 还要通过dfs判断人是否能到箱子后面 用inmap函数的好处.. 箱子要用三位数组来标记 因为箱子可以回到原来到过的地方 因为推的 ...
- 【Java】 剑指offer(40) 最小的k个数
本文参考自<剑指offer>一书,代码采用Java语言. 更多:<剑指Offer>Java实现合集 题目 输入n个整数,找出其中最小的k个数.例如输入4.5.1.6.2.7 ...
- java技术第二次作业
(一)学习总结 1.什么是构造方法?什么是构造方法的重载? 构造方法是用于对对象初始化的方法,当新对象被创建的时候,构造函数会被调用. 每一个类都有构造函数.在程序员没有给类提供构造函数的情况下,Ja ...
- IntelliJ IDEA(九) :插件(转)
最近项目比较忙,很久没有更新IDEA系列了,今天介绍一下IDEA的一些炫酷的插件,IDEA强大的插件库,不仅能给我们带来一些开发的便捷,还能体现我们的与众不同. 0|11.插件的安装 打开settin ...