Mysql一分钟定位 Next-Key Lock,你需要几分钟
一分钟定位 Next-Key Lock,你需要几分钟
标签: Mysql、Next-KeyLock、插入意向锁
连接与线程
查看连接信息 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 | | <null> |
| 8 | root | 172.17.0.1:60552 | test | Sleep | 4170 | | <null> |
+----+------+------------------+------+---------+------+----------+------------------+
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:
begin
select count(1) from peoples where age>30 for update;
session B id=5:
begin
insert into peoples(age) values(31)
show processlist 找到连接的id。
***************************[ 1. row ]***************************
Id | 3
User | root
Host | 172.17.0.1:60542
db | test
Command | Query
Time | 0
State | starting
Info | show processlist
***************************[ 2. row ]***************************
Id | 5
User | root
Host | 172.17.0.1:60546
db | test
Command | Query
Time | 394
State | update
Info | insert into peoples(age) values(31)
***************************[ 3. row ]***************************
Id | 8
User | root
Host | 172.17.0.1:60552
db | test
Command | Sleep
Time | 396
State |
Info | <null>
- 事务
select * from information_schema.innodb_trx \G 查看事务执行情况。
***************************[ 1. row ]***************************
trx_id | 457240
trx_state | LOCK WAIT
trx_started | 2020-01-27 06:08:12
trx_requested_lock_id | 457240:131:4:4
trx_wait_started | 2020-01-27 06:09:25
trx_weight | 6
trx_mysql_thread_id | 5
trx_query | insert into peoples(age) values(31)
trx_operation_state | inserting
trx_tables_in_use | 1
trx_tables_locked | 1
trx_lock_structs | 5
trx_lock_memory_bytes | 1136
trx_rows_locked | 4
trx_rows_modified | 1
trx_concurrency_tickets | 0
trx_isolation_level | REPEATABLE READ
trx_unique_checks | 1
trx_foreign_key_checks | 1
trx_last_foreign_key_error | <null>
trx_adaptive_hash_latched | 0
trx_adaptive_hash_timeout | 0
trx_is_read_only | 0
trx_autocommit_non_locking | 0
***************************[ 2. row ]***************************
trx_id | 457239
trx_state | RUNNING
trx_started | 2020-01-27 06:07:59
trx_requested_lock_id | <null>
trx_wait_started | <null>
trx_weight | 3
trx_mysql_thread_id | 8
trx_query | <null>
trx_operation_state | <null>
trx_tables_in_use | 0
trx_tables_locked | 1
trx_lock_structs | 3
trx_lock_memory_bytes | 1136
trx_rows_locked | 5
trx_rows_modified | 0
trx_concurrency_tickets | 0
trx_isolation_level | REPEATABLE READ
trx_unique_checks | 1
trx_foreign_key_checks | 1
trx_last_foreign_key_error | <null>
trx_adaptive_hash_latched | 0
trx_adaptive_hash_timeout | 0
trx_is_read_only | 0
trx_autocommit_non_locking | 0
457240 事务状态是 LOCK WAIT 在等待锁,457239事务状态是 RUNNING执行中,正在等待事务提交。
- 锁
select * from information_schema.innodb_locks \G 查看锁的占用情况。
***************************[ 1. row ]***************************
lock_id | 457240:131:4:4
lock_trx_id | 457240
lock_mode | X,GAP
lock_type | RECORD
lock_table | `test`.`peoples`
lock_index | idx_peoples_age
lock_space | 131
lock_page | 4
lock_rec | 4
lock_data | 35, 7
***************************[ 2. row ]***************************
lock_id | 457239:131:4:4
lock_trx_id | 457239
lock_mode | X
lock_type | RECORD
lock_table | `test`.`peoples`
lock_index | idx_peoples_age
lock_space | 131
lock_page | 4
lock_rec | 4
lock_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 | 457240
requested_lock_id | 457240:131:4:4
blocking_trx_id | 457239
blocking_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 started
0 lock struct(s), heap size 1136, 0 row lock(s)
---TRANSACTION 457240, ACTIVE 394 sec inserting
mysql tables in use 1, locked 1
LOCK WAIT 5 lock struct(s), heap size 1136, 7 row lock(s), undo log entries 1
MySQL thread id 5, OS thread handle 140556966967040, query id 105 172.17.0.1 root update
insert 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 waiting
Record 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 sec
3 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)
Mysql一分钟定位 Next-Key Lock,你需要几分钟的更多相关文章
- android 高德地图出现【定位失败key鉴权失败】
如题:android 高德地图出现[定位失败key鉴权失败] 原因:使用的是debug模式下的SHA1,发布的版本正确获取SHA1的方式见: 方法二使用 keytool(jdk自带工具),按照如下步骤 ...
- Key lock 的秘密
研究死锁,或者观察sp_lock,有时候最恼人的莫过于你看到下面研究成果的key lock,但是却不知道究竟是哪个page 哪个row被lock住了: Exec sp_lock: 就说上面的key ...
- Mysql中INSERT ... ON DUPLICATE KEY UPDATE的实践
转: Mysql中INSERT ... ON DUPLICATE KEY UPDATE的实践 阿里加多 0.1 2018.03.23 17:19* 字数 492 阅读 2613评论 2喜欢 1 一.前 ...
- 关于MySQL字符集问题:Specified key was too long; max key length is 767 bytes
[文章来源]http://blog.csdn.net/cindy9902/article/details/6215769 MySQL: ERROR 1071 (42000): Specified ke ...
- MySQL“慢SQL”定位
MySQL"慢SQL"定位 数据库调优我个人觉得必须要明白两件事 1.定位问题(你得知道问题出在哪里,要不然从哪里调优呢) 2.解决问题(这个没有基本的方法来处理,因为不同的问题处 ...
- MIT墙上的格言(如果你把任务留到最后一分钟,那么你一定能在一分钟内完成任务)
1,永远不能忘记傅立叶变换. Never far no can forget Fuliye changer. 2,盲目的研究者就像法拉第和麦克斯韦之间的电学家一样无所适从. Blind Eye's r ...
- MySQL 中如何定位 DDL 被阻塞的问题
经常碰到开发.测试童鞋会问,线下开发.测试环境,执行了一个DDL,发现很久都没有执行完,是不是被阻塞了?要怎么解决? 包括在群里,也经常会碰到类似问题:DDL 被阻塞了,如何找到阻塞它的 SQL ? ...
- MySQL InnoDB锁机制之Gap Lock、Next-Key Lock、Record Lock解析
MySQL InnoDB支持三种行锁定方式: l 行锁(Record Lock):锁直接加在索引记录上面,锁住的是key. l 间隙锁(Gap Lock):锁定索引记录间隙,确保索引记录的间隙 ...
- 记一次MySQL中Waiting for table metadata lock问题的处理
起因:由于需要,要把一张表的一个字段从不是 null 改成 可null,我用的Navicat Premium ,但是在保存的时候,工具无响应了,几个同事操作都是这样的,很奇怪,怀疑是不是由于表被锁了还 ...
随机推荐
- linux 短延时
当一个设备驱动需要处理它的硬件的反应时间, 涉及到的延时常常是最多几个毫秒. 在这 个情况下, 依靠时钟嘀哒显然不对路. The kernel functions ndelay, udelay, an ...
- 【50.40%】【BZOJ 4553】[Tjoi2016&Heoi2016]序列
Time Limit: 20 Sec Memory Limit: 128 MB Submit: 371 Solved: 187 [Submit][Status][Discuss] Descript ...
- vue-learning:17- js - methods
methods 函数是十分优雅的语言特性,它让我们可以采用可复用的方式存储一段逻辑,从而不用重复代码就可以在多处调用.函数.组件.模块等都有复用代码的考虑,函数应该是最早组织复用代码的实现. 在vue ...
- 台州学院第十二届校赛记录(B,C,E,H,I,J,L)
传送门:点我 题目很棒,感谢出题验题的大佬们. 细节坑不少,是好事. 还是很菜,继续加油! B: 桃子的生日 时间限制(普通/Java):1000MS/3000MS 内存限制:65536KBy ...
- dotnet 修复找不到 System.ServiceProcess 定义
本文告诉大家如果复制网上一段代码发现 System.ServiceProcess 提示找不到方法或定义,需要手动添加引用 例如下面一段代码 using System.ServiceProcess; p ...
- Vue学习笔记-目录结构
1.采用脚手架构建的项目基本目录结构 可能会有些许差别,但是大致基本目录都差不多 2.项目入口(index.html,main.js,App.vue) 一般情况下,我们都习惯性将 index.html ...
- BZOJ3527 推出卷积公式FFT求值
BZOJ3527 推出卷积公式FFT求值 传送门:https://www.lydsy.com/JudgeOnline/problem.php?id=3527 题意: \(F_{j}=\sum_{i&l ...
- github权限管理
引用自:https://www.cnblogs.com/zhaoyanjun/p/5882784.html 前言: 在上一篇文章中Android github 快速实现多人协作 (http://www ...
- java面试-泛型
1.什么是泛型?泛型有什么作用? 泛型就是参数化类型,也就是所操作的数据类型被指定为一个参数.这种参数类型可以用在类.接口和方法的创建中,分别称为泛型类.泛型接口.泛型方法. 在泛型出现之前, ...
- docker练习-容器和服务
使用定义容器 Dockerfile Dockerfile定义容器内环境中发生的事情.对网络接口和磁盘驱动器等资源的访问在此环境中进行虚拟化,该环境与系统的其他部分隔离,因此您需要将端口映射到外部世界, ...