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 各级别事务的实现机制的更多相关文章

  1. MYSQL数据库重点:事务与锁机制

    一.事务 一组连续的数据库操作,每一次操作都成功,整个事务就成功,只要有一步出错,整个事务就失败: MySQL事务与存储引擎相关 1.MyISAM:不支持事务,用于只读程序提高性能 2.InnoDB: ...

  2. mysql中不同事务隔离级别下数据的显示效果--转载

    事务是一组原子性的SQL查询语句,也可以被看做一个工作单元.如果数据库引擎能够成功地对数据库应用所有的查询语句,它就会执行所有查询,如果任何一条查询语句因为崩溃或其他原因而无法执行,那么所有的语句就都 ...

  3. 浅谈mysql中不同事务隔离级别下数据的显示效果

    事务的概念 事 务是一组原子性的SQL查询语句,也可以被看做一个工作单元.如果数据库引擎能够成功地对数据库应用所有的查询语句,它就会执行所有查询,如果任何一条查 询语句因为崩溃或其他原因而无法执行,那 ...

  4. MySQL 事务与锁机制

    下表展示了本人安装的MariaDB(10.1.19,MySQL的分支)所支持的所有存储引擎概况,其中支持事务的有InnoDB.SEQUENCE,另外InnoDB还支持XA事务,MyISAM不支持事务. ...

  5. 谈谈MySQL支持的事务隔离级别,以及悲观锁和乐观锁的原理和应用场景?

    在日常开发中,尤其是业务开发,少不了利用 Java 对数据库进行基本的增删改查等数据操作,这也是 Java 工程师的必备技能之一.做好数据操作,不仅仅需要对 Java 语言相关框架的掌握,更需要对各种 ...

  6. MySQL事物(一)事务隔离级别和事物并发冲突

    数据库的操作通常为写和读,就是所说的CRUD:增加(Create).读取(Read).更新(Update)和删除(Delete).事务就是一件完整要做的事情.事务是恢复和并发控制的基本单位.事务必须始 ...

  7. Mysql 四种事务隔离介绍以及锁机制

    还有很多不太懂,这里收集几份大佬文章“飞机票”,待我整理好了,再好好写一篇文章吧. MySQL的四种事务隔离级别 https://www.cnblogs.com/huanongying/p/70215 ...

  8. 深入理解Mysql索与事务隔离级别

    1. 概述 1.1 定义 锁是计算机协调多个进程或线程并发访问某一资源的机制. 在数据库中,除了传统的计算资源(如CPU.RAM.I/O等)的争用以外,数据也是一种供需要用户共享的资源.如何保证数据并 ...

  9. 面试必问的MySQL锁与事务隔离级别

    之前多篇文章从mysql的底层结构分析.sql语句的分析器以及sql从优化底层分析, 还有工作中常用的sql优化小知识点.面试各大互联网公司必问的mysql锁和事务隔离级别,这篇文章给你打神助攻,一飞 ...

随机推荐

  1. jsp统计页面访问量和刷访问量的简单使用

    ~Jsp可以进行简单的页面访问量统计,当然也可以使用Jsp刷访问量. 1:第一种使用全局变量<%! int i=0;%>进行页面的访问量统计,只有新打开一个浏览器才可以进行统计. 2:第二 ...

  2. VS2008中开发智能设备程序的一些总结收藏

    结合前几日开发的<全国大坝基础数据库采集端>中的PDA程序开发过程,对VS2008开发智能设备上的程序做个小总结. 1         程序结构 程序中包括四个部分: 1. 系统配置 这个 ...

  3. python全栈开发day47-jqurey

    一.昨日内容回顾 二.今日内容总结 1.jquery的介绍 1).为什么要用jquery? # window.onload 事件有事件覆盖的问题,因此只能写一个事件. # 代码容错性差 # 浏览器兼容 ...

  4. BZOJ1036 [ZJOI2008]树的统计Count 树链剖分

    欢迎访问~原文出处——博客园-zhouzhendong 去博客园看该题解 题目传送门 - BZOJ1036 题意概括 一个树,每个节点有一个权值.3种操作. 1:修改某一个节点的权值. 2:询问某两个 ...

  5. 4.Django|ORM模型层

    ORM简介 MVC或者MVC框架中包括一个重要的部分,就是ORM,它实现了数据模型与数据库的解耦,即数据模型的设计不需要依赖于特定的数据库,通过简单的配置就可以轻松更换数据库,这极大的减轻了开发人员的 ...

  6. EXIST子查询

    已知关系模式:S(Sno,Sname,Sclass),C(Cno,Cname,Cteacher),SC(Sno,Cno,Scgrade).其中,S为学生关系:Sno学号, Sname姓名,Sclass ...

  7. dict 知识汇总

    增: 1. copy 浅复制 2. setdefault (有就查询,没有就添加): 删: 3. clear:清除 4. pop :删除指定键值对 5. popitem :  随机删除一组键值对 改: ...

  8. Linux使用tcpdump命令抓包并使用wireshark分析

    Linux使用tcpdump命令抓包并使用wireshark分析 介绍 有时分析客户端和服务器网络交互的问题时,为了查找问题,需要分别在客户端和服务器上抓包,我们的客户端一般是windows上的,抓包 ...

  9. 条件随机场之CRF++源码详解-训练

    上篇的CRF++源码阅读中, 我们看到CRF++如何处理样本以及如何构造特征.本篇文章将继续探讨CRF++的源码,并且本篇文章将是整个系列的重点,会介绍条件随机场中如何构造无向图.前向后向算法.如何计 ...

  10. 由自定义事件到vue数据响应

    前言 除了大家经常提到的自定义事件之外,浏览器本身也支持我们自定义事件,我们常说的自定义事件一般用于项目中的一些通知机制.最近正好看到了这部分,就一起看了下自定义事件不同的实现,以及vue数据响应的基 ...