21-5_02_innodb内幕
6.32 一致性非锁定读
指innodb通过行多记录版本控制的方式来读取执行当前时间数据库中行的数据
如果读取的行在执行Delete或者Update操作,不会等待而是去读取行的一个快照数据

快照数据是指该行之前版本的数据,通过undo段实现的,undo则是来回滚数据的,所以没有额外的开销,快照也不需要上锁,因为对历史版本不可能有修改要求
默认是非锁定读机制,提高并发性,在不同事务的隔离级别下不都是非锁定的一致读
一个行记录可能不止一个快照数据,使用这种技术为行多版本技术,并发控制为多版本并发控制,Mutil Version Concurrency Control, MVVC
在read committed 和 repeatable read(innodb默认)下,innodb使用非锁定的一致性读,读已提交总是读取被锁定行的最新一份快照数据,而可重复读则是读取事务开始时的数据

可重复读将一直读到id = 3
读已提交会读到 id = 1 之后再读到 id = 3
6.3.3 一致性锁定读
select语句:
- select ... for update
行记录 + X锁
- select ... lock in share mode
行记录 + S锁
X锁,S锁同上述所说的
此两种方式需要在一个事务中,事务提交锁就释放了
6.3.4 自增长与锁
每个含义自增长值的表都有一个自增长计数器,插入操作会依据这个计数器的值加1赋予自增列,这种方式叫AUTO_INC Locking,特殊的表锁机制,对完成自增长值插入的sql语句后立即释放,性能相对来说还是较差
innodb有一种新的轻量级互斥量的自增长实现机制,
自增长分类:

innodb_autoinc_lock_mode可控制自增长模式

innodb中自增长只能是索引的列且是第一列
6.3.5 外键和锁
外键:完整性约束
对于外键列,没有显式地加索引,innodb会给其加上索引
外键的插入和更新需要查询父表的记录,使用select ... lock in share mode方式,对父表加一个S锁

6.4 锁的算法
6.4.1 行锁的3种算法
- record Lock:单个行记录上的锁
- gap Lock:间隙锁,锁定范围,不包含记录本身
- next-key lock:上述两个锁之和
Record Kock总是去锁索引记录(每个索引,聚集索引和辅助索引),如果表上没有索引,则使用隐式的主键来(row_id)锁定
在Next-Key Lock下,对于索引中有10,11,13,20,则锁定的区间有:
(-infinite, 10]
(10, 11]
(11, 13]
(13, 20]
(20, + infinite]
Next-Key Locking技术,解决幻读
还有previous-key locking技术,锁定区间为
(-infinite, 10)
[10, 11)
[11, 13)
[13, 20)
[20, + infinite)
当查询的索引含义唯一属性时,innodb会将next-key lock进行优化,降级为record lock,仅锁住索引本身(聚集索引也是唯一索引,同理),如果唯一索引是多列,而查询是查找一个,则是range类型,而不是point类型,仍使用next-key lock
关闭Gap Lock:
- 将事务隔离级别设置为read committed读已提交
- 将参数 innodb_locks_unsafe_for_binlog设为1(8.2中找不到,不知道是改变或移除)
6.4.2 解决Phantom Problem幻读
幻读指在同一事务下,连续两次sql(在某个范围)语句导致不同的结果,第二次可能返回之前不存在的行

innodb:
在可重复读下采用next-key locking方式加锁
在读已提交下采用record lock
采用next-key locking实现唯一性检查,
select * from table where col = 'xxx' lock in share mode;
如果没有返回行,则插入的值是唯一的,同时这范围被锁住,则重新添加时则是唯一的

浙公网安备 33010602011771号