首先,这里不考虑innodb行锁,仅仅考虑MDL LOCK和DML/select 语句。我们知道到了8.0.28(包含)以后,记录慢查询的标准有变化,主要依赖的实际执行时间,而不是以前去掉了锁的时间(包含MDL LOCK和行锁)作为判定标准,具体参考,
https://www.jianshu.com/p/27547eb97d6a
以前通常通过lock_time来判断是否可能出现过innodb行锁堵塞和MDL LOCK堵塞,但是后来发现在8.0.28(包含)以后lock_time记录的时间变化了,通常并不包含innodb表的MDL LOCK时间,可以做测试如下,首先设置参数, set global long_query_time = 0,位了记录全部的满查询。
使用如下步骤,我们观察最后selet 语句的lock_time时间是否包含了MDL LOCK时间
步骤 | session1 | session2 | session3 |
---|---|---|---|
1. | begin;select * from t1 | ||
2. | alter table t1 add ic int; | ||
3. | select * from t1; (我们主要观察本语句lock time记录时间的不同) | ||
4. | commit; |
其中第2步开始 session2和session3 因为mdl lock的原因不能获取到锁,直到第4步commit解锁。
8.0.28及之后的记录
# Time: 2024-10-08T17:40:48.426038+08:00
# User@Host: root[root] @ localhost [] Id: 29
# Query_time: 13.729913 Lock_time: 0.000019 Rows_sent: 2 Rows_examined: 2
SET timestamp=1728380434;
select * from t1;
注意这里的lock_time,几乎没有什么值,而这两个语句很明显的是等待在MDL LOCK下面,而lock_time几乎为0,因此在8.0.28及之后即便Lock_time为0不能说明语句没有遇到MDL LOCK。
8.0.28及之前的记录,这里使用8.0.23
注意这里一定要将long_query_time 设置为0,否则如果主要等待在MDL LOCK上,去掉这部分时间后可能实际执行时间已经很短了,因此如果不设置long_query_time 为0不会记录任何慢查询。
# Time: 2024-10-08T18:02:47.417947+08:00
# User@Host: root[root] @ localhost [] Id: 19
# Query_time: 16.397964 Lock_time: 16.396849 Rows_sent: 1 Rows_examined: 1
SET timestamp=1728381751;
select * from t1;
这里我们能看到Lock_time实际上还是至少是包含了mdl lock的时间的。
大体原因
对于一般的DML和SELCET语句来讲,8.0.28之前的版本记录的lock _time的时候语句当跑到mysql_lock_tables函数的时候计算一次时间,这个时间实际上包含了MDL LOCK的时间,但是到了8.0.28(包含)以后这里发生了一些变化,仅仅记录了mysql_lock_tables函数里面消耗的时间,可以在函数找到如下几句,
ulonglong lock_start_usec = my_micro_time(); //仅仅是这一段 记录到了lock time
ulonglong lock_end_usec = my_micro_time(); //结束
thd->inc_lock_usec(lock_end_usec - lock_start_usec);//计算
但是innodb表的MDL LOCK堵塞并不包含在这个函数中,因此8.0.28及过后的MDL LOCK堵塞就不会记录了。
但是myisam的表锁是包含在其中,当我们使用myisam 测试表锁的时候会发现表锁的时间记录在里面,如下是8.0.36的测试,
image.png
myisam表记录的慢查询如下,
# Time: 2024-10-08T18:27:27.772193+08:00
# User@Host: root[root] @ localhost [] Id: 28
# Query_time: 67.663484 Lock_time: 67.636786 Rows_sent: 0 Rows_examined: 4
SET timestamp=1728383180;
delete from testmyisam;
可以看到即便是8036,myisam的表锁也包含在Lock_time中。
总结
- 对于DML/SELECT 语句来讲,在8.0.28(包含)以后慢查询中的lock_time通常不包含MDL LOCK堵塞的时间,因此即便lock_time为0不能说明没有遇到MDL LOCK堵塞,但是lock_time包含了innodb 行锁堵塞的时间。
- 对于DML/SELECT 语句来讲,在8.0.28之前的慢查询中的lock_time通常包含了MDL LOCK堵塞的时间,且包含innodb 行锁堵塞的时间。
- 对于myisam的表锁堵塞始终包含在Lock_time中。
网友评论