mysql innodb 事务_MySQL学习笔记之InnoDB事务实现
我们都知道事务的几种性质,数据库为了维护这些性质,尤其是一致性和隔离性,一般使用加锁这种方式。
同时数据库又是个高并发的应用,同一时间会有大量的并发访问,如果加锁过度,会极大的降低并发处理能力。
所以对于加锁的处理,可以说就是数据库对于事务处理的精髓所在。
这里通过分析MySQL中InnoDB引擎的事务处理来理解事务、锁、隔离级别、MVCC、Next-Key Locks等概念。
事务
概念
事务是指满足ACID特性的一组操作,可以通过 Commit 提交一个事务,也可以使用 Rollback 进行回滚。
ACID
原子性(Atomicity)
事务被视为不可分割的最小单元,事务的所有操作要么全部提交成功,要么全部失败回滚。
回滚可以用回滚日志来实现,回滚日志记录着事务所执行的修改操作,在回滚时反向执行这些修改操作即可。
一致性(Consistency)
数据库在事务执行前后都保持一致性状态。在一致性状态下,所有事务对一个数据的读取结果都是相同的。
隔离性(Isolation)
一个事务所做的修改在最终提交以前,对其他事务是不可见的。
持久性(Durability)
一旦事务提交,则其所做的修改将会永远保存到数据库中。即使系统发生崩溃,事务执行的结果也不能丢。
使用重做日志来保证持久性。
ACID 之间的关系
事务的 ACID 特性概念简单,但不是很好理解,主要是因为这几个特性不是一种平级关系:
只有满足一致性,事务的执行结果才是正确的。
在无并发的情况下,事务串行执行,隔离性一定能够满足。此时只要能满足原子性,就
一定能满足一致性。 在并发的情况下,多个事务并行执行,事务不仅要满足原子性,还需要满足隔离性,才能满足一致性。
事务满足持久化是为了能应对数据库崩溃的情况。
隔离级别
未提交读(READ UNCOMMITTED)
事务中的修改,即使没有提交,对其他事务也是可见的。
提交读(READ COMMITTED)
一个事务只能读取已经提交的事务所做的修改。换句话说,一个事务所做的修改在提交之前对其他事务是不可见的。
可重复读(REPEATABLE READ)
保证在同一个事务中多次读取同样数据的结果是一样的。
可串行化(SERIALIZABLE)
强制事务串行执行。
需要加锁实现,而其它隔离级别通常不需要。
隔离级别
脏读
不可重复读
幻影读
未提交读
√
√
√
提交读
×
√
√
可重复读
×
×
√
可串行化
×
×
×
InnoDB 可重复读的实现
在 InnoDB 中,SELECT、UPDATE、DELETE 操作的不可重复读问题可以通过 MVCC 来解决,但是 INSERT 操作的不可重复读问题(幻读问题)需要通过 MVCC + Next-Key Locks 来解决。
不可重复读和幻读
不可重复读
T2 读取一个数据,T1 对该数据做了修改。如果 T2 再次读取这个数据,此时读取的结果和第一次读取的结果不同。
幻读
T1 读取某个范围的数据,T2 在这个范围内插入新的数据,T1 再次读取这个范围的数据,此时读取的结果和和第一次读取的结果不同。
可以看出,幻读问题是不可重复读问题的子集。
不可重复读和幻读的区别
很多人容易搞混不可重复读和幻读,确实这两者有些相似。但不可重复读重点在于update和delete,而幻读的重点在于insert。
如果使用锁机制来实现这两种隔离级别,在可重复读中,该sql第一次读取到数据后,就将这些数据加锁,其它事务无法修改这些数据,就可以实现可重复读了。但这种方法却无法锁住insert的数据,所以当事务A先前读取了数据,或者修改了全部数据,事务B还是可以insert数据提交,这时事务A就会发现莫名其妙多了一条之前没有的数据,这就是幻读,不能通过行锁来避免。需要Serializable隔离级别 ,读用读锁,写用写锁,读锁和写锁互斥,这么做可以有效的避免幻读、不可重复读、脏读等问题,但会极大的降低数据库的并发能力。
所以说不可重复读和幻读最大的区别,就在于如何通过锁机制来解决他们产生的问题。
上文说的,是使用悲观锁机制,但是MySQL、ORACLE、PostgreSQL等成熟的数据库,出于性能考虑,都是使用了以乐观锁为理论基础的MVCC(多版本并发控制)。
在 InnoDB 中,SELECT、UPDATE、DELETE 操作的不可重复读问题可以通过 MVCC 来解决,但是 INSERT 操作的不可重复读问题(幻读问题)需要通过 MVCC + Next-Key Locks 来解决。
多版本并发控制(MVCC)
多版本并发控制(Multi-Version Concurrency Control, MVCC)是 MySQL 的 InnoDB 存储引擎实现隔离级别的一种具体方式,用于实现提交读和可重复读这两种隔离级别。而未提交读隔离级别总是读取最新的数据行,无需使用 MVCC。可串行化隔离级别需要对所有读取的行都加锁,单纯使用 MVCC 无法实现。
基础概念
版本号
系统版本号:是一个递增的数字,每开始一个新的事务,系统版本号就会自动递增。
事务版本号:事务开始时的系统版本号。
隐藏的列
MVCC 在每行记录后面都保存着两个隐藏的列,用来存储两个版本号:
创建版本号:指示创建一个数据行的快照时的系统版本号;
删除版本号:如果该快照的删除版本号大于当前事务版本号表示该快照有效,否则表示该快照已经被删除了。
Undo 日志
MVCC 使用到的快照存储在 Undo 日志中,该日志通过回滚指针把一个数据行(Record)的所有快照连接起来。
实现过程
以下实现过程针对可重复读隔离级别。
当开始一个事务时,该事务的版本号肯定大于当前所有数据行快照的创建版本号,理解这一点很关键。数据行快照的创建版本号是创建数据行快照时的系统版本号,系统版本号随着创建事务而递增,因此新创建一个事务时,这个事务的系统版本号比之前的系统版本号都大,也就是比所有数据行快照的创建版本号都大。
SELECT
多个事务必须读取到同一个数据行的快照,并且这个快照是距离现在最近的一个有效快照。但是也有例外,如果有一个事务正在修改该数据行,那么它可以读取事务本身所做的修改,而不用和其它事务的读取结果一致。
把没有对一个数据行做修改的事务称为 T,T 所要读取的数据行快照的创建版本号必须小于等于 T 的版本号,因为如果大于 T 的版本号,那么表示该数据行快照是其它事务的最新修改,因此不能去读取它。除此之外,T 所要读取的数据行快照的删除版本号必须是未定义或者大于 T 的版本号,因为如果小于等于 T 的版本号,那么表示该数据行快照是已经被删除的,不应该去读取它。
INSERT
将当前系统版本号作为数据行快照的创建版本号。
DELETE
将当前系统版本号作为数据行快照的删除版本号。
UPDATE
将当前系统版本号作为更新前的数据行快照的删除版本号,并将当前系统版本号作为更新后的数据行快照的创建版本号。可以理解为先执行 DELETE 后执行 INSERT。
快照读与当前读
在可重复读级别中,通过MVCC机制,虽然让数据变得可重复读,但我们读到的数据可能是历史数据,是不及时的数据,不是数据库当前的数据!这在一些对于数据的时效特别敏感的业务中,就很可能出问题。
对于这种读取历史数据的方式,我们叫它快照读 (snapshot read),而读取数据库当前版本数据的方式,叫当前读 (current read)。很显然,在MVCC中:
快照读:就是select。
select * from table ….;
当前读:特殊的读操作,插入/更新/删除操作,属于当前读,处理的都是当前的数据,需要加锁。
select * from table where ? lock in share mode;
select * from table where ? for update;
insert;
update ;
delete;
事务的隔离级别实际上都是定义了当前读的级别,MySQL为了减少锁处理(包括等待其它锁)的时间,提升并发能力,引入了快照读的概念,使得select不用加锁。而update、insert这些“当前读”,就需要另外的模块来解决了。
Next-Key Locks
Next-Key Locks 是 MySQL 的 InnoDB 存储引擎的一种锁实现。
MVCC 不能解决幻影读问题,Next-Key Locks 就是为了解决这个问题而存在的。在可重复读(REPEATABLE READ)隔离级别下,使用 MVCC + Next-Key Locks 可以解决幻读问题。
Record Locks
锁定一个记录上的索引,而不是记录本身。
如果表没有设置索引,InnoDB 会自动在主键上创建隐藏的聚簇索引,因此 Record Locks 依然可以使用。
Gap Locks
锁定索引之间的间隙,但是不包含索引本身。例如当一个事务执行以下语句,其它事务就不能在 t.c 中插入 15。
SELECT c FROM t WHERE c BETWEEN 10 and 20 FOR UPDATE;
Next-Key Locks
它是 Record Locks 和 Gap Locks 的结合,不仅锁定一个记录上的索引,也锁定索引之间的间隙。例如一个索引包含以下值:10, 11, 13, and 20,那么就需要锁定以下区间:
(-∞, 10]
(10, 11]
(11, 13]
(13, 20]
(20, +∞)
示例讲解
MySQL是这么实现的:
在class_teacher这张表中,teacher_id是个索引,那么它就会维护一套B+树的数据关系,为了简化,我们用链表结构来表达(实际上是个树形结构,但原理相同)
如图所示,InnoDB使用的是聚集索引,teacher_id身为二级索引,就要维护一个索引字段和主键id的树状结构(这里用链表形式表现),并保持顺序排列。
Innodb将这段数据分成几个个区间
(negative infinity, 5],
(5,30],
(30,positive infinity);
update class_teacher set class_name=‘初三四班’ where teacher_id=30;不仅用行锁,锁住了相应的数据行;同时也在两边的区间,(5,30]和(30,positive infinity),都加入了gap锁。这样事务B就无法在这个两个区间insert进新数据。
受限于这种实现方式,Innodb很多时候会锁住不需要锁的区间。如下所示:
事务A
事务B
事务C
begin;
begin;
begin;
select id,class_name,teacher_id from class_teacher;
表字段:id class_name teacher_id
update class_teacher set class_name='初一一班' where teacher_id=20;
insert into class_teacher values (null,'初三五班',10);waiting .....
insert into class_teacher values (null,'初三五班',40);
commit;
事务A commit之后,这条语句才插入成功
commit;
commit;
update的teacher_id=20是在(5,30]区间,即使没有修改任何数据,Innodb也会在这个区间加gap锁,而其它区间不会影响,事务C正常插入。
如果使用的是没有索引的字段,比如update class_teacher set teacher_id=7 where class_name=‘初三八班(即使没有匹配到任何数据)’,那么会给全表加入gap锁。同时,它不能像上文中行锁一样经过MySQL Server过滤自动解除不满足条件的锁,因为没有索引,则这些字段也就没有排序,也就没有区间。除非该事务提交,否则其它事务无法插入任何数据。
行锁防止别的事务修改或删除,GAP锁防止别的事务新增,行锁和GAP锁结合形成的的Next-Key锁共同解决了RR级别在写数据时的幻读问题。
欢迎关注我的公众号:荒古传说
本文作者: 荒古
本文链接: https://haxianhe.com/2019/08/...
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 3.0 许可协议。转载请注明出处!
mysql innodb 事务_MySQL学习笔记之InnoDB事务实现相关推荐
- mysql 临时表 事务_MySQL学习笔记十:游标/动态SQL/临时表/事务
逆天十三少 发表于:2020-11-12 08:12 阅读: 90次 这篇教程主要讲解了MySQL学习笔记十:游标/动态SQL/临时表/事务,并附有相关的代码样列,我觉得非常有帮助,现在分享出来大家一 ...
- 《SQL高级应用和数据仓库基础(MySQL版)》学习笔记 ·006【事务】
文章目录 一.事务 1.事务介绍 2.事务组成 3.事务结束 4.MySQL事务处理 5.保存点与回档 6.事务隔离级别 7.脏读.不可重复读.幻读 一.事务 1.事务介绍 事务,也称工作单元,是由一 ...
- mysql分页概念_MySQL学习笔记之数据定义表约束,分页方法总结
本文实例讲述了MySQL学习笔记之数据定义表约束,分页方法.分享给大家供大家参考,具体如下: 1. primary key 主键 特点:主键是用于唯一标识一条记录的约束,一张表最多只能有一个主键,不能 ...
- mysql 分组链接_MySQL学习笔记(四)——分组函数,分组查询,链接查询
MySQL学习笔记(四)--分组函数,分组查询,链接查询 做者:就叫易易好了 日期:2020/11/18 1.分组函数 功能:用做统计使用,又称为聚合函数或统计函数mysql 分类:web sum函数 ...
- mysql innodb 索引结构_Mysql 学习笔记:InnoDB 索引结构浅析
索引是检索图书资料的一种工具,把书刊中的内容或项目分类摘录,注明页数,按一定次序排列. 针对不同的数据存储结构有不同的数据查找方式. 1. 数据结构 1.1 B树 B树又名平衡多路查找树,主要用于文件 ...
- mysql三大范式_MySQL学习笔记
1.数据库结构设计 1.总-总体流程图 2.分-[提取属性]业务分析 评价的属性:{用户,课程主标题,内容,综合评分,内容实用,简洁易懂,逻辑分析,发布时间} 问答评论属性:{类型,标题,内容,关联章 ...
- mysql数据类型选择_MySQL学习笔记(二):MySQL数据类型汇总及选择参考
本文主要介绍了MySQL 的常用数据类型,以及实际应用时如何选择合适的类型. ******几个通用的简单原则:******* 1. 更小的通常更好.但是要确保没有低估需要存储的值的范围,如果无法确定哪 ...
- Mysql成语数据库_mysql学习笔记
今天开始学习mysql数据库了,也是第一次在这里写技术笔记,相信好记忆不如烂笔头,就以此来见证自己的成长吧! 首先安装mysql服务器的过程这里就不啰嗦啦,相信大家都懂的,直接进入主题吧,欢迎那些也是 ...
- MySQL编程技巧_MySQL学习笔记---MySQL开发技巧
SQL语句分类 DDL:数据定义语言 --- CREATE.ALTER.DROP.TRUNCATE TPL:事务处理语言 --- COMMIT.ROLLBACK.SAVEPOINT.SET TRANS ...
最新文章
- Java调用库文件(JNI)(android对此有所改变)
- php 验证码小写,PHP实现验证码
- ubuntu sever 命令行 使用 u盘
- springboot+mybatis+SpringSecurity 实现用户角色数据库管理(一)
- 使用MASM02 - Win32汇编语言010
- vim查找忽略大小写
- Matlab形态学图像处理:二值图像分割 标记连通区域和重心位置 删除连通区域
- JuliaPro安装与使用
- python selenium加载插件
- python 绘制简易折线图
- virtual memory exhausted: Cannot allocate memory Linux虚拟内存不足,扩展虚拟内存的解决办法
- Android基础 --- Widget
- java反编译class文件,一种最最简单的方法
- IIS7管理器设置网站首页
- 将数字金额转换为对应的中文大写金额
- java 数字转大写中文_java如何实现,把数字转换成中文的大写数字?
- Qt2D游戏开发引擎QtGameEngine使用入门10——游戏中如何响应用户输入(鼠标/键盘输入)
- 图像一阶导数和二阶导
- Python_封装案例(小明爱跑步)
- 数据仓库——数据仓库基础