欢迎来到尧图网

客户服务 关于我们

您的位置:首页 > 新闻 > 焦点 > 生产环境MYSQL常见锁表场景

生产环境MYSQL常见锁表场景

2025/6/23 4:21:48 来源:https://blog.csdn.net/u010565910/article/details/148316852  浏览:    关键词:生产环境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;

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com

热搜词