MySQL事务原理分析以及隔离与锁
事务
事务的前提是有并发的连接访问,没有并发的连接就没有讨论事务的必要。
事务时用户定义的一系列操作,这些操作要么都做,要么都不做,是一个不可分割的单位。
学习MySQL事务时可以和redis的事务进行对比
事务控制语句
-- 显示开启事务
START TRANSACTION | BEGIN
-- 提交事务,并使得已对数据库做的所有修改持久化
COMMIT
-- 回滚事务,结束用户的事务,并撤销正在进行的所有未提交的修改
ROLLBACK
-- 创建一个保存点,一个事务可以有多个保存点
SAVEPOINT identifier
-- 删除一个保存点
RELEASE SAVEPOINT identifier
-- 事务回滚到保存点
ROLLBACK TO [SAVEPOINT] identifier
ACID特性
原子性
事务操作要么都做(提交),要么都不做(回滚);事务是访问并更新数据库各种数据项的一个程 序执行单元,是不可分割的工作单位;通过 undolog 来实现回滚操作。undolog 记录的是事务每步 具体操作,当回滚时,回放事务具体操作的逆运算; 事务包含的全部操作是一个不可分割的整体,要么全部执行,要么全部不执行。
一致性
数据库完整约束实现数据上的一致性
同时还有逻辑上的一致性
隔离性
防止多个并发事务交叉执行导致数据不一致,通过锁和mvcc实现,但是隔离性会破坏一定的一致性。
持久性
事务一旦完成,要将数据所做的变更记录下来,包括数据存储和多副本的网络备份; 事务提交后,事务 DML 操作将会持久化(写入 redolog 磁盘文件 哪一个页 页偏移值 具体数据); 即使发生宕机等故障,数据库也能将数据恢复。redolog 记录的是物理日志。
隔离级别
read uncommitted(读未提交)
读:不做任何处理
谢:自动加x锁
read committed(读已提交)
读:mvcc,读取最新版本行数据
写:自动加x锁
repeatedable read(可重复读)
读:mvcc,读取事务开始前版本的行数据
写:自动加x锁
serializable(可串行化)
读:自动加s锁
写:自动加x锁
命令
-- 设置隔离级别
SET [GLOBAL | SESSION] TRANSACTION ISOLATION LEVEL REPEATABLE READ;
-- 或者采用下面的方式设置隔离级别
SET @@tx_isolation = 'REPEATABLE READ';SET @@global.tx_isolation = 'REPEATABLE READ';
-- 查看全局隔离级别
SELECT @@global.tx_isolation;
-- 查看当前会话隔离级别
SELECT @@session.tx_isolation;SELECT @@tx_isolation;-- 手动给读加 S 锁
SELECT ... LOCK IN SHARE MODE;
-- 手动给读加 X 锁
SELECT ... FOR UPDATE;
-- 查看当前锁信息
SELECT * FROM information_schema.innodb_locks;
不同隔离级别并发异常
脏读
一个事务读到另一个未提交事务修改的数据
这种情况出现在读未提交隔离级别出现。
不可重复读
一个事务内两次读取同一个数据不一样
出现在读未提交和读已提交。
幻读
一个事务内两次读取同一个范围内的记录得到的结果集不一样
这个需要解释一下,即在两个连接事务处于可重复读隔离级别中,一个事务提交了id为4的数据,但另一个事务中无法select到这个数据,于是在insert的时候发现已经有了这个数据的情况。
出现在读未提交,读已提交和可重复读下。
MVCC
多版本并发控制;用来实现一致性的非锁定读;非锁定读是指不需要等待访问的行上X锁的释放; 在 read committed 和 repeatable read 下,innodb 使用 MVCC;然后对于快照数据的定义不同; 在 read committed 隔离级别下,对于快照数据总是读取被锁定行的最新一份快照数据;而在 repeatable read 隔离级别下,对于快照数据总是读取事务开始时的行数据版本。
思考:为什么读取快照数据不需要上锁?
因为没有事务需要对历史的数据进行修改操作。
read view
在 read committed 和 read repeatable 隔离级别下,MVCC 采用 read view 来实现的,它们的区别 在于创建 read view 时机不同:
read committed 隔离级别会在事务中每个 select 都会生成一个新的 read view,也意味着 在同一个事务多次读取同一条数据可能出现数据不一致;因为在多次读取期间可能有其他事 务修改了该条记录,并提交了。
read repeatable 隔离级别是启动事务时生成一个 read view,在整个事务读取数据都才使用这 个 read view,这样保证了在事务期间读到的数据都是事务启动前的记录;
构成
m_ids :创建 read view 时,当前数据库活跃事务(开启未提交的事务)的事务 id 列表;
min_trx_id :创建 read view 时, m_ids 中的最小事务 id;
max_trx_id :创建 read view 时,当前数据库将为下一个事务分配的事务 id;并不一定是 中的最大事务 id;
creator_trx_id :创建 read view 所在事务的 id;
聚集索引隐藏列
trx_id :当某个事务对某条聚集索引记录进行修改时,将会把当前事务的 id 赋值给 trx_id 。
roll_pointer :当某个事务对某条聚集索引记录进行修改时,会将上一个版本的记录写到 undolog,然后通过roll_pointer 指向旧版本记录,通过它可以找到修改前的记录。
锁
共享锁(S)
事务读操作加的锁;对某一行加锁;
在 SERIALIZABLE 隔离级别下,默认帮读操作加共享锁;
在 REPEATABLE READ 隔离级别下,需手动加共享锁,可解决幻读问题;
在 READ COMMITTED 隔离级别下,没必要加共享锁,采用的是 MVCC;
在 READ UNCOMMITTED 隔离级别下,既没有加锁也没有使用 MVCC。
排他锁(X)
事务删除或更新加的锁;对某一行加锁;
在4种隔离级别下,都添加了排他锁,事务提交或事务回滚后释放锁。
意向共享锁(IS)
对一张表中某几行加的共享锁;
意向排他锁(IX)
对一张表中某几行加的排他锁;
目的:为了告诉其他事务,此时这条表被一个事务在访问;作用:排除表级别读写锁 (全面扫描 加锁)。
锁的兼容性
Record Lock
记录锁,单个行记录上的锁;
Gap Lock(重点)
间隙锁,锁定一个范围,但不包含记录本身;全开区间;REPEATABLE READ 级别及以上支持间隙锁。
Next-Key Lock
记录锁+间隙锁,锁定一个范围,并且锁住记录本身;左开右闭区间;
Insert Intention Lock
插入意向锁,insert 操作的时候产生;在多事务同时写入不同数据至同一索引间隙的时候,并不 需要等待其他事务完成,不会发生锁等待。 假设有一个记录索引包含键值 4 和 7,两个不同的事务分别插入 5 和 6,每个事务都会产生一个加 在 4-7 之间的插入意向锁,获取在插入行上的排它锁,但是不会被互相锁住,因为数据行并不冲突。
锁兼容
更多资料在:https://github.com/0voice查询