InnoDB锁介绍

news2024/11/19 9:27:55

本文主要介绍MySQL InnoDB引擎中的各种锁策略和锁类别,并针对记录锁做演示以便于理解。

以下内容适用于MySQL 8.0版本。

读写锁

处理并发读/写访问的系统通常实现一个由两种锁类型组成的锁系统。这两种锁通常被称为共享锁(shared lock)和排他锁(exclusive lock),也叫读锁(read lock)和写锁(write lock)。

读锁

资源上的读锁是共享的,或者说是相互不阻塞的。多个客户端可以同时读取同一个资源而互不干扰。

写锁

写锁则是排他的,也就是说,一个写锁既会阻塞读锁也会阻塞其他的写锁,这是出于安全策略的考虑,只有这样才能确保在特定的时间点只有一个客户端能执行写入,并防止其他客户端读取正在写入的资源。

锁粒度

一种提高共享资源并发性的方式就是让锁定对象更有选择性。尽量只锁定包含需要修改的部分数据,而不是所有的资源。但是锁的范围越小,管理锁的逻辑就会越复杂,开销也越大。因此锁定策略是锁开销和数据安全性之间的平衡,这种平衡会影响性能。MySQL提供了两种粒度的锁。

表锁

InnoDB的表级别锁包含五种锁模式:LOCK_IS、LOCK_IX、LOCK_X、LOCK_S以及LOCK_AUTO_INC锁。

LOCK_IS/LOCK_IX

也就是所谓的意向锁,这实际上可以理解为一种“暗示”未来需要什么样行级锁,IS表示未来可能需要在这个表的某些记录上加共享锁,IX表示未来可能需要在这个表的某些记录上加排他锁。意向锁是表级别的,IS和IX锁之间相互并不冲突,但与表级S/X锁冲突。

在对记录加S锁或者X锁时,必须保证其在相同的表上有对应的意向锁或者锁强度更高的表级锁。

LOCK_X

当加了LOCK_X表级锁时,所有其他的表级锁请求都需要等待。X锁的几个情况:

  • DDL操作的最后一个阶段(ha_innobase::commit_inlace_alter_table)对表上加LOCK_X锁,以确保没有别的事务持有表级锁。通常情况下Server层MDL锁已经能保证这一点了,在DDL的commit 阶段是加了排他的MDL锁的。但诸如外键检查或者刚从崩溃恢复的事务正在进行某些操作,这些操作都是直接InnoDB自治的,不走server层,也就无法通过MDL所保护;

  • 当设置会话的autocommit变量为OFF时,执行LOCK TABLE tbname WRITE这样的操作会加表级的LOCK_X锁(ha_innobase::external_lock);

  • 对某个表空间执行discard或者import操作时,需要加LOCK_X锁(ha_innobase::discard_or_import_tablespace)。

LOCK_S
  • 在DDL的第一个阶段,如果当前DDL不能通过ONLINE的方式执行,则对表加LOCK_S锁(prepare_inplace_alter_table_dict);

  • 设置会话的autocommit为OFF,执行LOCK TABLE tbname READ时,会加LOCK_S锁(ha_innobase::external_lock)。

当客户端想对表进行写操作(插入、删除、更新等)时,需要先获得一个写锁,这会阻塞其他客户端对该表的所有读写操作。只有没有人执行写操作时,其他读取的客户端才能获得读锁,读锁之间不会相互阻塞。

表锁的冲突和兼容情况

XIXSIS
XConflictConflictConflictConflict
IXConflictCompatibleConflictCompatible
SConflictConflictCompatibleCompatible
ISConflictCompatibleCompatibleCompatible

从上面的描述我们可以看到LOCK_X及LOCK_S锁在实际的大部分负载中都很少会遇到。主要还是互相不冲突的LOCK_IS及LOCK_IX锁。一个有趣的问题是,每次加表锁时,却总是要扫描表上所有的表级锁对象,检查是否有冲突的锁。很显然,如果我们在同一张表上的更新并发度很高,这个链表就会非常长。

基于大多数表锁不冲突的事实,我们在RDS MYSQL中对各种表锁对象进行计数,在检查是否有冲突时,例如当前申请的是意向锁,如果此时LOCK_S和LOCK_X的锁计数都是0,就可以认为没有冲突,直接忽略检查。由于检查是在持有全局大锁lock_sys->mutex下进行的。在单表大并发下,这个优化的效果还是非常明显的,可以减少持有全局大锁的时间。

LOCK_AUTO_INC

AUTO_INC锁加在表级别,和AUTO_INC、表级S锁以及X锁不相容。锁的范围为SQL级别,SQL结束后即释放。

行锁

使用行级锁(row lock)可以最大程度地支持并发处理(也带来了最大的锁开销)。行级锁是在存储引擎而不是服务器中实现的。行锁依赖索引加锁,如果表没有索引,InnoDB会将锁加在隐藏的聚簇索引上。

Record Locks(行记录锁)

表示这个锁对象只是单纯的锁在记录上,不会锁记录之前的 GAP。比如利用唯一索引查询表中存在的一条记录。

Gap Locks(间隙锁)

间隙锁,锁定一个范围,但不包含这个范围中的行记录。

Next-Key Locks

上面两种锁的结合,锁定一个范围和这个范围内的行记录本身。目标是解决幻读问题(Phantom Problem)。

Record Locks演示

表结构

CREATE TABLE `tx_demo` (
  `id` bigint NOT NULL AUTO_INCREMENT,
  `age` int DEFAULT NULL,
  `name` varchar(255) DEFAULT NULL,
  PRIMARY KEY (`id`),
  KEY `idx_age` (`age`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;

数据

id

age

name

15

5

agc

16

8

agc

17

12

agc

19

13

agc

行记录锁演示

  1. 开始事务后执行SQL

    -- 开始事务后执行SQL
    select * from tx_demo where id = 19 for update;
  2. 查看持有锁的情况

    -- 查看持有锁的情况
    SELECT 
        thread_id,
        INDEX_NAME,
        LOCK_TYPE,
        LOCK_MODE,
        LOCK_STATUS,
        LOCK_DATA
    FROM
        performance_schema.data_locks;

  3. 持有锁的情况 

如上所示,该事务当前持有的锁是索引记录19,LOCK_MODE是X,REC_NOT_GAP。原因是该索引是唯一索引,并且该查询能检索出唯一一条数据。

Gap Locks演示
  1. 开始事务后执行SQL

    -- 开始事务后执行SQL
    select * from tx_demo where age = 10 for update;
  2. 查看持有锁的情况

    -- 查看持有锁的情况
    SELECT 
        thread_id,
        INDEX_NAME,
        LOCK_TYPE,
        LOCK_MODE,
        LOCK_STATUS,
        LOCK_DATA
    FROM
        performance_schema.data_locks;
  3. 持有锁的情况

 

如上所示,因为表中没有age=10的记录,因此Gap Locks锁定了(8,12)这个区间,注意,不包括12个索引记录。

Next-Key Locks演示
  1. 开始事务后执行SQL

    -- 开始事务后执行SQL
    select * from tx_demo where age = 8 for update;

  2. 查看持有锁的情况
    -- 查看持有锁的情况
    SELECT 
        thread_id,
        INDEX_NAME,
        LOCK_TYPE,
        LOCK_MODE,
        LOCK_STATUS,
        LOCK_DATA
    FROM
        performance_schema.data_locks;
  3. 持有锁的情况

 

如上所示,在索引idx_age上,这里涉及到的Record Locks是索引记录8,涉及到的Gap Locks有两个,分别是(5,8)、(8,12),因此结合起来就是Next-Key Locks (5,8]和Gap Locks(8,12)。 表中Next-Key Locks (5,8]的表示就是LOCK_DATA为8,16那一行,LOCK_MODE是X;表中Gap Locks(8,12)的表示就是LOCK_DATA为12,17那一行,LOCK_MODE是X,REC_NOT_GAP

LOCK_MODE的含义

MySQL表performance_schema.data_locks中LOCK_MODE列的含义

  • LOCK_MODE = X,说明是 X 型的 next-key 锁;

  • LOCK_MODE = X, REC_NOT_GAP,说明是 X 型的记录锁;

  • LOCK_MODE = X, GAP,说明是 X 型的间隙锁;

  • LOCK_MODE = AUTO_INC,说明是表级自增锁;

  • LOCK_MODE = UNKNOWN,说明是未知模式锁;

参考文档

https://dev.mysql.com/doc/refman/8.0/en/innodb-locking.html

https://dev.mysql.com/doc/refman/8.0/en/performance-schema-data-locks-table.html

https://xiaolincoding.com/mysql/lock/show_lock.html#%E5%87%86%E5%A4%87%E5%B7%A5%E4%BD%9C

http://mysql.taobao.org/monthly/2016/01/01/

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1475747.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

网络编程-编码与解码(Protobuf)

编码与解码 下面的文字都来自于极客时间 为什么要编解码呢?因为计算机数据传输的是二进制的字节数据 解码:字节数据 --> 字符串(字符数据) 编码:字符串(字符数据)–> 字节数据 我们在编…

车载测试-常用adb命令和使用场景

app安装: 最常用 adb install apk地址 应用安装,常用于直接在系统上安装新包用于测试验证bug 常用参数(一般直接使用这三个参数一起) -t 允许测试包 -r 替换已存在的应用程序,也就是说强制安装 -d 允许进行将见状…

【.NET Core】深入理解IO之File类

【.NET Core】深入理解IO之File类 文章目录 【.NET Core】深入理解IO之File类一、概述二、File类2.1 File.AppendAllLines方法2.2 File.AppendAllText方法2.3 File.Copy 方法2.4 File.Create 方法2.5 File.Decrypt(String) 方法2.6 File.Delete(String) 方法2.7 File.Move 方法…

基于Python网络爬虫的IT招聘就业岗位数据分析可视化推荐系统

文章目录 基于Python网络爬虫的IT招聘就业岗位数据分析可视化推荐系统项目概述招聘岗位数据爬虫分析系统展示用户注册登录系统首页IT招聘数据开发岗-javaIT招聘数据开发岗-PythonIT招聘数据开发岗-AndroidIT招聘数据开发岗-其它招聘岗位数据分析算法方面运维方面测试方面招聘岗…

C语言 变量

变量其实只不过是程序可操作的存储区的名称。C 中每个变量都有特定的类型,类型决定了变量存储的大小和布局,该范围内的值都可以存储在内存中,运算符可应用于变量上。 变量的名称可以由字母、数字和下划线字符组成。它必须以字母或下划线开头…

Python入门到精通(九)——Python数据可视化

Python数据可视化 一、JSON数据格式 1、定义 2、python数据和JSON数据转换 二、pyecharts 三、折线图 四、地图 五、动态柱状图 一、JSON数据格式 1、定义 JSON是一种轻量级的数据交互格式。可以按照JSON指定的格式去组织和封装数据JSON本质上是一个带有特定格式的字符…

python|闲谈2048小游戏和数组的旋转及翻转和转置

目录 2048 生成数组 n阶方阵 方阵旋转 顺时针旋转 逆时针旋转 mxn矩阵 矩阵旋转 测试代码 测试结果 翻转和转置 2048 《2048》是一款比较流行​的数字游戏​,最早于2014年3月20日发行。原版2048由Gabriele Cirulli首先在GitHub上发布,后被移…

LeetCode142. 环形链表 II刷题详解

今天力扣刷到了一个特别有意思的题目,于是就写了下面的题解来加深以下理解。 142. 环形链表 II - 力扣(LeetCode) 这个可以分为两大步去写,首先要判断链表是否有环,然后如果有环就去找到环的入口,没有环返…

11 PLL IP核

PLL IP 核简介 锁相环(PLL)作为一种反馈控制电路,其特点是利用外部输入的参考信号来控制环路内部震荡信号的频率和相位。因为锁相环可以实现输出信号频率对输入信号频率的自动跟踪,所以锁相环通常用于闭环跟踪电路。锁相环在工作…

一文读懂压敏电阻原理,参数,选型

大家好,我是砖一。 压敏电阻并不是一般的电阻,而是一种具有瞬态电压抑制功能的元件,效果同TVS。 这篇文章介绍压敏电阻的一些基本知识,包括参数、选型、应用等。 一,基础知识 压敏电阻用MY表示,MY后…

东方博宜 1078. 求恰好使s=1+1/2+1/3+…+1/n的值大于X时n的值

东方博宜 1078. 求恰好使s11/21/3…1/n的值大于X时n的值 #include<iostream> using namespace std; int main() {int x ;cin >> x ;double s 0 ;int i 1 ;bool m true ;while(m){s 1.0/i ;if(s>x)break;i ;}cout << i ;return 0 ; }

【C++那些事儿】深入理解C++类与对象:从概念到实践(上)| 揭开this指针的神秘面纱

&#x1f4f7; 江池俊&#xff1a; 个人主页 &#x1f525;个人专栏&#xff1a; ✅数据结构冒险记 ✅C那些事儿 &#x1f305; 有航道的人&#xff0c;再渺小也不会迷途。 文章目录 1. 面向过程和面向对象初步认识2.类的引入3.类的定义4.类的访问限定符及封装4.1 访问限定符…

SpringBoot案例(黑马学习笔记)

这个案例呢&#xff0c;就是Tlias智能学习辅助系统。 参考接口文档完成后端功能的开 发&#xff0c;然后结合前端工程进行联调测试即可。 完成后的成品效果展示&#xff1a; 准备工作 需求&环境搭建 需求说明 部门管理 部门管理功能开发包括&#xff1a; ● 查询部门列…

C++ 高频考点

1. C/C内存有哪几种类型&#xff1f; C中&#xff0c;内存分为5个区&#xff1a;堆(malloc)、栈(如局部变量、函数参数)、程序代码区&#xff08;存放二进制代码&#xff09;、全局/静态存储区&#xff08;全局变量、static变量&#xff09;和常量存储区&#xff08;常量&…

Linux如何磁盘分区2

gdisk GPT 128个分区 注意&#xff1a;转换格式时保证此分区没有被系统占用 parted -s /dev/sdc mklabel gpt #将/dev/sdc(MBR格式)转换成(GPT格式) parted -s /dev/sdc mklabel msdos #将/dev/sda(GPT格式)转换成(MBR格式) 1.创建分区 yum -y install gdisk #安装分…

Postman越来越难用了

今天看到有个哥们吐槽postman的新版界面&#xff0c;这位是Spotify的资深工程师&#xff0c;所以他的槽点还是有些代表性&#xff0c;他是这么说的 Uhhhh what happened to Postman? I just want to send, and inspect http requests? What is all this shit? Adam Rackis S…

【黑马程序员】3、TypeScript常用类型_黑马程序员前端TypeScript教程,TypeScript零基础入门到实战全套教程

课程地址&#xff1a;【黑马程序员前端TypeScript教程&#xff0c;TypeScript零基础入门到实战全套教程】 https://www.bilibili.com/video/BV14Z4y1u7pi/?share_sourcecopy_web&vd_sourceb1cb921b73fe3808550eaf2224d1c155 目录 3、TypeScript常用类型 3.1 类型注解 …

SD-WAN技术:优化国内外服务器访问的关键

在全球化的商业环境中&#xff0c;企业经常需要在国内访问国外的服务器。然而&#xff0c;由于地理位置和网络架构的限制&#xff0c;这种跨国访问往往会遇到速度慢、延迟高等问题。SD-WAN&#xff08;软件定义广域网&#xff09;技术的兴起&#xff0c;为企业提供了一种新的解…

kotlin与java的相互转换

Kotlin转java 将kotlin代码反编译成java Tools -> Kotlin -> Show Kotlin Bytecode 然后点击 【Decompile】 生成java代码 java转kotlin Code -> Convert Java File To Kotlin File

代码随想录算法训练营第二十七天| 39. 组合总和、40.组合总和II、131.分割回文串

文章目录 1.组合总和2.组合总和II3.分割回文串 1.组合总和 参数和返回值&#xff1a; vector<vector<int>> result; vector<int> path; int sum; void backtracking(vector<int>& candidates, int target, int index)终止条件&#xff1a;大于等于…