MySQL InnoDB 事务锁源码分析


当前第2页 返回上一页

如果相等 并且 是普通INSERT,则接着判断record是否是deleted mark:

  •  如果不是delete mark,说明的确有duplicate,返回DB_DUPLICATE_KEY到上层,然后上层通过看是INSERT ON DUPLICATE KEY UPDATE还是普通INSERT来决定是转成update操作继续还是给用户报错duplicate
  • 如果是delete mark,则实际没有duplicate,接着往下走

(3)如果是INSERT ON DUPLICATE KEY UPDATE 并且 当前index是unique,则给其下一个record X Gap lock,保护不会被其他事务插入相同的entry

(4)判断record的下一个record上当前有没有锁,如果有的话,则给其加插入意向锁

(LOCK_X | LOCK_GAP | LOCK_INSERT_INTENTION)

确保要插入entry的区间没有其他Gap lock/Next-key lock保护

(5)插入entry

(6)释放page latch

:【二级索引】的步骤3似乎有些多余,因为即使有其他并发事务使用INSERT ON DUPLICATE KEY UPDATE来插入相同record的话,和【主键索引】流程一样,步骤1也只能串行进入,第一个线程没有找到与entry相同的record,走步骤4插入,直到步骤6结束释放page latch之后,第二个线程才能进到步骤1里,此时在步骤2中会中卡在加record的X Next-key lock上,直到线程一事务提交之后才能接着进行,所以看起来不会冲突?

上述流程在row_ins_index_entry函数中,具体入口如下:

mysql_parse->mysql_execute_command->Sql_cmd_dml::execute->
Sql_cmd_insert_values::execute_inner->write_record->handler::ha_write_row->
ha_innobase::write_row->row_insert_for_mysql->row_insert_for_mysql_using_ins_graph->
row_ins_step->row_ins->row_ins_index_entry_step->row_ins_index_entry


其中插入意向锁是在lock_rec_insert_check_and_lock函数里加的,入口如下:

row_ins_index_entry->row_ins_clust_index_entry/row_ins_sec_index_entry->
btr_cur_optimistic_insert/btr_cur_pessimistic_insert->btr_cur_ins_lock_and_undo->
lock_rec_insert_check_and_lock

3.3 隐式锁

另外要提的一点就是,Insert操作不会显式的加锁,每一条Insert的record上都默认有一个隐式锁,它是通过record的隐藏字段trx_id来检测的,对于主键索引,如果要插入的record在Btree中找到,那么只需要通过比较已有record的trx_id,如果这个trx_id对应的事务还是活跃事务,那么说明这个record的插入事务还未提交,隐式代表这个record上有锁,那么此时就才会将其转成显式锁放进lock_sys中并wait,这样做是为了提高性能,尽量减少对lock_sys的操作。对于二级索引的隐式锁检测就没有主键索引这么容易了,因为二级索引record没有记录trx_id,只能首先通过其所在page上的max_trx_id与当前活跃事务列表的最小trx_id来比较,小于它的话代表最后一次修改这个page的事务都已经提交,所以record上没有隐式锁,如果大于或等于它的话,就需要回主键找到对应的主键record并遍历undo历史版本来确认是否有隐式锁,具体实现在row_vers_impl_x_locked_low中,

4. Select 加锁流程

SELECT做当前读的加锁流程就在row_search_mvcc当中,一条SELECT语句会多次进入这个函数,第一次是通过index_read->row_search_mvcc进来,一般是首次访问index,取找WHERE里的exact record,之后每次再通过general_fetch->row_search_mvcc进来,根据具体条件遍历prev/next record,直到把满足WHRER条件的record都取出来。具体的加锁也就是在访问和遍历record的过程中进行,row_search_mvcc代码很长,这里我只提炼总结下加锁相关的流程:

  • 在index上查找search_tuple对应的record。(这里的record可能是上面说的index_read进来首次通过index Btree查找search_tuple对应的record,也有可能是之后多次general_fetch进来通过之前保存的cursor来恢复出来的上一次访问位置,然后拿到的prev/next record)
  • 如果是index_read 并且 mode是PAGE_CUR_L 或着PAGE_CUR_LE,给定位到的record的next record加 GAP LOCK
  • 如果record是infimum,跳转步骤9 next_rec,如果是supremum,加Next-key Lock,跳转步骤9 next_rec
  • 如果是index_read,record与search_tuple不相等,给recordGAP LOCK,返回 NOT FOUND
  • 到这里说明record与search_tuple相等,给record加Next-key Lock,两个例外,只加Rec Lock:
  1. 对于index_read,如果当前index是主键索引 并且 modePAGE_CUR_GE 并且 search_tuple的fields个数等于index的unique fields个数
  2. 看是否是unique_search,即search_tuple的fields个数等于当前index的unique fields个数 并且 当前index是主键索引或者(是二级索引且search_tuple不包含NULL字段)并且 record不是deleted mark
  • 到这里说明加锁成功了,然后处理record是deleted mark的情况:
  1. 当前index是主键索引 并且 是unique_search,返回 NOT FOUND
  2. 否则,跳转步骤9 next_rec
  • 如果当前index是二级索引 并且 需要回查主键索引,去主键索引里找对应的primary record并加 Rec Lock,如果primary record是deleted mark,则当前二级索引接着跳转步骤9 next_rec
  • 成功,返回DB_SUCCESS
  • next_rec: 根据mode来取对应的prev/next record,跳转 步骤3 继续

重点说一下步骤3,这里一般record是infimum或者supremum的情况都是多次genera_fetch对某个page取prev/next record之后走到page边缘,对于infimum,不会加任何lock,直接继续访问前一个prev record(即prev page的supremum),对于supremum的话,会加上Gap lock,它保护当前page最后一个user record和next page第一个user record之间的Gap。

其他的流程也就没什么了:

  1. 对于遍历到的满足条件的record,基本默认都是加Next-key lock
  2. 二级索引回表时只会对主键加Rec lock
  3. 对于某些特殊的场景,会将某些Next-key lock降级成Rec lock(步骤5)
  4. 还有一些特殊场景,会只加Gap lock(步骤2、4)

总结:

以上基本就是InnoDB加事务锁的相关流程,InsertSelect的加锁流程配合着看,事务锁的原则及实现基本也就出来了。

到此这篇关于MySQL InnoDB 事务锁源码分析的文章就介绍到这了,更多相关MySQL InnoDB 事务锁源码分析内容请搜索

更多相关Mysql内容来自木庄网络博客


标签:Mysql

返回前面的内容

相关阅读 >>

mysql行级锁实现原理是什么

如何测试空间是否支持mysql数据库?

mysql8.0.20安装教程及其安装问题详细教程

mysql 常用数据库语句 小练习

sql中关于distinct关键字的四种用法

mysql中exists、in及any的基本用法

mysql数据库 load data 多种用法

mysql的case方法使用好处是什么

python+tkinter+mysql做简单数据库查询界面

mysql怎么给字符串字段加索引

更多相关阅读请进入《mysql》频道 >>


数据库系统概念 第6版
书籍

数据库系统概念 第6版

机械工业出版社

本书主要讲述了数据模型、基于对象的数据库和XML、数据存储和查询、事务管理、体系结构等方面的内容。



打赏

取消

感谢您的支持,我会继续努力的!

扫码支持
扫码打赏,您说多少就多少

打开支付宝扫一扫,即可进行扫码打赏哦

分享从这里开始,精彩与您同在

评论

管理员已关闭评论功能...