深入理解mysql锁和事务隔离级别
-
Innodb和Myisam最大区别
- 支持事务
- 支持行级锁
- MyIsam中索引文件和数据文件是分离的(非聚集),B+树的叶子节点存储的是数据在磁盘的位置指针
- InnoDB中数据是存储在主键索引的B+树的叶子节点中
- MyisAM底层存储分为三个文件
- .frm:frame缩写,存放表字段结构
- .myd:myisam data缩写,存放数据
- .myi:MyisAM index缩写, 存放索引
- InnoDB底层存储分为两个文件
- .frm:frame缩写,存放表字段结构
- .ibd:存放索引和数据
-
并发事务带来的问题
- **更新丢失:**两个事务同时操作相同的数据,后提交的事务会覆盖先提交的事务处理结果,通过乐观锁可以解决
- 脏读: 事务A读取到了事务B已经修改但尚未提交的数据,如果事务B回滚,A读取的数据无效,不符合一致性
- 不可重读: 事务A访问了两次数据,但是在两次访问之间事务B进行了一次修改,导致事务A的两次查询得到的数据不同,不符合隔离性.
- 幻读: 与不可重复读相似,只是事务B在事务A的两次查询之间做了新增操作,导致事务A的第二次读取发现多了记录,不符合隔离性.
-
隔离级别
查看数据库事务隔离级别: show global VARIABLES like ‘%transaction_isolation%’;
设置事务隔离级别: set tx_isolation=‘REPEATABLE-READ’
隔离级别 脏读 不可重复读 幻读 读未提交
READ UNCOMMITTED√ √ √ 读已提交
READ COMMITTED× √ √ 可重复读(默认)
REPEATABLE READ× × √ 可串行化
SERIALIZABLE× × × **注意:**在可重复读级别,虽然保证事务A两次读的数据是一致的,但是如果事务A此时更新该条数据,是以数据库中的实际数据为基准进行修改的.
MVCC(多版本并发控制):不可重复读隔离级别在读的时候是读取的上一次查询到的快照版本(为确保两次读取的数据一致,在事务A不修改该数据的情况下,俩次读取的内容不会发生改变,均读取的快照版本),不会读取到修改或新增的数据,但是会读取到新增的数据.更新是按照数据库最新数据进行更新.
而InnoDb在可重复读级别解决幻读问题是依靠临键锁来实现
-
锁的模式
-
共享锁(行锁/S锁):Shared Locks
又成为读锁,顾名思义,共享锁就是多个事务对于同一数据可以共享一把锁,都能访问到数据,但是只能读不能修改.
加锁方式:select * from student where id=1 LOCK IN SHARE MODE;
释放锁:commit;rollback
-
排它锁(行锁/X锁):Exclusive Locks
又称为写锁,排它锁不能与其它锁并存,如果一个事务获取了一个数据的排它锁,其它事务就不能在获取改行的锁(共享锁,排它锁),只有该获取了排它锁的事务可以对数据行进行读取和修改.
加锁方式:detele/update/insert默认加上X锁或者 select * from student where id =1 for update
-
意向共享锁(表锁/IS锁):Intention Shared Locks
意向锁是由数据引擎自己维护的,用户无法手动操作意向锁
表示事务准备给数据行加入共享锁,也就是说一个数据行加共享锁前必须先取得该表的IS锁
-
意向排它锁(表锁/IX锁):Intention Exclusive Locks
表示事务准备给数据行加入排它锁,说明事务在一个数据行加排它锁之前必须先取得该表的IX锁.
-
-
锁的算法(行锁的到底是什么)
- 如果没有索引,mysql存在隐藏的列rowId,会锁住索引的隐藏的rowId
- 主键索引(聚集索引(除了主键索引,其它索引都是非聚集索引)),会锁住主键id
记录(record):指存在主键值的位置
间隙(Gap):记录之间的区间称为间隙,左右都不包括record
临键(Next-key):间隙+下一个记录,是一个左开右闭的区间
-
记录锁 Record Locks
如果id精准命中了一个record,则锁住了该record
-
间隙锁 Gap Locks
如果record不存在,则锁住了间隙
-
临键锁 Next-key Locks
一定是范围查询,才能同时包含Record和Gap
而InnoDb在可重复读级别解决幻读问题是依靠临键锁来实现
此处实际测试并未锁住id=10,而是锁住了(4,7],(7,9]