【MySQL数据库教程天花板,mysql安装到mysql高级,强!硬!-哔哩哔哩】
【阿里巴巴Java开发手册】https://www.w3cschool.cn/alibaba_java
一个事务在执行
INSERT
操作时,如果即将插入的间隙
已经被其他事务加了gap锁
,那么本次INSERT
操作会阻塞,并且当前事务会在该间隙上加一个插入意向锁
,否则一般情况下INSERT
操作时不加锁的。那如果一个事务首先插入了一条记录(此时并没有在内存生产该记录关联的锁结构),然后另一个事务:
立即使用
SELECT ... LOCK IN SHARE MODE
语句读取这条记录,也就是要获取这条记录的S锁
,或者使用SELECT ... FOR UPDATE
语句读取这条记录,也就是获取这条记录的X锁
,怎么办?
如果允许这种情况的发生,那么可能产生脏读
问题。
立即修改这条记录,也就是要获取这条记录的X锁
,怎么办?
如果允许这种情况的发生,那么可能产生脏写
问题。
这时候我们前边提过的事务id
又要起作用了。我们把聚簇索引和二级索引中的记录分开看一下:
情景一:
对于聚簇索引记录来说,有一个trx_id
隐藏列,该隐藏列记录着最后改动该记录的事务id
。那么如果当前事务中新插入一条聚簇索引记录后,该记录的trx_id
隐藏列代表的就是当前事务的事务id
,如果其他事务此时想对该记录添加S锁
或者X锁
时,首先会看一下该记录的trx_id
隐藏列代表的事务是否是当前的活跃事务,如果是的话,那么就帮助当前事务创建一个X锁
(也就是为当前事务创建一个锁结构,is_waiting
属性false
),然后自己进入等待状态(也就是为自己也创建一个锁结构,is_waiting
属性是true
)。
情景二:
对于二级索引记录来说,本身并没有trx_id
隐藏列,但是在二级索引页面的Page Header
部分有一个PAGE_MAX_TRX_ID
属性,该属性代表对该页面做改动的最大的事务id
,如果PAGE_MAX_TRX_ID
属性值小于当前最小的活跃事务id
,那么说明对该页面做修改的事务都已经提交了,否则就需要在页面中定位到对应的二级索引记录,然后回表找到它对应的聚簇索引记录,然后再重复情景一
的做法。
即:一个事务对新插入的记录可以不显式的加锁(生成一个锁结构),但是由于
事务id
的存在,相当于加了一个隐式锁
。别的事务在对这条记录加S锁
或者X锁
时,由于隐式锁
的存在,会先帮助当前事务生成一个锁结构,然后自己再生成一个锁结构后进入等待状态。隐式锁是一种延迟加锁
的机制,从而来减少加锁的数量。
隐式锁在实际内存对象中并不含有这个锁信息。只有当产生锁等待时,隐式锁转化为显示锁。
通过特定的语句进行加锁,我们一般称之为显示加锁,例如:
显示加共享锁:
select .... lock in share mode
显示加排它锁:
select .... for update