- 解锁被事务阻塞的行(
InnoDB引擎):这是最常见的“锁”问题,当一个事务没有提交或回滚,它锁定的资源(比如一行数据)会阻塞其他事务的访问。 - 解锁被用户手动上锁的表(
LOCK TABLES):这是一种较少使用的手动锁定机制。
下面我将详细介绍这两种情况的解锁方法。

解锁被事务阻塞的行 (InnoDB 行锁)
这种情况通常被称为“死锁”或“等待锁”,但实际上更常见的是一个长事务阻塞了其他所有后续事务,解决这个问题的关键在于找到并结束(KILL)那个持有锁的、长时间运行的事务。
第 1 步:找到持有锁的会话(Session)
你需要查询 information_schema 数据库来找出正在持有锁的会话,最常用的查询是 innodb_trx 和 innodb_locks。
查询 1:查看当前正在运行的事务
-- 查看当前所有正在运行的事务,重点关注事务ID、执行时间、状态等
SELECT
trx_id,
trx_state,
trx_started,
trx_requested_lock_id,
trx_wait_started,
trx_mysql_thread_id AS session_id,
trx_query
FROM
information_schema.innodb_trx;
查询 2:查看正在等待锁的会话

-- 查看哪些会话正在等待锁,以及它们在等待什么
SELECT
r.trx_id AS waiting_trx_id,
r.trx_mysql_thread_id AS waiting_thread,
r.trx_query AS waiting_query,
b.trx_id AS blocking_trx_id,
b.trx_mysql_thread_id AS blocking_thread,
b.trx_query AS blocking_query
FROM
information_schema.innodb_lock_waits w
JOIN
information_schema.innodb_trx b ON b.trx_id = w.blocking_trx_id
JOIN
information_schema.innodb_trx r ON r.trx_id = w.requesting_trx_id;
查询 3:查看所有会话的详细信息(推荐)
这个查询非常全面,能帮你快速定位问题会话。
SELECT
r.trx_id AS waiting_trx_id,
r.trx_mysql_thread_id AS waiting_thread_id,
r.trx_query AS waiting_query,
b.trx_id AS blocking_trx_id,
b.trx_mysql_thread_id AS blocking_thread_id,
b.trx_query AS blocking_query,
l.lock_table,
l.lock_index,
l.lock_type,
l.lock_mode
FROM
information_schema.innodb_lock_waits w
JOIN
information_schema.innodb_trx b ON b.trx_id = w.blocking_trx_id
JOIN
information_schema.innodb_trx r ON r.trx_id = w.requesting_trx_id
JOIN
information_schema.innodb_locks l ON l.lock_trx_id = w.blocking_trx_id;
从以上查询结果中,你需要记下 blocking_thread_id(或 trx_mysql_thread_id)的值,这就是导致阻塞的会话ID。
第 2 步:结束持有锁的会话
一旦你找到了 blocking_thread_id,就可以使用 KILL 命令来强制终止这个会话,这会立即回滚该会话中的所有未提交事务,从而释放所有锁。

-- 将 '12345' 替换为你找到的 blocking_thread_id KILL 12345;
⚠️ 重要警告:
- 数据丢失风险:
KILL命令会立即终止会话,该会话中所有未提交的更改(INSERT,UPDATE,DELETE)都将被回滚,这可能导致数据不一致,尤其是在应用程序中。 - 谨慎操作:在生产环境中执行
KILL命令前,请务必确认该会话确实是一个“流氓”会话,并且终止它带来的负面影响小于让它继续阻塞整个系统。 - 先备份:如果可能,先通知相关业务人员,或者在业务低峰期执行。
解锁被手动锁定的表 (LOCK TABLES)
这是一种比较老旧的锁定方式,现代应用中很少使用,当一个会话执行了 LOCK TABLES 命令后,它会锁定指定的表,直到它执行 UNLOCK TABLES 或会话结束。
第 1 步:找到持有表锁的会话
-- 查看当前所有持有表锁的会话
SELECT
id,
db,
user,
host,
command,
time,
state,
info
FROM
information_schema.processlist
WHERE
command = 'Locked';
或者更通用的查询:
SELECT
id,
db,
user,
host,
command,
time,
state,
info
FROM
information_schema.processlist
WHERE
state = 'Locked' OR command = 'Locked';
记下查询结果中的 id 列,这就是持有表锁的会话ID。
第 2 步:解锁表
你有两种方法来解锁:
方法 A:让持有锁的会话自己解锁(推荐)
这是最安全的方法,联系该会话对应的用户或应用程序,让它执行以下命令:
UNLOCK TABLES;
方法 B:强制终止持有锁的会话
如果无法联系到该会话,或者它已经卡死,可以像处理行锁一样,使用 KILL 命令强制结束它。
-- 将 '12345' 替换为你找到的会话 ID KILL 12345;
同样,KILL 命令会终止会话并自动释放其持有的所有表锁。
总结与最佳实践
| 锁类型 | 原因 | 查找方法 | 解锁方法 |
|---|---|---|---|
| InnoDB 行锁 | 事务未提交,阻塞其他事务 | 查询 information_schema.innodb_trx 和 innodb_lock_waits |
KILL [blocking_thread_id] |
LOCK TABLES 表锁 |
会话手动锁定表,未释放 | 查询 information_schema.processlist |
UNLOCK TABLES 或 KILL [session_id] |
最佳实践:如何避免锁问题?
- 保持事务简短:事务中的代码执行时间越短越好,获取锁后尽快提交或回滚。
- 按固定顺序访问表:如果多个事务需要访问相同的几张表,确保它们以相同的顺序进行,可以大大降低死锁的概率。
- 设置合理的隔离级别:根据业务需求选择合适的隔离级别(如
READ COMMITTED或REPEATABLE READ)。READ COMMITTED能减少锁的持有时间。 - 使用乐观锁:对于读多写少的场景,可以考虑使用版本号(如
version字段)等乐观锁机制,而不是直接使用悲观锁。 - 应用层重试机制:对于偶发的锁等待,可以在应用程序中实现自动重试逻辑,而不是立即报错。
- 监控长事务:定期监控
information_schema.innodb_trx,发现长时间运行的事务应及时处理。
