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

生产环境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.lqws.cn/news/94861.html

相关文章:

  • Vue3 中使用 i18n
  • 08.MySQL复合查询详解
  • 可视化大屏工具对比:GoView、DataRoom、积木JimuBI、Metabase、DataEase、Apache Superset 与 Grafana
  • LeetCode第244题_最短单词距离II
  • C++ Visual Studio 2017厂商给的源码没有.sln文件 易兆微芯片下载工具加开机动画下载。
  • Java复习Day26
  • 登高架设作业实操考试需要注意哪些安全细节?
  • Docker 镜像深度剖析:构建、管理与优化
  • 基于langchain的简单RAG的实现
  • AXURE安装+汉化-Windows
  • Axure形状类组件图标库(共8套)
  • jenkins结合gitlab实现CI
  • 详解开漏输出和推挽输出
  • Apache Doris 在数据仓库中的作用与应用实践
  • pikachu靶场通关笔记12 XSS关卡08-XSS之htmlspecialchars(四种方法渗透)
  • 奥威BI+AI数据分析:企业数智化转型的加速器
  • HTTP Error 400 Bad request 问题分析解决
  • 【前端并发请求控制:必要性与实现策略】
  • 如何进行页面前端监控
  • 手摸手还原vue3中reactive的get陷阱以及receiver的作用
  • SpringBoot3.2新特性:JdbcClient
  • web攻防之SSTI 注入漏洞
  • Windows 下部署 SUNA 项目:虚拟环境尝试与最终方案
  • 【从0-1的HTML】第2篇:HTML标签
  • Double/Debiased Machine Learning
  • 从仿射矩阵得到旋转量平移量缩放量
  • 【氮化镓】GaN HMETs器件物理失效分析进展
  • 【Java Web】7.事务管理AOP
  • 下载并运行自制RAG框架
  • PyTorch——线性层及其他层介绍(6)