千家信息网

Mysql一分钟定位 Next-Key Lock,你需要几分

发表于:2026-11-01 作者:千家信息网编辑
千家信息网最后更新 2026年11月01日,连接与线程查看连接信息 show processlist+----+------+------------------+------+---------+------+----------+-----
千家信息网最后更新 2026年11月01日Mysql一分钟定位 Next-Key Lock,你需要几分

连接与线程

查看连接信息 show processlist

+----+------+------------------+------+---------+------+----------+------------------+| Id | User | Host             | db   | Command | Time | State    | Info             |+----+------+------------------+------+---------+------+----------+------------------+| 3  | root | 172.17.0.1:60542 | test | Query   | 0    | starting | show processlist || 5  | root | 172.17.0.1:60546 | test | Sleep   | 4168 |          |            || 8  | root | 172.17.0.1:60552 | test | Sleep   | 4170 |          |            |+----+------+------------------+------+---------+------+----------+------------------+

mysql 非企业版本只支持一个线程一个链接

查看线程模型 show variables like 'thread_handling'

+-----------------------------------------+---------------------------+| Variable_name                           | Value                     |+-----------------------------------------+---------------------------+| thread_handling                         | one-thread-per-connection |+-----------------------------------------+---------------------------+

【 事务提交策略】
有两个隐藏事务提交时间点需要注意,第一个是 autocommit=1 Mysql session 级别的自动提交变量,所有 ORM 框架中的事务提交控制都会受到这个字段影响,默认情况下当前语句会自动提交,但是如果是显示 begin transaction 开启事务需要自行手动提交。有些时候 ORM 框架会根据一些设置或者策略,将 autocommit 设置为0。

第二个就是,DDL操作前都会隐式提交当前事务,有些脚本将DML和DDL混合在一起使用,这样会有一致性问题。DDL会自动提交当前事务。因为DDL在5.7之前都是不支持事务原则操作的。(Mysql8.0已经支持DDL事务性)

Next-Key Lock 排查

Next-Key Lock 只发生在 RR(REPEATABLE-READ) 隔离级别下。

Mysql 有很多类型对种锁,表锁、record lock、gap lock、意向共享/排他锁、插入意向锁、元数据锁、Auto_Incr自增锁,排除掉 元数据锁、Auto_Incr自增锁 之后,剩下的锁组合使用最多的就是在RR隔离级别下。

RR隔离级别是默认事务隔离级别,也是Mysql的强项之一,在RR隔离级别下事务有最大的吞吐量,而且不会出现幻读问题。Next-Key Lock 就是为了解决这个问题,简单讲 record lock+gap lock 就是 Next-Key Lock。

_幻读_的根本问题就是出现在记录的边界值上,比如我们统计年龄大于30岁的人数:select count(1) peoples where age>30 这个语句有可能每次查询得到的结果集都是不一样的,因为只要符合 age>30 的记录进到我们的 peoples 表中就会被查询条件命中。

所以要想解决幻读不仅不允许记录的空隙被插入记录外,还要防止两遍记录被修改,因为如果前后两条记录被修改了那区间就会变大,就会有幻读出现。

我们看个例子。

 CREATE TABLE `peoples` (  `id` int(11) unsigned NOT NULL AUTO_INCREMENT,  `age` int(11) DEFAULT NULL,  PRIMARY KEY (`id`),  KEY `idx_peoples_age` (`age`)) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4
+----+-----+| id | age |+----+-----+| 1  | 20  || 2  | 30  || 3  | 35  || 4  | 40  |+----+-----+

为了方便调试,将 innodb 获取锁的超时时间调大点

show variables like '%innodb_lock_wait%'set innodb_lock_wait_timeout=600

开启两个会话。

session A id=8:beginselect count(1) from peoples where age>30 for update;
session B id=5:begininsert into peoples(age) values(31)

show processlist 找到连接的id。

***************************[ 1. row ]***************************Id      | 3User    | rootHost    | 172.17.0.1:60542db      | testCommand | QueryTime    | 0State   | startingInfo    | show processlist***************************[ 2. row ]***************************Id      | 5User    | rootHost    | 172.17.0.1:60546db      | testCommand | QueryTime    | 394State   | updateInfo    | insert into peoples(age) values(31)***************************[ 3. row ]***************************Id      | 8User    | rootHost    | 172.17.0.1:60552db      | testCommand | SleepTime    | 396State   |Info    | 
  • 事务

select * from information_schema.innodb_trx \G 查看事务执行情况。

***************************[ 1. row ]***************************trx_id                     | 457240trx_state                  | LOCK WAITtrx_started                | 2020-01-27 06:08:12trx_requested_lock_id      | 457240:131:4:4trx_wait_started           | 2020-01-27 06:09:25trx_weight                 | 6trx_mysql_thread_id        | 5trx_query                  | insert into peoples(age) values(31)trx_operation_state        | insertingtrx_tables_in_use          | 1trx_tables_locked          | 1trx_lock_structs           | 5trx_lock_memory_bytes      | 1136trx_rows_locked            | 4trx_rows_modified          | 1trx_concurrency_tickets    | 0trx_isolation_level        | REPEATABLE READtrx_unique_checks          | 1trx_foreign_key_checks     | 1trx_last_foreign_key_error | trx_adaptive_hash_latched  | 0trx_adaptive_hash_timeout  | 0trx_is_read_only           | 0trx_autocommit_non_locking | 0***************************[ 2. row ]***************************trx_id                     | 457239trx_state                  | RUNNINGtrx_started                | 2020-01-27 06:07:59trx_requested_lock_id      | trx_wait_started           | trx_weight                 | 3trx_mysql_thread_id        | 8trx_query                  | trx_operation_state        | trx_tables_in_use          | 0trx_tables_locked          | 1trx_lock_structs           | 3trx_lock_memory_bytes      | 1136trx_rows_locked            | 5trx_rows_modified          | 0trx_concurrency_tickets    | 0trx_isolation_level        | REPEATABLE READtrx_unique_checks          | 1trx_foreign_key_checks     | 1trx_last_foreign_key_error | trx_adaptive_hash_latched  | 0trx_adaptive_hash_timeout  | 0trx_is_read_only           | 0trx_autocommit_non_locking | 0

457240 事务状态是 LOCK WAIT 在等待锁,457239事务状态是 RUNNING执行中,正在等待事务提交。

  • 锁

select * from information_schema.innodb_locks \G 查看锁的占用情况。

***************************[ 1. row ]***************************lock_id     | 457240:131:4:4lock_trx_id | 457240lock_mode   | X,GAPlock_type   | RECORDlock_table  | `test`.`peoples`lock_index  | idx_peoples_agelock_space  | 131lock_page   | 4lock_rec    | 4lock_data   | 35, 7***************************[ 2. row ]***************************lock_id     | 457239:131:4:4lock_trx_id | 457239lock_mode   | Xlock_type   | RECORDlock_table  | `test`.`peoples`lock_index  | idx_peoples_agelock_space  | 131lock_page   | 4lock_rec    | 4lock_data   | 35, 7

innodb_locks 表包含了已经获取到的锁信息和请求锁的信息。lock_index字段表示锁走的索引,record锁都是基于索引完成。

根据上面事务457240状态是获取锁,lock_data | 35, 7,表示请求的数据。而事务457239占用了当前X锁。

  • 锁等待

select * from information_schema.innodb_lock_waits 查看锁等待信息。

***************************[ 1. row ]***************************requesting_trx_id | 457240requested_lock_id | 457240:131:4:4blocking_trx_id   | 457239blocking_lock_id  | 457239:131:4:4

457240 事务需要获取131:4:4锁,457239 事务占用了131:4:4锁。

  • innodb 监视器
    show engine innodb status
LIST OF TRANSACTIONS FOR EACH SESSION:---TRANSACTION 422032240994144, not started0 lock struct(s), heap size 1136, 0 row lock(s)---TRANSACTION 457240, ACTIVE 394 sec insertingmysql tables in use 1, locked 1LOCK WAIT 5 lock struct(s), heap size 1136, 7 row lock(s), undo log entries 1MySQL thread id 5, OS thread handle 140556966967040, query id 105 172.17.0.1 root updateinsert into peoples(age) values(31)------- TRX HAS BEEN WAITING 165 SEC FOR THIS LOCK TO BE GRANTED:RECORD LOCKS space id 131 page no 4 n bits 72 index idx_peoples_age of table `test`.`peoples` trx id 457240 lock_mode X locks gap before rec insert intention waitingRecord lock, heap no 4 PHYSICAL RECORD: n_fields 2; compact format; info bits 0 0: len 4; hex 80000023; asc    #;; 1: len 4; hex 00000007; asc     ;;---------------------TRANSACTION 457239, ACTIVE 407 sec3 lock struct(s), heap size 1136, 5 row lock(s)MySQL thread id 8, OS thread handle 140556966696704, query id 104 172.17.0.1 root

MySQL thread id 5 正在准备上插入意向锁,插入意向锁本质上是加间隙锁,是为了保证最大并发插入,不相关的行插入不受到互斥。thread id 5 需要保证在插入前加上间隙锁,主要是防止并发插入带来的一致性问题。

session 5 和 session 8 都没有操作到 id=3,age=35的记录,但是却被X+Gap Lock 锁住,只有这样才能解决幻读问题。

作者:王清培(趣头条 Tech Leader)

事务 级别 问题 就是 隔离 信息 意向 情况 数据 状态 线程 支持 最大 一致 一致性 两个 字段 时间 框架 正在 数据库的安全要保护哪些东西 数据库安全各自的含义是什么 生产安全数据库录入 数据库的安全性及管理 数据库安全策略包含哪些 海淀数据库安全审计系统 建立农村房屋安全信息数据库 易用的数据库客户端支持安全管理 连接数据库失败ssl安全错误 数据库的锁怎样保障安全 云南咖啡 广州紫鲸互联网科技有限公司 互联网和科技技术 科技企业和互联网企业 致骋互联网科技 腾讯科技 互联网女皇 齐家互联网科技有限公司 互联网高科技企业 四个字互联网科技公司取名 重庆威客帮互联网科技 栖莱科技互联网股份有限公司 崇明区互联网教育科技诚信经营 米聚互联网科技南京有限公司排面 青海科技产业互联网发展 恒生互联网科技业指数调整周期 科技互联网的核心竞争力 杭州披云加互联网科技有限公司 厦门江牛科技互联网 中国新注册互联网科技 互联网科技对化妆品行业 金融科技与互联网金融 广州凡科互联网科技 互联网科技公司介绍 成都狮子座互联网科技 崇明区互联网教育科技诚信经营 昆明中奇联合互联网科技 中国新注册互联网科技 米泉互联网科技专业怎么样 唐山双旺合互联网科技产业园 山东亿川互联网科技有限责任公司
0