当前位置: 首页 > article >正文

生产环境MYSQL常见锁表场景

前言

锁表是我们在生产环境十分常见的问题之一,解决问题前需要先了解锁表产生的原因以找到解决方案,并制定方案以预防锁表,本文接下来会分别模拟元数据锁表(MDL锁)行锁升级为表锁死锁、**显示锁表
**四种锁表情形。

准备工作

  • 安装了MYSQL和MYSQL客户端的带可视化界面的终端系统(windows、mac、可视化ubuntu等)。
  • 分别打开三个MYSQL客户端A、B、C(会话C可以替换为navicat等可视化连接工具操作)。
  • 创建测试表
-- 创建测试数据库
CREATE DATABASE IF NOT EXISTS locktest;
USE locktest;-- 创建测试表
CREATE TABLE test_table (id INT PRIMARY KEY AUTO_INCREMENT,name VARCHAR(50),value INT
);-- 插入一些测试数据
INSERT INTO test_table (name, value) VALUES 
('测试1', 100),
('测试2', 200),
('测试3', 300),
('测试4', 400),
('测试5', 500);

元数据锁表(MDL锁)

说明

当前进程执行DDL语句时需要获取MDL锁,若此时其他进程正在执行事务且包含当前表的操作语句(读或写),则DDL语句会被阻塞,进入等待锁的状态。

模拟流程

  • 会话A执行:
TART TRANSACTION;
SELECT * FROM test_table;
# 暂时不执行COMMIT
  • 会话B执行:
ALTER TABLE test_table ADD COLUMN new_col INT;
  • 此时会话B会被阻塞,会话C(navicat)查看进程情况,执行:
SHOW PROCESSLIST;
  • navicat上查询结果如下:
    等待元数据锁

  • 可以看到ID=24的进程正在等待元数据锁。这时会话A执行提交:

# 会话A提交事务
commit;
  • 会话A提交事务后释放锁,会话B获取锁执行成功。

总结

这种锁表情形,常见于项目新版本上线时,开发人员A需要执行修改表tableA字段的SQL脚本时,此时开发人员B正在启动事务并查询tableA的数据,导致开发人员A的SQL脚本执行阻塞;或者此时线上项目正在执行包含表tableA操作的事务,该事务需要执行很长时间。

解决方案

行锁升级为表锁

说明

对于大数量的表进行查询时,通常会增加索引以提高查询效率。但如果在DML语句执行期间,如果索引使用不当导致索引失效时,行锁可能会升级为表锁(多行锁)。

模拟流程

  • 给测试表增加索引
create index idx_name on test_table(name);
  • 会话A执行:
START TRANSACTION;
UPDATE test_table SET value = 777 WHERE name LIKE '%测试%';
-- 不执行COMMIT
  • 会话B执行:
UPDATE test_table SET value = 888 WHERE name = '测试2';
  • 此处会话B语句阻塞,会话C(navicat)查看进程:
# 会话C执行
SHOW PROCESSLIST;

在这里插入图片描述

  • 长时间等待后,会话B提示等待锁超时:
    在这里插入图片描述
  • 同样会话A执行commit,语句执行成功后恢复正常
# 会话A提交事务
commit;

总结

在InnoDB引擎中,锁是加在索引上的,当修改语句的索引失效时,innoDB会对扫描到的所有索引记录都加锁(多行锁),直到这条查询语句执行结束。在此期间,其他的进程如果需要修改其中一个被扫描到的索引对应的行记录时,就会因为没有得到行级锁而被阻塞。

解决方案

预防索引失效,索引失效常见情况:

死锁

说明

死锁是线上环境的经典问题,死锁形成的本质就是多进程之间形成了资源等待环,并且无法凭借自身能力解锁。

模拟流程

  • 事务A执行:
START TRANSACTION;
UPDATE test_table SET value = 100 WHERE id = 1;
  • 事务B执行:
START TRANSACTION;
UPDATE test_table SET value = 200 WHERE id = 2;
  • 事务A再执行:
UPDATE test_table SET value = 300 WHERE id = 2;
  • 事务B再执行:
UPDATE test_table SET value = 400 WHERE id = 1;
  • 此时其中一个会话会提示死锁,并且innoDB已强制回滚并终止了其中一个事务:
    在这里插入图片描述

总结

解决方案

  • 检查相关事务的逻辑,尽量保证多个事务的获取锁资源顺序是相同的,比如都是先获取A锁,再获取B锁,最后获取C锁。又或者让获取多个锁的流程作为一个原子操作。

显式锁表

说明

手动执行sql语句LOCK TABLE锁表

模拟流程

  • 会话A执行:
LOCK TABLES test_table WRITE;
-- 现在表被锁定,只有会话A可以读写
  • 会话B常识查询或修改表,会被阻塞:
SELECT * FROM test_table;
-- 或
UPDATE test_table SET value = 500 WHERE id = 1;
  • 会话A释放锁,会话B的语句能继续执行:
UNLOCK TABLES;

总结

解决方案

找到手动锁表的会话,进行手动释放锁即可。

常见观察锁表的SQL语句

-- 查看当前进程列表,观察被阻塞的查询
SHOW PROCESSLIST;-- 查看InnoDB事务和锁信息
SHOW ENGINE INNODB STATUS;-- 查看当前事务
SELECT * FROM information_schema.innodb_trx;-- 查看当前锁
SELECT * FROM performance_schema.data_locks;-- 查看锁等待
SELECT * FROM performance_schema.data_lock_waits;
http://www.lryc.cn/news/2398728.html

相关文章:

  • 结构性设计模式之Composite(组合)
  • Java面试八股--04-MySQL
  • 日语学习-日语知识点小记-构建基础-JLPT-N4阶段(31):そう
  • 设计模式——访问者设计模式(行为型)
  • 实验设计与分析(第6版,Montgomery著,傅珏生译) 第10章拟合回归模型10.9节思考题10.1 R语言解题
  • 《对象创建的秘密:Java 内存布局、逃逸分析与 TLAB 优化详解》
  • LeetCode 高频 SQL 50 题(基础版) 之 【高级查询和连接】· 下
  • Java并发编程:读写锁与普通互斥锁的深度对比
  • Spring Boot Actuator未授权访问漏洞修复
  • 机器学习——SVM
  • 【音视频】FFmpeg 硬件(NVDIA)编码H264
  • 贪心算法应用:超图匹配问题详解
  • OpenCV CUDA模块结构分析与形状描述符------计算指定阶数的矩(Moments)所需的总数量函数:numMoments
  • 【Web应用】若依框架:基础篇13 源码阅读-前端代码分析
  • [java八股文][JavaSpring面试篇]SpringCloud
  • 深度学习篇---face-recognition的优劣点
  • 基于分布式状态机的集装箱智能道口软件架构方法
  • Oracle的Hint
  • 手动事务的使用
  • Vue 树状结构控件
  • Spring Boot的启动流程,以及各个扩展点的执行顺序
  • 【LUT技术专题】图像自适应3DLUT代码讲解
  • Apache Doris 在数据仓库中的作用与应用实践
  • vscode使用“EIDE”和“Cortex-Debug”插件利用st-link插件实现程序烧写以及调试工作
  • Spring @Value注解的依赖注入实现原理
  • 三、kafka消费的全流程
  • 商品模块中的多规格设计:实现方式与电商/ERP系统的架构对比
  • (三)动手学线性神经网络:从数学原理到代码实现
  • Axure形状类组件图标库(共8套)
  • 20250530-C#知识:String与StringBuilder