返回顶部
首页 > 资讯 > 数据库 >MySQL InnoDB 事务锁源码分析
  • 237
分享到

MySQL InnoDB 事务锁源码分析

2024-04-02 19:04:59 237人浏览 安东尼
摘要

目录1. Lock 与 Latch2. Repeatable Read3. Insert加锁流程3.1 lock mode3.2 加锁流程3.3 隐式锁4. Select 加锁流程本

本文前提:

代码Mysql 8.0.13

只整理Repeatable Read当前读。Read Committed简单很多,另外快照读是基于mvcC不用加锁,所以不在本文讨论范畴。

1. Lock 与 Latch

InnoDB 中的lock事务中对访问/修改的record加的锁,它一般是在事务提交或回滚时释放。latch是在BTree上定位record的时候对Btree pages加的锁,它一般是在对page中对应record加上lock并且完成访问/修改后就释放,latch的锁区间比lock小很多。在具体的实现中,一个大的transaction会被拆成若干小的mini transaction(mtr),如下图所示:有一个transaction,依次做了insertselect…for updateupdate操作,这3个操作分别对应3个mtr,每个mtr完成:

  • 在btree查找目标record,加相关page latch
  • 加目标record lock,修改对应record
  • 释放page latch

为什么要这么做呢?是为了并发,事务中的每一个操作,在步骤二完成之后,相应的record已经加上了lock保护起来,确保其他并发事务无法修改,所以这时候没必要还占着record所在的page latch,否则其他事务 访问/修改 相同page的不同record时,这本来是可以并行做的事情,在这里会被page latch会被卡住。

lock是存在lock_sys->rec_hash中,每个record lockrec_hash中通过<space_id, page_no, heap_no>来标识

latch是存在bufferpool对应pageblock中,对应block->lock

本文只关注lock相关的东西,latch后面单独搞一篇整理

2. Repeatable Read

具体每个隔离级别就不展开说了,这里主要说下RR,从名字上也能看出来,RR支持可重复度,也就是在一个事务中,多次执行相同的SELECT…FOR UPDATE应该看到相同的结果集(除本事务修改外),这个就要求SELECT的区间里不能有其他事务插入新的record,所以SELECT除了对满足条件的record加lock之外,对相应区间也要加lock来保护起来。在InnoDB的实现中,并没有一个一下锁住某个指定区间的锁,而是把一个大的区间锁拆分放在区间中已有的多个record上来完成。所以引入了Gap lock和Next-key lock的概念,它们加再一个具体的record上

  • Gap lock 保护这个record与其前一个record之间的开区间
  • Next-key lock 保护包含这个record与其前一个record之间的左开右闭区间

它们都是为了保护这个区间不能被别的事务插入新的record,实现RR。

接下来从源码实现上来分别看下Insert和Select是如何加lock的,结合着看也就知道InnoDB的RR是如何实现的了。Insert的加锁分布在Insert操作的过程中,遍布在多个相关的函数里,Select的加锁则比较集中,就在row_search_mvcc里。

3. Insert加锁流程

3.1 lock mode

lock的mode主要有Share(S)和Exclusive(X)【代码中对应LOCK_S和LOCK_X】

lock的gap mode主要有Record lock, Gap lock, Next-key lock【代码中对应LOCK_REC_NOT_GAP, LOCK_GAP, LOCK_ORDINARY】

在具体使用中将 mode|gap_mode 之后就是一个lock的实际类型,Record lock是作用在单个record上的记录锁,Gap lock/Next-key lock虽然也是加在某个具体record上,但作用是为了确保record前面的gap不要有其他并发事务插入,这个具体是怎么实现呢,InnoDB引入了一个插入意向锁,他的实际类型是

(LOCK_X | LOCK_GAP | LOCK_INSERT_INTENTION)

Gap lock/Next-key lock互斥,如果要插入前检测到插入位置的next record上有lock,则会尝试对这个next record加一个插入意向锁,代表本事务打算给这个gap里插一个新record,看行不行?如果已经有别的事务给这里上了Gap/Next-key lock,代表它想保护这里,所以当前插入意向锁需要等待相关事务提交才行。这个检测只是单向的,即插入意向锁需等待Gap/Next-key lock释放,而任何锁不用等待插入意向锁释放,否则严重影响这个gap中不冲突的Insert操作并发。

具体的锁冲突检测在lock_rec_has_to_wait函数中,大体原则就是:判断两个lock兼容还是不兼容,首先先做mode的冲突检测

如果不冲突,则代表锁兼容,无需等待,如果冲突,则接着做gap mode的冲突例外检测,整理如下:


如果gap mode不冲突,则作为例外情况可以认为锁兼容,无需等待。可以看到:

  • 插入意向锁需要等待Gap lockNext-key lock
  • 任何锁不用等待插入意向锁
  • Gap lock无需等待任何锁
  • Next-key lock需要等待其他Next-key lock及Record Lock,反之亦然

了解了这些锁兼容原则,接下来就可以看在实际Insert流程中是如何使用它们的。

3.2 加锁流程

Insert的顺序是先插入主键索引,再依次插入二级索引。以下是从代码中整理出来的流程,插入某个entry的操作,

【对于主键索引】:

(1)先在查找Btree,加相关page latch,定位到entry对应插入位置的record (<= entry)

(2)如果要插入的entry已经存在,即entry = record此时接着判断:

  • 如果是INSERT ON DUPLICATE KEY UPDATE,则对recordX Next-key lock
  • 如果是普通INSERT,则对recordS Next-key lock

之后接着判断record是否是deleted mark:

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

(3)判断record的下一个record上当前有没有锁,如果有的话,则给其加插入意向锁,确保要插入entry的区间没有其他Gap lock/Next-key lock保护

(4)插入entry

(5)释放page latch,此时依旧占有lock

【对于二级索引】

(1)先在查找Btree,加相关page latch,定位到entry对应插入位置的record (<= entry)

(2)如果要插入的entry已经存在,即entry = record,并且当前index是unique:

  • 如果是INSERT ON DUPLICATE KEY UPDATE,则对recordX Next-key lock
  • 如果是普通INSERT,则对record2S Next-key lock

判断record与entry是否相等:

如果相等 并且 是普通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 事务锁源码分析内容请搜索编程网以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程网!

您可能感兴趣的文档:

--结束END--

本文标题: MySQL InnoDB 事务锁源码分析

本文链接: https://lsjlt.com/news/136327.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

猜你喜欢
  • MySQL InnoDB 事务锁源码分析
    目录1. Lock 与 Latch2. Repeatable Read3. Insert加锁流程3.1 lock mode3.2 加锁流程3.3 隐式锁4. Select 加锁流程本...
    99+
    2024-04-02
  • InnoDB 事务加锁分析
    本文首发于 vivo互联网技术 微信公众号  链接:https://mp.weixin.qq.com/s/S7MhlsZveBHRSQhq5aTIJA 作者:何志创 一般大家对数据库事务的了解可能停...
    99+
    2024-04-02
  • MySQL中InnoDB事务锁阅读锁信息状态的示例分析
    这篇文章主要介绍了MySQL中InnoDB事务锁阅读锁信息状态的示例分析,具有一定借鉴价值,感兴趣的朋友可以参考下,希望大家阅读完这篇文章之后大有收获,下面让小编带着大家一起了解一下。 ...
    99+
    2024-04-02
  • Mysql-InnoDB锁的示例分析
    小编给大家分享一下Mysql-InnoDB锁的示例分析,希望大家阅读完这篇文章后大所收获,下面让我们一起去探讨吧!锁类型行级锁锁模式只有LOCK_S 和LOCK_X,其他的 FLAG 用于锁的描述,如前述 ...
    99+
    2024-04-02
  • MySQL中InnoDB锁机制分析
    本篇内容介绍了“MySQL中InnoDB锁机制分析”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成! ...
    99+
    2024-04-02
  • mysql innodb的行锁举例分析
    这篇文章主要讲解了“mysql innodb的行锁举例分析”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“mysql innodb的行锁举例分析”吧! ...
    99+
    2024-04-02
  • MySQL之InnoDB中锁的情况分析
    这篇文章主要讲解了“MySQL之InnoDB中锁的情况分析”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“MySQL之InnoDB中锁的情况分析”吧!mysq...
    99+
    2024-04-02
  • Mysql-InnoDB事物的示例分析
    这篇文章给大家分享的是有关Mysql-InnoDB事物的示例分析的内容。小编觉得挺实用的,因此分享给大家做个参考,一起跟随小编过来看看吧。事物基本概念事物的特性(ACID)原子性 atomicity一致性 ...
    99+
    2024-04-02
  • MySQL事务和InnoDB锁类型介绍
    这篇文章为大家带来有关MySQL事务和InnoDB锁类型的介绍。文章涵盖MySQL事务和InnoDB锁类型的知识点,希望大家通过这篇文章能有所收获。一、事务的隔离级别1、4 种隔离级别(1)未提交读(Rea...
    99+
    2024-04-02
  • Spring事务源码分析专题(一)JdbcTemplate使用及源码分析
    Spring中的数据访问,JdbcTemplate使用及源码分析 前言 本系列文章为事务专栏分析文章,整个事务分析专题将按下面这张图完成 对源码分析前,我希望先介绍一下Spring中数据访问的相关内容,然后层层递进到事物的源码分析,主要分...
    99+
    2019-10-22
    Spring事务源码分析专题(一)JdbcTemplate使用及源码分析 数据库入门 数据库基础教程 数据库 mysql
  • InnoDB IO路径源码的示例分析
    这篇文章主要介绍InnoDB IO路径源码的示例分析,文中介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们一定要看完!InnoDB实现IO Flush通过“os_file_flush”宏收敛,macro展开后为”os_file_flus...
    99+
    2023-06-04
  • 分析MySQL数据库Innodb中的事务隔离级别和锁的关系
    本篇内容主要讲解“分析MySQL数据库Innodb中的事务隔离级别和锁的关系”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“分析MySQL数据库Innodb中的事...
    99+
    2024-04-02
  • MySQL InnoDB事务与锁的详细讲解
    这篇文章主要介绍“MySQL InnoDB事务与锁的详细讲解”,在日常操作中,相信很多人在MySQL InnoDB事务与锁的详细讲解问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解...
    99+
    2024-04-02
  • Redisson分布式锁之加解锁源码分析
    这篇文章主要介绍“Redisson分布式锁之加解锁源码分析”的相关知识,小编通过实际案例向大家展示操作过程,操作方法简单快捷,实用性强,希望这篇“Redisson分布式锁之加解锁源码分析”文章能帮助大家解决问题。锁的可重入性我们都知道,Ja...
    99+
    2023-07-05
  • mysql InnoDB锁等待的查看以及分析
    本篇内容主要讲解“mysql InnoDB锁等待的查看以及分析”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“mysql InnoDB锁等待的查看以及分析”吧!在...
    99+
    2024-04-02
  • MySQL与InnoDB下共享锁与排他锁实例分析
    这篇文章主要介绍“MySQL与InnoDB下共享锁与排他锁实例分析”的相关知识,小编通过实际案例向大家展示操作过程,操作方法简单快捷,实用性强,希望这篇“MySQL与InnoDB下共享锁与排他锁实例分析”文...
    99+
    2024-04-02
  • MySQL InnoDB 事务
    事务的定义 事务:数据库操作的最小工作单元,是作为单个逻辑工作单元执行的一系列操作; 事务是一组不可再分割的操作集合(工作逻辑单元)。 典型事务使用场景:转账 MySQL 开启事务: MySQL 默认是开启...
    99+
    2024-04-02
  • Golang Mutex互斥锁源码分析
    目录前言Mutex 特性数据结构Lock()Unlock()前言 在上一篇文章中,我们一起学习了如何使用 Go 中的互斥锁 Mutex,那么本篇文章,我...
    99+
    2024-04-02
  • InnoDB事务锁之行锁-insert加锁-隐式锁
    ...
    99+
    2024-04-02
  • MySQL中事务和锁的示例分析
    小编给大家分享一下MySQL中事务和锁的示例分析,相信大部分人都还不怎么了解,因此分享这篇文章给大家参考一下,希望大家阅读完这篇文章后大有收获,下面让我们一起去了解一下吧! My...
    99+
    2024-04-02
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作