SQL锁表是数据库运行中常见的并发控制机制,但如果锁表时间过长或者出现死锁,会导致后续的业务SQL无法执行,引发接口超时、数据更新失败等问题,快速定位锁表是解决问题的第一步。

MySQL数据库的锁表定位方法
MySQL中可以通过系统库和性能_schema来查询锁表信息,常用的方式有以下两种。
使用information_schema库查询
MySQL的information_schema库中的INNODB_TRX表记录了当前运行的事务信息,INNODB_LOCKS表记录了当前的锁信息,INNODB_LOCK_WAITS表记录了锁等待关系,通过关联这几个表可以快速找到锁表的相关信息。
-- 查询当前锁等待的相关信息,包含阻塞方和被阻塞方的SQL、会话ID等
SELECT
r.trx_id AS 等待事务ID,
r.trx_mysql_thread_id AS 等待线程ID,
r.trx_query AS 等待执行的SQL,
b.trx_id AS 阻塞事务ID,
b.trx_mysql_thread_id AS 阻塞线程ID,
b.trx_query AS 阻塞方执行的SQL,
b.trx_started AS 阻塞事务开始时间
FROM information_schema.INNODB_LOCK_WAITS w
INNER JOIN information_schema.INNODB_TRX b ON b.trx_id = w.blocking_trx_id
INNER JOIN information_schema.INNODB_TRX r ON r.trx_id = w.requesting_trx_id;
使用performance_schema查询
MySQL 5.7及以上版本可以通过performance_schema库的data_locks和data_lock_waits表查询更详细的锁信息,这种方式支持更细粒度的锁类型查询。
-- 查询当前所有的锁信息
SELECT
OBJECT_SCHEMA AS 库名,
OBJECT_NAME AS 表名,
INDEX_NAME AS 索引名,
LOCK_TYPE AS 锁类型,
LOCK_MODE AS 锁模式,
LOCK_STATUS AS 锁状态,
THREAD_ID AS 线程ID
FROM performance_schema.data_locks
WHERE OBJECT_SCHEMA NOT IN ('mysql', 'information_schema', 'performance_schema');
PostgreSQL数据库的锁表定位方法
PostgreSQL可以通过系统视图pg_locks和pg_stat_activity来定位锁表问题,这两个视图分别记录了锁信息和活跃会话信息。
-- 查询当前锁表相关的信息,包含锁定的表、阻塞SQL、会话PID等
SELECT
a.pid AS 会话PID,
a.usename AS 用户名,
a.application_name AS 应用名,
a.client_addr AS 客户端地址,
a.state AS 会话状态,
a.query AS 执行的SQL,
l.locktype AS 锁类型,
l.mode AS 锁模式,
l.granted AS 是否获得锁,
n.nspname AS 模式名,
c.relname AS 表名
FROM pg_locks l
JOIN pg_stat_activity a ON l.pid = a.pid
LEFT JOIN pg_class c ON l.relation = c.oid
LEFT JOIN pg_namespace n ON c.relnamespace = n.oid
WHERE c.relname IS NOT NULL
ORDER BY l.granted DESC, a.query_start ASC;
SQL Server数据库的锁表定位方法
SQL Server可以通过系统存储过程sp_lock或者动态管理视图sys.dm_tran_locks来查询锁信息,同时结合sys.dm_exec_requests可以获取阻塞的SQL语句。
-- 查询当前锁表信息及对应的SQL语句
SELECT
tl.request_session_id AS 会话ID,
tl.resource_type AS 资源类型,
tl.resource_database_id AS 数据库ID,
DB_NAME(tl.resource_database_id) AS 数据库名,
tl.resource_associated_entity_id AS 关联实体ID,
OBJECT_NAME(tl.resource_associated_entity_id) AS 表名,
tl.request_mode AS 请求锁模式,
tl.request_status AS 请求状态,
er.blocking_session_id AS 阻塞会话ID,
SUBSTRING(est.text, (er.statement_start_offset/2)+1,
((CASE er.statement_end_offset
WHEN -1 THEN DATALENGTH(est.text)
ELSE er.statement_end_offset
END - er.statement_start_offset)/2) + 1) AS 执行的SQL
FROM sys.dm_tran_locks tl
LEFT JOIN sys.dm_exec_requests er ON tl.request_session_id = er.session_id
OUTER APPLY sys.dm_exec_sql_text(er.sql_handle) est
WHERE tl.resource_type IN ('OBJECT', 'PAGE', 'RID', 'KEY')
ORDER BY tl.request_session_id;
锁表定位后的处理建议
定位到锁表的源头后,可以根据实际情况处理:如果是长时间未提交的事务导致的锁表,可以联系相关人员提交事务,或者谨慎执行KILL命令终止对应的会话;如果是业务SQL写法不合理导致的频繁锁表,需要优化SQL逻辑,比如避免大事务、给查询字段添加合适的索引减少锁范围等。
日常运维中也可以定期监控数据库的锁表情况,提前发现潜在的锁表风险,避免影响业务正常运行。