MySQL InnoDB事务模型
(一)MySQL InnoDB事务模型
(二)MySQL InnoDB锁模型
(三)MySQL InnoDB非锁定一致性读与锁定读
(四)MySQL InnoDB锁类型及幻象读问题
(五)MySQL InnoDB中各类语句加锁方式
(六)事务的提交与回滚极死锁检测、处理和预防
InnoDB四种事务隔离级别
事务的ACID特性:原子性、一致性、隔离性、持久性。这部分不多说了,任何一本讲数据库理论的书籍里边都会有讲。MySQL InnoDB通过锁来实现事务的一致性和隔离性,共实现了四种事务隔离级别:
· READ UNCOMMITTED
· READ COMMITTED
· REPEATABLE READ
· SERIALIZABLE
(1)READ UNCOMMITTED:某个session中的事务可以看到其他session的事务中尚未提交的更改,而该更改可能回滚,也即会出现”脏读“;(2)READ COMMITTED:某个session中的事务只可以看到其他session的事务中已经提交的更改,不会出现”脏读“,但一个事务对同一对象的两次查询结果可能出现不一致,也即会出现“不可重复读”;(3)REPEATABLE READ:某个session中的事务不能查询到其他session的事务中未提交和已经提交的更改,不会出现”不可重复读”,但期间,其他事务却可能对数据进行更改并提交,而这些更改对前一个事务中的INSERT/UPDATE/DELETE等语句是可见的,因此可能出现”更新丢失“,另外,虽然SELECT不到,但对其进行更改操作时却真实的存在,就好像幻象一样,且更改过后可以被同一事物中的SELECT看到,也即”幻像读“(其实”不可重复读”也可理解为”幻像读“,因为同一事务中前后两次读取到的结果不一样,看个人怎么理解了,这些叫法只是一个名字而已);(4)SERIALIZABLE:使事务串行化执行,解决上述问题。
为了更易于理解四种事务隔离级别,还是以具体的示例来说明会比较形象。但在说明前有必要了解一些前置内容。
隐式/显式事务
若MySQL变量autocommit设置为1,也即开启了自动提交特性,则事务隐式的开启/关闭,每条SQL自成一个事务,执行后事务自动提交。此时,也可通过START TRANSACTION/BEGIN显式的开启事务,而通过COMMIT/ROLLBACK显式结束事务;若变量autocommit设置为0,也即关闭了自动提交特性,则连接的session会始终保持开启一个事物,可通过COMMIT/ROLLBACK显式的结束事务并开启新的事务。锁在事务内的每条SQL语句中逐个获取而在事务结束后被全部释放。
查看/设置隔离级别
分别查看全局和session的事务隔离级别
SELECT @@global.tx_isolation, @@session.tx_isolation;
分别设置全局和session级别的事物隔离级别。需注意的是若指定了GLOBAL关键字则会对所有后续的session生效而对当前session无效。若指定了SESSION关键字则会对当前session中的后续的事务生效而对当前事务无效。若没有指定任何关键字则仅对当前session中接下来的一个事务生效。
SET GLOBAL/SESSION TRANSACTION ISOLATION LEVEL SERIALIZABLE;
SET GLOBA/SESSIONL TX_ISOLATION='REPEATABLE-READ';
InnoDB事务隔离级别测试
创建测试表
USE test;
CREATE TABLE t1 (i INT, PRIMARY KEY (i)) ENGINE = InnoDB;
开启两个session,SESSION A和SESSIONB。InnoDB默认事务隔离级别为REPEATABLE READ。我们先从最低的隔离级别开始READ UNCOMMITTED测试。
SESSION A中的事务读取到了SESSION B中的事务插入但未提交的数据,最终SESSION B的事务回滚了插入操作。SESSION A读取到的为“脏数据”。
结束之前的事务,事务隔离级别重新调整为READ-COMMITTED,开启新的事务。
在SESSION B中的事务提交前SESSION A中的事务读取不到SESSION B插入的记录。只有在SESSION B中的事务提交了更新后SESSION A才能读到该条记录,这避免了脏读。但是,可能出现“不可重复”读,也即同一个事务中两次读到的结果不一致,比如上例中,SESSION B中的事务后来又插入了一条记录并提交了事务,此时SESSION A中的事务再次查询时读到了不一样的内容。某些情形下这并没有什么问题,但一些特定应用场景可能不允许两次读到的结果不一致,可通过设置事务隔离级别为REPEATABLE READ来避免该问题。
结束之前的事务,事务隔离级别重新调整为REPEATABLE-READ,开启新的事务。
即便是SESSION B的事务期间插入数据并提交了事务,SESSION A的同一个事务中前后两次读到的数据还是一致的,避免了“不可重复”读问题。但是,当在SESSION A中插入i=3的记录时确报主键重复的错误,同一事物中SELECT时并没有该条记录,但现在插入时却发生冲突,这条记录就像”幻影“一样存在。同样,当UPDATE了表中的记录后,发现除了原有记录发生改变外还多了一条新的记录,这条记录也像”幻影“一样神奇的存在了。接上表:
结束之前的事务,事务隔离级别重新调整为SERIALIZABLE,开启新的事务。
该隔离级别同REPEATABLE-READ但是会在autocommit=0也即显示的开启/关闭事务时隐式的讲SELECT语句转换为SELECT...LOCK IN SHARE MODE,也即加一把S锁,会阻塞其他SESSION中的事务获取记录上的X锁。但S锁与S锁是兼容的,因此不会阻塞读请求。同理,其他SESSION中的更新操作由于要在记录上加X锁因而会阻塞序列化隔离级别的SESSION的事务中的读操作。这样可以有效避免”幻象读“问题。但若是autocommit=1且不明确的开启/关闭事务时则SELECT不会进行转换也就不会获取S锁,从而不会阻塞其更新操作,也不会被其他更新操作阻塞。
InnoDB中的锁为两阶段锁,事务中分别获取,事务结束时同时释放。默认为REPEATABLE READ事务隔离级别,使用行锁、非锁定一致性读。锁信息开销极小不需要锁升级
。