MySQL锁¶
InnoDB锁模式¶
行锁:
- 共享锁(S):允许一个事务去读一行,阻止其他事务获得相同数据集的排他锁。
- 排他锁(X):允许获得排他锁的事务更新数据,阻止其他事务取得相同数据集的共享读锁和排他写锁。
为了允许行锁和表锁共存,实现多粒度锁机制,InnoDB 还有两种内部使用的意向锁(Intention Locks),这两种意向锁都是表锁:
- 意向共享锁(IS):事务打算给数据行加行共享锁,事务在给一个数据行加共享锁前必须先取得该表的 IS 锁。
- 意向排他锁(IX):事务打算给数据行加行排他锁,事务在给一个数据行加排他锁前必须先取得该表的 IX 锁。
InnoDB 行锁实现方式:¶
- InnoDB 行锁是通过给索引上的索引项加锁来实现的,只有通过索引条件检索数据,InnoDB 才使用行级锁,否则,InnoDB 将使用表锁(真正使用索引时)
- 多个session是访问不同行的记录, 但是如果使用相同的索引键, 会出现锁冲突的
锁算法¶
Record lock 记录锁:锁定行记录
Gap lock:间隙锁:锁定一个区间,是键值在条件范围内,但不存在的记录
锁定的是索引记录之间的间隙,并发事务插入新数据前会先检测间隙中是否已被加锁,防止幻读的出现
若执行的条件是范围过大,则InnoDB会将整个范围内所有的索引键值全部锁定,很容易对性能造成影响。
Next-key lock:记录锁 + 间隙锁,锁定行记录 + 区间
快照读
- 即不加锁读,读取记录的快照版本而非最新版本,通过MVCC实现;
- 在快照读读情况下,mysql通过mvcc来避免幻读。
当前读
- 即加锁读,读取记录的最新版本,会加锁保证其他并发事务不能修改当前记录,直至获取锁的事务释放锁;
- 在当前读读情况下,mysql通过next-key来避免幻读。
例子¶
select * from table where id = ?
select * from table where id < ?
select * from table where id = ? lock in share mode
select * from table where id < ? lock in share mode
select * from table where id = ? for update
select * from table where id < ? for update
需要根据事务隔离级别、索引类型进行判断。
RC/RU 隔离级别 + 条件非索引 || RC/RU 隔离级别 + (非)聚簇索引¶
(1)select * from table where num = 200
不加任何锁,是快照读。
(2)select * from table where num > 200
不加任何锁,是快照读。
(3)select * from table where num = 200 lock in share mode
当num = 200,有两条记录。这两条记录对应的pId=2,7,因此在pId=2,7的聚簇索引上加行级S锁,采用当前读。
(非聚簇索引时,两个索引都加上锁)
(4)select * from table where num > 200 lock in share mode
当num > 200,有一条记录。这条记录对应的pId=3,因此在pId=3的聚簇索引上加上行级S锁,采用当前读。
(5)select * from table where num = 200 for update
当num = 200,有两条记录。这两条记录对应的pId=2,7,因此在pId=2,7的聚簇索引上加行级X锁,采用当前读。
(6)select * from table where num > 200 for update
当num > 200,有一条记录。这条记录对应的pId=3,因此在pId=3的聚簇索引上加上行级X锁,采用当前读。
RR/Serializable 隔离级别 + 非索引¶
(1)select * from table where num =(或>) 200
在RR级别下,不加任何锁,是快照读。
在Serializable级别下,在pId = 1,2,3,7(全表所有记录)的聚簇索引上加S锁。
并且在聚簇索引的所有间隙(-∞,1)(1,2)(2,3)(3,7)(7,+∞)加gap lock
(2)select * from table where num =(或>) 200 lock in share mode
在pId = 1,2,3,7(全表所有记录)的聚簇索引上加S锁。
并且在聚簇索引的所有间隙(-∞,1)(1,2)(2,3)(3,7)(7,+∞)加gap lock
(3)select * from table where num =(或>) 200 for update
在pId = 1,2,3,7(全表所有记录)的聚簇索引上加X锁。
并且在聚簇索引的所有间隙(-∞,1)(1,2)(2,3)(3,7)(7,+∞)加gap lock
RR/Serializable 隔离级别 + 聚簇索引¶
如果where后的条件为精确查询(=的情况),那么只存在record lock。如果为范围查询(>或<的情况),那么存在的是record lock + gap lock。