博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
innodB的隐式锁
阅读量:5139 次
发布时间:2019-06-13

本文共 3783 字,大约阅读时间需要 12 分钟。

http://blog.csdn.net/taozhi20084525/article/details/19545231

一、知识准备之隐式锁

 

参考:

Innodb 实现了一个延迟加锁的机制,来减少加锁的数量,在代码中称为隐式锁(Implicit Lock)。隐式锁中有个重要的元素,事务ID(trx_id)。隐式锁的逻辑过程如下:

A. InnoDB的每条记录中都一个隐含的trx_id字段,这个字段存在于簇索引的B+Tree中。

B. 在操作一条记录前,首先根据记录中的trx_id检查该事务是否是活动的事务(未提交或回滚)。如果是活动的事务,首先将隐式锁转换为显式锁(就是为该事务添加一个锁)。

C. 检查是否有锁冲突,如果有冲突,创建锁,并设置为waiting状态。如果没有冲突不加锁,跳到E。

D. 等待加锁成功,被唤醒,或者超时。

E. 写数据,并将自己的trx_id写入trx_id字段。Page Lock可以保证操作的正确性。

 

二、具体代码

转自:《InnoDB SMO & Page Extent & Lock & Latch》 by 何登成

InnoDB 的 insert 操作,对插入的记录不加锁,但是此时如果另一个线程进行当前读,类似与以下的用例,session 2 会锁等待 session 1,那么这是如何实现的呢?

 

session 1:                			session 2:set autocommit = ‘ off ’;insert into c values (11, ’ aaa’);					        select * from c where c1 = 11 lock in share mode;

 

下面是 session 2 的源码跟踪流程:

 

row_search_for_mysql();sel_set_rec_lock();…	//  将记录上的 implicit 锁转换为 explicit 锁	lock_rec_convert_impl_to_expl();		//  查询当前记录上是否存在 implicit 锁		// 1.  必须已经持有了 kernel mutex		// 2.  获取记录上的 DB_TRX_ID 系统列,获取事务 ID		// 3.  根据事务 ID,判断当前事务是否为活跃事务		// 4.  若为活跃事务,则返回此活跃事务对象		impl_trx = lock_clust_rec_some_has_impl(rec, index, offsets);			ut_ad(mutex_own(&kernel_mutex));			trx_id = row_get_rec_trx_id();			trx_is_active(trx_id);		//  判断返回事务,是否含有 explicit 锁;若有,直接返回;否则将		// implicit 锁转化为 explicit 锁;由 session 2 完成 session 1 insert 记录的加锁		lock_rec_has_expl(impl_trx);		//  当前 session 1 不存在 explicit 锁,因此直接创建一个锁,锁模式为		// LOCK_REC | LOCK_X | LOCK_REC_NOT_GAP		//  由于 insert 记录上不可能有其他锁,因此转化直接成功,X 锁加上		lock_rec_add_to_queue();//  完成 session 1  的 insert 操作的 implicit 到 explicit 锁转化之后,此时可以加 session 2//  的 scan S 锁,但是会等待 session 1 放锁lock_rec_lock();

备注:Insert 不加锁,或者说是 Implicit Lock 的意义其实十分重大。从前面介绍的 Lock 结构中,我们可以分析出,其实 InnoDB 的一个锁结构的开销是比较大的。 或者说InnoDB 锁一条记录的开销,与锁一个页面中所有记录的开销是一样的。而 Insert 通过 Implicit 方式加锁,极大的减轻了 Insert 时的锁模块开销,对于 InnoDB 支持并发 Insert 操作,是一个极大的提升。

 

三、隐式锁的重要问题——check Duplicate

转自:《InnoDB SMO & Page Extent & Lock & Latch》 by 何登成

进一步参考: Innodb锁系统 Insert/Delete 锁处理及死锁示例分析 http://fan0321.iteye.com/blog/1984364

这里主要看聚集索引的check duplicate。check duplicate也就是保证在隐式锁情况下,多个事务的insert是如何保证索引的unique的。

1、聚集索引check duplicate

 

ha_innobase::write_row();	…	row_ins_index_entry_low();		//  做 search path,将 cursor 定位到第一个小于等于插入值的位置		btr_cur_search_to_nth_level(PAGE_CUR_LE);		// cursor 是 binary search 之后在叶节点定位的 insert 位置		//  判断 binary search 的结果,当前记录与待 insert 记录有几个相同的列		//  若相同列取值的列数量(cursor->low_match),超过当前索引的唯一键值数量,		//  则可能存在唯一性键值冲突		row_ins_duplicate_error_in_clust(cursor, entry, thr);			n_unique = dict_index_get_n_uniques();			if (cursor->low_match >= n_unique)				//  对 cursor 对应的已有记录加 S 锁(可能会等待),保证记录上的操作,包括:				// Insert/Update/Delete 已经提交或者回滚				// S 锁已经可以保证其他事务的 insert 操作不能进行,因为在真正				// insert 操作进行时,会尝试对 下一个record加 X 锁,详见下一章节分析				row_ins_set_shared_rec_loc(LOCK_S);					lock_clust_rec_read_check_and_lock();						//  判断 cursor 对应的记录上是否存在 implicit 锁(有活跃事务)						//  若存在,则将 implicit 锁转化为 explicit 锁						lock_rec_convert_impl_to_expl();						lock_rec_lock();  //如果上面的隐式锁转化成功,此处加S锁将会等待,直到活跃事务释放锁。				// S 锁加锁完成之后,可以再次做判断,最终决定是否存在 unique 冲突				// 1.  判断 insert 记录与 cursor 对应的记录取值是否相同				// 2.  二级唯一键值锁引,可以存在多个 NULL 值				// 3.  最后判断记录的 delete_bit 状态,判断记录是否被删除提交				row_ins_dupl_err_with_rec();					cmp_dtuple_rec_with_match();					return !rec_get_deleted_flag();

 

注意:S锁加锁成功之时,活跃事务应当提交或回滚并释放锁;但不管是提交还是回滚,cursor指向的record仍然存在,可能会有delete标志(发生回滚)。

2、辅助索引check duplicate

a、聚簇索引 primary key 是唯一的;非聚簇唯一索引,其索引也是唯一的。

b、若 insert 记录与聚簇索引项完全相同,并且聚簇索引项为删除项,则直接将其删除标记设置为 0,并在删除项上做 update。
c、若 insert 记录与非聚簇唯一索引项键值完全相同, 并且非聚簇索引项为删除项, 此时并不一定修改项状态,还需要判断两者对应的 primary key 是否相同,若 primary key 也相同,则重用项;否则,插入新项。
d、聚簇索引中,相同 primary key 取值的项,最多只有一项,不可能存在多项。
e、非聚簇唯一索引,索引键值相同的项可能有多项,但是这些项,其 primary  key 是不同的;而且,这些项,只有一个是有效项,其他项都为已删除的提交项。
f、聚簇索引的唯一性检查,只需要检测 insert 对应的记录即可,因为只有一项;非聚簇唯一索引的唯一性检测,需要向后检查多条键值相同记录

 
 

转载于:https://www.cnblogs.com/zengkefu/p/5691230.html

你可能感兴趣的文章
sql server mdx
查看>>
ruby正则表达式
查看>>
[移动端] IOS下border-image不起作用的解决办法
查看>>
springmvc集成Freemarke配置的几点
查看>>
自己写的仿爱奇艺综艺频道轮播图,没有淡入淡出效果
查看>>
提炼游戏引擎系列:第一次迭代
查看>>
Django 学习
查看>>
Android的事件处理机制详解(二)-----基于监听的事件处理机制
查看>>
s5-12 RIP
查看>>
Linux-以指定用户运行redis
查看>>
Linux-socket的close和shutdown区别及应用场景
查看>>
初探Oracle全栈虚拟机---GraalVM
查看>>
移动端的点击滚动逻辑实现。
查看>>
xpath
查看>>
sqlserver 中数据导入到mysql中的方法以及注意事项
查看>>
python 协程(单线程中的异步调用)(转廖雪峰老师python教程)
查看>>
Jenkins管理静态资源
查看>>
打地鼠游戏(7)
查看>>
redis实现分页技术
查看>>
第八章 SpringCloud之Feign、Hystrix结合使用
查看>>