【MySQL】说透锁机制(三)行锁升表锁如何避免? 锁表了如何排查?

news2024/9/23 13:24:00

文章目录

  • 前言
  • 哪些场景会造成行锁升表锁?
  • 如何避免?
  • 如何分析排查?
    • 查看`InnoDB_row_lock%`相关变量
    • 查看 `INFORMATION_SCHEMA`系统库
  • 总结
  • 最后


前言

在上文我们曾小小的提到过,在索引失效的情况下,MySQL会把所有聚集索引记录和间隙都锁上,我们称之为锁表,或叫行锁升表锁.

那么对于 行锁升表锁,有的同学误以为行锁 升级变成了 表锁,但实际上锁的类型并没有发生变化✍️,还是行锁! 只是表的所有聚集索引记录都被加上了行锁, 看起来像表锁, 所以提前澄清一下, 举个例子:

假设,表中有10万多条记录

  • 行锁升表锁
    会给10万多条索引记录加行锁, 锁的粒度小, 但开销非常大,示意图如下:
    在这里插入图片描述
  • 直接加 表锁
    只会加1个表锁,锁的粒度大, 但开销非常小,示意图如下:
    在这里插入图片描述

OK, 相信已经澄清了~ 那么对于行锁升表锁, 我们应该如何避免呢? 如果真被行锁锁表了又该如何分析排查呢? 别着急, 我们一步一步来, 干货满满, 建议先收藏!后面如果有需要了, 直接能找到这里来看.


哪些场景会造成行锁升表锁?

兵法有云:知己知彼,百战不殆!
所以在说如何避免之前,我们提前说一下哪些场景会造成行锁升表锁,建议还未看过前面两文的小伙伴先了解一下加锁规则:
【MySQL】说透锁机制(一)行锁 加锁规则 之 等值查询
【MySQL】说透锁机制(二)行锁 加锁规则 之 范围查询(你知道会锁表吗?)
那么对于看过前两篇文章的小伙伴,应该已经猜到了,场景肯定和索引有关!

没错, 就是 无索引索引失效!
那么原因呢? 你想过这里的原因吗?
在这里插入图片描述
解读:因为InnoDB引擎的 3种行锁算法(Record Lock、Gap Lock、Next-key Lock),都是锁定的索引,当触发X锁(写锁)的where条件无索引 或 索引失效 时, 查找的方式就会变成全表扫描,也就是扫描所有的聚集索引记录,到这我想大家都应该看懂了,但是可能还有个疑问,为什么要把不匹配的记录也加锁呢?
这里是针对于默认的事务隔离级别:可重复读(RR)事务隔离级别来说的, 因为在RR隔离级别下,需要解决不可重复读幻读问题, 所以在遍历扫描聚集索引记录时, 为了防止扫描过的索引被其它事务修改(不可重复读问题) 或 间隙被其它事务插入记录(幻读问题), 从而导致数据不一致, 所以MySQL的解决方案就是把所有扫描过的索引记录和间隙都锁上, 这也就 发生了我们看到的锁表!💪💪💪

在这里插入图片描述
展开来说:

无索引
例如, 下面这个sql的 remark列 不是索引列, 如果按remark更新就是无索引更新.

update ct set abc = 1 
where remark = '阿根廷';

索引失效
索引失效的情况有很多, 我们本文不分析为什么失效, 也不会列举出所有失效的场景, 因为那不是本节的重点(我会考虑单独安排一篇详细讲解)。 这里直接用explain说话:

  • explain 返回的key不是你期望的索引, 而是PRIMARY;
  • explain 返回的typeindexall

如果同时满足上面这两个条件, 那么就说明索引失效了!

对于索引失效列几个常见的场景简单说明一下:

  • 复合索引未遵循最左前缀原则

例如,我新建一个复合索引:abc列name列,如下:

 ALTER TABLE `lock_test`.`ct`
 ADD INDEX `idx_abc_name`(`abc`, `name`);
但更新sql语句未按照最左前缀, 直接按`name=`更新,这样就会**导致索引失效**:
update ct set abc = 1 
where name = '阿根廷';

看一下explain的结果
在这里插入图片描述

  • like以%开头

例如,我新建一个普通索引:name列

ALTER TABLE `lock_test`.`ct`
ADD INDEX `idx_name`(`name`);

但更新sql语句使用了 like以%开头,这样也会导致索引失效

update ct set abc = 1 
where name like '%阿根廷';

看一下explain的结果
在这里插入图片描述

  • MySQL成本计算分析认为全表扫描成本更低时

这是比较特殊的情况. 同样的SQL, 传入的参数不同, explain的结果也不同, 有时会走索引, 但有时索引又失效! 😫
这里的原因:因为根据传入的参数不同 导致 结果集不同, 在正式扫描之前,MySQL会进行成本计算计算走哪个索引更快!结果一算,发现走索引还不如全表扫描快, 那么这时即使你用的是索引列等值 也不会走索引,会走全表扫描,这也就导致了索引失效
关于成本计算, 它是先计算不同索引的I/0成本和CPU成本, 然后进行对比, 哪个成本低就采用哪个索引来执行! 当然, 成本计算并不会真实执行, 所以速度非常快, 在上文【范围查询】时曾给过一个小的示例说明,这里不再重复赘述!
在这里插入图片描述

当然,索引失效的情况还有很多, 这里只是举几个例子让大家学会用explain分析, 如果不够过瘾,我后面紧接着会更新索引相关文章!记得关注我哦!


如何避免?

此时, 咱们已经清楚的知道了 可能造成 行锁升表锁 的场景,那么应对起来也就更有底气了,我的建议是:

  • 禁止where条件使用无索引列进行更新/删除
    这是我们最应该做到的!除了会锁表,性能也是真的不好!
  • 尽可能使用聚集索引进行更新/删除
    这是我们能做到的最优做法
  • 确实需要使用非聚集索引 进行更新/删除,需要确认:
    • 使用explain检查是否会索引失效!
    • 避免对 索引列 进行类型转换、函数、运算符等会造成升级的情况!
    • 尽可能减少检索条件范围, 范围越大就越可能被MySQL成本计算太高,从而导致索引失效!
  • 尽可能控制事务大小,减少锁定时间
    涉及事务加锁的sql语句尽可能放在事务最后执行!
  • 推荐使用读已提交(RC)事务隔离级别
    这条非常重要!
    对于读已提交(RC)事务隔离级别,由于没有间隙锁(Gap Lock),所以它的加锁规则相当简单,都是针对匹配索引记录加Record Lock,因为不用解决不可重复读幻读问题,所以也就不存在 锁表了。

    前面两文咱们说的都是基于可重复读(RR)事务隔离级别,因为引入了间隙锁(Gap Lock),所以情况变的复杂, 而在RC下, 情况变的简单.


如何分析排查?

咱们只能做到尽可能避免, 根据墨菲定律:只要有可能 就一定会发生!
所以我们必须掌握锁表应该如何分析排查

查看InnoDB_row_lock%相关变量

show status like 'innodb_row_lock%';

在这里插入图片描述

字段说明
Innodb_row_lock_current_waits当前正在等待锁定的数量
Innodb_row_lock_time等待总时长: 从系统启动到现在锁定总时间长度
Innodb_row_lock_time_avg等待平均时长: 每次等待所花平均时间
Innodb_row_lock_time_max从系统启动到现在等待最长的一次所花时间
Innodb_row_lock_waits等待总次数: 系统启动后到现在总共等待的次数

从上述值,我们可以看出我们行锁的整体情况,有助于我们分析。

查看 INFORMATION_SCHEMA系统库

我们可以通过 INFORMATION_SCHEMA系统库提供的:查看事务锁等待的 数据表 来分析.

-- 查看事务
select * from INFORMATION_SCHEMA.INNODB_TRX;
-- 查看锁
select * from INFORMATION_SCHEMA.INNODB_LOCKS;
-- 查看锁等待
select * from INFORMATION_SCHEMA.INNODB_LOCK_WAITS;
-- 查看连接情况
select * from INFORMATION_SCHEMA.PROCESSLIST;
  • 通过 INNODB_LOCK_WAITS 可以找出阻塞的事务id和锁id
-- 查看锁等待
select * from INFORMATION_SCHEMA.INNODB_LOCK_WAITS;
字段说明
requesting_trx_id请求的事务id
requested_lock_id请求的锁id
blocking_trx_id阻塞的事务id
blocking_lock_id阻塞的锁id

我这里模拟一个锁等待,然后查询,可以清晰的看到谁阻塞了谁
在这里插入图片描述

温馨提示:只有发生锁等待才有数据

  • 通过 INNODB_LOCKS 可以查看上锁的详细信息
-- 查看锁
select * from INFORMATION_SCHEMA.INNODB_LOCKS;

这和我们通过show engine innodb status\G;看到的结果类似, 略…, 也是只有发生阻塞才会有数据.

  • 通过 INNODB_TRX 可以查看 事务的状态、阻塞开始时间、阻塞的sql、线程id等等
 -- 查看事务
 select * from INFORMATION_SCHEMA.INNODB_TRX;

这个表很关键, 对于我们排查来说必不可少, 一些关键字段说明如下:

字段说明
trx_id事务id
trx_state事务状态,LOCK WAIT代表发生了锁等待
trx_started事务开始时间
trx_requested_lock_id请求锁id, 事务当前正在等待锁的标识,可以join关联INNODB_LOCKS.lock_id
trx_wait_started事务开始锁等待的时间
trx_weight事务的权重
trx_mysql_thread_id事务线程 ID,可以join关联PROCESSLIST.ID
trx_query事务正在执行的 SQL 语句
trx_operation_state事务当前操作状态
trx_isolation_level当前事务的隔离级别

当发生阻塞时,我们来看一下数据:
在这里插入图片描述
一目了然,哪个SQL从什么时间开始阻塞,线程id是多少,看的一清二楚.

  • 通过 PROCESSLIST 可以查看连接情况
-- 查看连接情况
select * from INFORMATION_SCHEMA.PROCESSLIST;

通过这个表,我们可以定位到事务所在的主机.

字段说明
ID线程ID, 可以JOIN INNODB_TRX.trx_requested_lock_id
USER连接用户
HOST连接主机 ip:port
DB连接的数据库
  • 如何kill某个事务?

通过对上面的表进行查询, 当我们发现某个事务阻塞了很多事务, 并且执行时间很长时, 我们可以手动中止它, 只需要找到INNODB_TRX.trx_mysql_thread_id,然后调用kill命令:

kill {INNODB_TRX.trx_mysql_thread_id}

总结

本文主要介绍了:

  • 哪些场景会造成行锁升表锁
    无索引索引失效
  • 如何避免
    建议中最重要的一条:尽可能使用 读已提交(RC)事务隔离级别
  • 如何分析排查
    最重要的两个分析表:INFORMATION_SCHEMA.INNODB_TRXINFORMATION_SCHEMA.INNODB_LOCK_WAITS,以及手动中止 kill {INNODB_TRX.trx_mysql_thread_id}

最后

如果感觉不错,欢迎订阅本专栏,后面还有更详细的MySQL知识陆续放出。
关注我 天罡gg 分享更多干货: https://blog.csdn.net/scm_2008
大家的「关注 + 点赞 + 收藏」就是我创作的最大动力!谢谢大家的支持,我们下文见!


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

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

相关文章

【正点原子】嵌入式Linux C应用编程-第十一章

第十一章:线程 前言: 与进程类似,线程是允许应用程序并发执行多个任务的一种机制,线程参与系统调度,事实上,系统调度的最小单元为线程,而不是进程 1:线程的概念 什么是线程&#xff…

【游戏编程扯淡精粹】自研引擎切 UE

【游戏编程扯淡精粹】自研引擎切 UE UF2022 的两篇讲座,再加上 The Machinery 引擎项目失败 结合过去两年笔者使用自研引擎的体验,其实有一部分是共通的 Crystal Dynamics:如何从自研引擎转变到虚幻引擎5 游戏技术(featurelist…

LVGL的学习及使用

1、LVGL简介 LVGL是最受欢迎的免费开源嵌入式图形库,可为任何MCU、MPU和显示器类型创建漂亮的用户界面。使用SquareLine工作室,使用拖放UI编辑器来简化开发。 1.1、LVGL源码下载 lvgl 在github 上的开源代码 https://github.com/lvgl/lvgl 下载的源码包里…

Vue的数据绑定

一、Vue的数据绑定 1、单向数据绑定:将Model绑定到View上,当通过JavaScript代码改变了Model时,View就会自动刷新。不需要进行额外的DOM 操作就可以实现视图和模型的联动 ​ a、数据只保存一份 ​ b、data—->DOM ​ (1&am…

在wsl下开发T113的主线linux(3)-写入spinand测试

接下来是烧写入硬件验证,我的板子焊接的是W25N01GV,这里使用xfel,因为支持写入spi-nand。GitHub - xboot/xfel: Tiny FEL tools for allwinner SOC, support RISC-V D1 chipTiny FEL tools for allwinner SOC, support RISC-V D1 chip - GitH…

QML学习笔记【04】:常用控件

1 Repeater与model Window {width: 640; height: 480visible: truetitle: qsTr("Hello World")Column{id: colspacing: 30Repeater{model: 3 //model控制了所有的数据,这里定义了Button的数量Button{width: 100; height: 50text: "btn" index…

整数划分——完全背包的变形

整数划分——完全背包的变形一、题目二、思路分析1、状态转移方程(1)状态表示(2)方程书写2、循环与初始化(1)循环(2)初始化三、代码一、题目 二、思路分析 这道题这么看的话还是比较…

Docsify使用之Markdown语法

Docsify使用过程中的排版,他是基于Markdown语法的。我们来看一下使用的常用语法: 字体加粗: 在需要加粗的文字前后各加两个** 具体格式如下 **加粗内容** 在需要加粗的文字前后各加一个* 具体格式如下 *倾斜内容* 在需要加粗并且倾斜的…

缅怀2022,展望2023

个人主页:董哥聊技术我是董哥,嵌入式领域新星创作者创作理念:专注分享高质量嵌入式文章,让大家读有所得!文章目录1、缘起2、收获3、憧憬不知不觉,2022已然到了最后一天,同时也是我技术创作一周年…

2.脚手架和逆向工程-使用renren开源

1.脚手架工程 脚手架工程提供了业务模块通用的类,比如返回结果封装、异常封装、分页工具类等 比较好用脚手架工程如renren-fast 备份地址 gitgithub.com:nome1024/renren-fast.git 2.逆向工程——使用renren-generator生成代码 逆向工程的作用是根据数据库快速生…

2022年仪器仪表行业研究报

第一章 行业概况 仪器仪表是用以检出、测量、观察、计算各种物理量、物质成分、物性参数等的器具或设备。真空检漏仪、压力表、测长仪、显微镜、乘法器等均属于仪器仪表。仪器仪表是人们对客观世界的各种信息进行测量、采集、分析与控制的手段和设备,是人类了解世界…

MySQL之表的修改和约束条件的添加

修改表中的数据:update[DML] 语法格式: update 表名 set 字段名1值1,字段名2值2,字段名3值3......where 条件;注意:没有条件限制会导致所有数据全部更新 举例: - 将id号为10的学生的姓名改变为"jas…

【自学Python】解释型程序与编译型程序

解释型程序与编译型程序 解释型程序与编译型程序教程 高级语言所编制的程序不能直接被计算机识别,必须经过转换才能被执行,按转换方式可将它们分为两类:解释型程序与编译型程序。 解释型程序 执行方式类似于我们日常生活中的 “同声翻译”…

Vue--》实现todo-list组件的封装与使用

目录 项目结构 创建todolist组件 创建todoinput组件 创建todobutton组件 项目结构 今天用 vite 脚手架搭建一个 vue3 的小案例,vite的搭建过程参考:vite的搭建 。其项目结构组件构成如下:注意:因为使用的是 vite 框架&#x…

51单片机GMS短信自动存取快递柜

实践制作DIY- GC0103-直流电机PID速度控制 一、功能说明: 基于51单片机设计-GMS短信自动存取快递柜 功能介绍: STC89C52RC最小系统板0.96寸OLED显示器DY-SV17F串口语音播报模块4*4矩阵键盘GSM短信模块4路舵机(模拟4个柜子) ***…

再学C语言21:循环控制语句——do while循环

一、其他赋值运算符 除了最基本的赋值运算符&#xff0c;C还有多个赋值运算符 a b等于a a ba - b等于a a - ba * b等于a a * ba / b等于a a / ba % b等于a a % b 这些赋值运算符的优先级与赋值运算符同样低 运算符优先级&#xff1a;赋值运算符 < 关系运算符 <…

《C++程序设计原理与实践》笔记 第9章 类相关的技术细节

在本章中&#xff0c;我们继续关注主要的程序设计工具——C语言。本章主要介绍与用户自定义类型&#xff08;即类和枚举&#xff09;相关的语言技术细节。这些语言特性大部分是以逐步改进一个Date类型的方式来介绍的。采用这种方式&#xff0c;我们还可以顺便介绍一些有用的类设…

数据结构与算法_五大算法之分治算法

这篇笔记记录分治算法的思想和两道leetcode题。 分治算法思想&#xff1a; 规模为n的原问题的解无法直接求出&#xff0c;进行问题规模缩减&#xff0c;划分子问题&#xff0c;子问题相互独立而且和原问题解的性质是相同的&#xff0c;只是问题规模缩小了。递归地缩小问题规模…

用Python记录一场2023的烟花

弹指间&#xff0c;2023已经到来&#xff0c;新的一年&#xff0c;祝大家新年快乐&#xff0c;阖家幸福呀~~~ 好吧&#xff0c;进入正题&#xff0c;2023的到来&#xff0c;肯定少不了烟花吧&#xff08;外面不让放炮&#xff0c;那咱们就用python放炮【DOGE】&#xff09; 首…

JSON Web Tokens(JWT)简单使用

文章目录什么是JWT&#xff1f;JWD对字符串进行Base64加密JWT加密字符串解释JWT使用场景jwt 特点JWT token在线解密什么是JWT&#xff1f; JWT&#xff08;json web token&#xff09;&#xff0c;它并不是一个具体的技术实现&#xff0c;而更像是一种标准。 JWT规定了数据传输…