Innodb的锁,保障了事务的隔离性;对索引(聚集索引组织表)加锁,Oracle是对数据块(堆表)加锁
锁类型
innodb支持多粒度锁:记录级锁和表级锁;
为了支持这种多粒度,加入意向锁(intention lock),
意向锁是表级锁,提示事务对此表记录加的记录锁类型。
兼容 | X | IX | S | IS |
---|---|---|---|---|
X | ✘ | ✘ | ✘ | ✘ |
IX | ✘ | ✔ | ✘ | ✔ |
S | ✘ | ✘ | ✔ | ✔ |
IS | ✘ | ✔ | ✔ | ✔ |
1、要想获得S锁,必须先取得IS或者更强的锁
2、要想获得X锁,必须先取得IX锁
- 意向锁不会阻塞任何操作,除非是全表请求(alter, lock table ...)
- 意向锁是完全兼容的,因为是表级别的,若不兼容,则innodb的行锁无从谈起
事务1对表a的某些记录进行写操作,事务2相对表a的某另一些记录进行写操作,两者都有IX操作,若不兼容,直接退变成Myisam的表锁形式了
- X, S 的兼容场景,针对的是同一记录区间的读写,这样就理解上面的兼容性
锁算法(这是锁算法,不是锁类型)
Record lock:This is a lock on an index record.
记录锁一定是锁索引记录,即使无索引,也会使用innodb的隐藏的聚集索引
Gap lock: *This is a lock on a gap * between index records, or a lock on the gap before the first or after the last index record.
间隙锁唯一索引不会用到(联合唯一索引前缀过滤仍会使用),无索引和非唯一索引会使用,锁记录的前后区间
Gap锁特性:
不同事务对同一区间的不同记录进行插入操作,两者不冲突
gap 区间的一条记录被删除, gap 锁要合并
gap s == gap x ,且兼容。只阻塞插入区间操作
Next-key lock:This is a combination of a record lock on the index record and a gap lock on the gap * before * the index record.
举例:
(negative infinity, 10]
(10, 11]
(11, 13]
(13, 20]
(20, positive infinity)退化为gap lock,信息展示next_key lock
X type | record lock | gap lock | next_key lock |
---|---|---|---|
record lock | ✘ | ✘ | ✘ |
gap lock | ✔ | ✔ | ✔ |
next_key lock | ✘ | ✘ | ✘ |
不可重复读(幻读)
一个事务内,对同一数据集合连续查询(包括非锁定读),另外一个事务对此数据集合进行了DML,本事务内结果是一致的。但不保障查询之间,本事务的DML(会有“幽灵”输出)的场景。注意,innodb读取的是查询开始的快照,不是事务开启时的快照
锁案例
CREATE TABLE testlock (
a int(11) NOT NULL DEFAULT '0',
b int(11) DEFAULT NULL,
PRIMARY KEY (a
),
KEYb
(b
)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin
select * from testlock;
+----+------+
| a | b |
+----+------+
| 2 | 1 |
| 5 | 3 |
| 6 | 5 |
| 10 | 8 |
+----+------+
- 有值加锁
select * from testlock where b=3 for update
由图例可以看出:
索引b,加了next_key lock (1,3] 和 gap lock(3,5)
主键a,加了一个record lock a=5
- 无值加锁
select * from testlock where b=2 for update
由图例可以看出:
索引b,加了gap lock (1,3)
- 临界加锁
- select xxxx from where b xxx for update
select * from testlock where b=8 for update
(5,8],(8, +∞)。
select * from testlock where b>8 for update
select * from testlock where b>80 for update
锁全表
- delete from where b xxx
delete from testlock where b=8
此种情况,与上面的一致,加锁(5,8],(8, +∞)。截图与上面的模式相同
delete from testlock where b> 8
此种情况,加next_key lock (8, +∞)。图例准确
- upate where b xxx
update testlock set a=9 where b=6
update testlock set b=10 where b=6
update涉及对索引的自身值得改写操作,存在锁分裂的情况,不一而足
当然,跟组合四:[id无索引, Read Committed]类似,这个情况下,MySQL也做了一些优化,就是所谓的semi-consistent read。semi-consistent read开启的情况下,对于不满足查询条件的记录,MySQL会提前放锁。针对上面的这个用例,就是除了记录[d,10],[g,10]之外,所有的记录锁都会被释放,同时不加GAP锁。semi-consistent read如何触发:要么是read committed隔离级别;要么是Repeatable Read隔离级别,同时设置了innodb_locks_unsafe_for_binlog 参数。更详细的关于semi-consistent read的介绍,可参考我之前的一篇博客:MySQL+InnoDB semi-consitent read原理及实现分析 。