暂无图片
暂无图片
1
暂无图片
暂无图片
暂无图片

MySQL innodb 锁相关知识 锁机制

原创 wzf0072 2022-10-01
315

https://zhuanlan.zhihu.com/p/75595737
1 MySQL的三种锁
1.1 表锁
开销小,加锁快
不会出现死锁
锁定粒度大,发生锁冲突的概率最高,并发度最低
1.2 行锁
开销大,加锁慢
会出现死锁
锁定粒度小,发生锁冲突的概率最低,并发度最高
1.3 页锁
开销和加锁时间介于表锁和行锁之间
会出现死锁
锁定粒度介于表锁和行锁之间,并发度一般
1.4 引擎与锁
MyISAM和MEMORY支持表锁
BDB支持页锁,也支持表锁
Innodb既支持行锁,也支持表锁,默认行锁
1.5 查询表锁争用情况
检查table_locks_waited和table_locks_immediate状态变量分析
table_locks_immediate : 可以立即获取锁的次数
table_locks_waited : 不能立即获取锁,需要等待锁的次数
mysql> show status like '%table%';
+-----------------------------------------+---------+
| Variable_name | Value |
+-----------------------------------------+---------+
| Com_alter_table | 0 |
| Com_alter_tablespace | 0 |
| Com_create_table | 0 |
| Com_drop_table | 0 |
| Com_lock_tables | 0 |
| Com_rename_table | 0 |
| Com_show_create_table | 0 |
| Com_show_open_tables | 0 |
| Com_show_table_status | 0 |
| Com_show_tables | 0 |
| Com_unlock_tables | 0 |
| Created_tmp_disk_tables | 0 |
| Created_tmp_tables | 0 |
| Innodb_undo_tablespaces_total | 2 |
| Innodb_undo_tablespaces_implicit | 2 |
| Innodb_undo_tablespaces_explicit | 0 |
| Innodb_undo_tablespaces_active | 2 |
| Open_table_definitions | 617 |
| Open_tables | 5860 |
| Opened_table_definitions | 0 |
| Opened_tables | 0 |
| Performance_schema_table_handles_lost | 0 |
| Performance_schema_table_instances_lost | 0 |
| Performance_schema_table_lock_stat_lost | 0 |
| Replica_open_temp_tables | 0 |
| Slave_open_temp_tables | 0 |
| Table_locks_immediate | 4099671 |
| Table_locks_waited | 0 |
| Table_open_cache_hits | 0 |
| Table_open_cache_misses | 0 |
| Table_open_cache_overflows | 0 |
+-----------------------------------------+---------+
31 rows in set (3.13 sec)
table_locks_waited 的值越高,则说明存在严重的表级锁的争用情况
2 表锁模式(MyISAM)
MySQL的表锁有两种模式

表共享读锁(Table Read Lock)
表独占写锁(Table Write Lock)
2.1 表锁兼容性
锁模式的兼容如下表
| 是否兼容 | 请求none | 请求读锁 | 请求写锁 |
|:----|:----|:----|:----|
| 当前处于读锁 | 是 | 是 | 否 |
| 当前处于写锁 | 是 | 否 | 否 |
可见,对MyISAM表的读操作,不会阻塞其他用户对同一表的读请求,但会阻塞对同一表的写请求;
对MyISAM表的写操作,则会阻塞其他用户对同一表的读和写请求;

MyISAM表的读和写操作之间,以及写和写操作之间是串行的!(当某一线程获得对一个表的写锁后,只有持有锁的线程可以对表进行更新操作.其他线程的读、写操作都会等待,直到锁被释放为止)

2.2 如何加表锁
对于 MyISAM 引擎

执行select前,会自动给涉及的所有表加 读
执行更新(update,delete,insert)会自动给涉及到的表加 写
不需要用户直接显式用lock table命令

对于给MyISAM显式加锁,一般是为了在一定程度上模拟事务操作,实现对某一个时间点多个表一致性读取

2.2.1 实例
订单表 - orders 记录各订单的总金额total
订单明细表 - order_detail 记录各订单每一产品的金额小计subtotal
假设我们需要检查这两个表的金额合计是否相符,可能就需要执行如下两条SQL

这时,如果不先给这两个表加锁,就可能产生错误的结果;

因为第一条语句执行过程中,order_detail表可能已经发生了改变.

因此,正确写法应该如下

2.2.2 注意点
上面的例子在LOCK TABLES时加了‘local’选项,其作用就是在满足MyISAM表并发插入条件的情况下,允许其他用户在表尾插入记录
在用LOCK TABLES给表显式加表锁时,必须同时取得所有涉及表的锁,并且MySQL支持锁升级; 也就是说,在执行LOCK TABLES后,只能访问显式加锁的这些表,不能访问未加锁的表; 同时,如果加的是读锁,那么只能执行查询操作,而不能执行更新操作 其实,在自动加锁的情况下也基本如此,MySQL会一次获得SQL语句所需要的全部锁.这也正是MyISAM表不会出现死锁(Deadlock Free)的原因
| session1 | session2 |
|:----|:----|
| 获得表 film_text 的读锁 lock table film_text read; | | | 可select * from film_text | 可select * from film_text |
| 不能查询没有锁定的表 :select * from film | 可查询/更新未锁定的表: select * from film |
| 插入或更新锁定表会提示错误 update...from film_text | 更新锁定表会等待 update...from film_text |
| 释放锁 unlock tables | 等待 | | | 获得锁,更新成功 |

2.3 tips
当使用lock tables时,不仅需要一次锁定用到的所有表

且同一表在SQL语句中出现多少次,就要通过与SQL语句中别名锁多少次

lock table actor read
会提示错误

select a.first_name.....
需要对别名分别锁定

lock table actor as a read,actor as b read;

3 MyISAM的并发锁
在一定条件下,MyISAM也支持并发插入和读取

3.1 系统变量 : concurrent_insert
控制其并发插入的行为,其值分别可以为

0 不允许并发插入,所有插入对表加互斥锁
1 只要表中无空洞,就允许并发插入. MyISAM允许在一个读表的同时,另一个进程从表尾插入记录(MySQL的默认设置)
2 无论MyISAM表中有无空洞,都强制在表尾并发插入记录 若无读线程,新行插入空洞中
可以利用MyISAM的并发插入特性,来解决应用中对同表查询和插入的锁争用

例如,将concurrent_insert系统变量设为2,总是允许并发插入;

同时,通过定期在系统空闲时段执行OPTIONMIZE TABLE语句来整理空间碎片,收到因删除记录而产生的中间空洞

删除操作不会重整整个表,只是把 行 标记为删除,在表中留下空洞

MyISAM倾向于在可能时填满这些空洞,插入时就会重用这些空间,无空洞则把新行插到表尾

3.2 MyISAM的锁调度
MyISAM的读和写锁互斥,读操作串行的

一个进程请求某个MyISAM表的读锁,同时另一个进程也请求同表的写锁,MySQL如何处理呢? 写进程先获得锁!!! 不仅如此,即使读进程先请求先到锁等待队列,写请求后到,写锁也会插到读请求之前!!!
这是因为MySQL认为写请求一般比读请求重要

这也正是MyISAM表不适合有大量更新 / 查询操作应用的原因

大量的更新操作会造成查询操作很难获得读锁,从而可能永远阻塞

幸好,我们可以通过一些设置来调节MyISAM的调度行为

指定启动参数low-priority-updates 使MyISAM引擎默认给予读请求以优先权利
执行命令SET LOW_PRIORITY_UPDATES=1 使该连接发出的更新请求优先级降低
指定INSERT、UPDATE、DELETE语句的LOW_PRIORITY属性 降低该语句的优先级
虽然上面3种方法都是要么更新优先,要么查询优先,但还是可以用其来解决查询相对重要的应用(如用户登录系统)中,读锁等待严重的问题

另外,MySQL也提供了一种折中的办法来调节读写冲突;

即给系统参数max_write_lock_count设置一个合适的值;

当一个表的读锁达到这个值后,MySQL便暂时将写请求的优先级降低,给读进程一定获得锁的机会

* * *



查看行锁争用:
mysql> show status like 'innodb_row_lock%';
ERROR 4031 (HY000): The client was disconnected by the server because of inactivity. See wait_timeout and interactive_timeout for configuring this behavior.
No connection. Trying to reconnect...
Enter password:
Connection id: 2979265
Current database: *** NONE ***

+-------------------------------+--------+
| Variable_name | Value |
+-------------------------------+--------+
| Innodb_row_lock_current_waits | 0 |
| Innodb_row_lock_time | 324960 |
| Innodb_row_lock_time_avg | 1483 |
| Innodb_row_lock_time_max | 10021 |
| Innodb_row_lock_waits | 219 |
+-------------------------------+--------+
5 rows in set (3.04 sec)

mysql>

https://zhuanlan.zhihu.com/p/75595737

千金良方
找出谁持有表级锁 104页
表级锁对应的instruments ( wait/lock/ table/sql/handler )默认己启用,对应的consumers
为performance_schema.table_handles , 在setup_consumers 中只受全局配置项global_instrumentation 控制,默认己启用。所以,在默认情况下,只需要设置系统配置参数performance_schema=ON 即可。
select connection();
lock table sbtest1 read ;
排查陷入僵局,我们知道MDL 锁非常常见,对表的绝大部分操作都会先加MDL 锁
( performance_schema.metadata_locks 表中记录的锁信息也不顶用了〉。通常看到这些信息时,
我们可能会立刻想到需要查询information_schema 下的三个关于InnoDB 引擎的锁和事务信
息表( INNODB_LOCK_WAITS 、INNODB_LOCKS 、INNODB_TRX ),我们尝试查看这三个表(在会话3 中执行〉,可是发现都没有记录。
只有 SELECT * FROM information_schema.INNODB_TRX 能执行(5.7,8.0)
mysql> select * from information_schema.INNODB_TRX ;
Empty set (0.00 sec)
mysql> select * information_schema.INNODB_LOCKS ; #测试无法执行
Empty set , 1 warning (0 . 00 sec)
mysql>select * information_schema.INNODB_LOCK WAITS ; #测试无法执行
Empty set , 1 warn 工ng (0 . 00 sec)

mysql 5.7 、8.0 information_schema.INNODB_LOCKS 、information_schema.INNODB_LOCK WAITS 两表被`performance_schema`.data_locks、 `performance_schema`.data_lock_waits代替
select * from `performance_schema`.data_locks
select * from `performance_schema`.data_lock_waits
当然,可能有的人会说,就4 个线程,第二个会话的“ Command ”为Sleep ,应该是它,
把它杀掉试试看。是的, 在该案例中确实可以做这个尝试,但如果是在生产环境中有数十
个、上百个正常的长连接处于Sleep 状态, 该怎么办呢?这时就不能挨个去尝试了,我们可
以尝试查询一些表级别的锁信息( 通过会话3 查询performance_schema.table_handles表)。
select* from performance_schema.table_handles

通过上面查询到的相关数据,我们知道113线程对sbtest1表显式加了表记读锁,而且长时间处于sleep状态,但是并不知道该线程正在执行什么SQL语句,我们可以通过performance_schema.events_statements_current表查询。
select * from performance_schema.events_statements_current where thread_id=113
从performance_schema.events_ statements current 表的查询信息中,通过SQL_TEXT 字
段可以清晰地看到该线程正在执行的SQL 语句是什么。如果是生产环境,现在你可以去找
相关的开发人员确认,如果没有什么特殊操作,就可以尝试杀掉这个钱程(在会话3 中执
行, processlist_id 为18 ) , 同时针对这个问题进行优化, 避免再发生类似的情况。
#如何知道内部ID 为113 的线程对应的processlist_id 是多少呢?可以通过performance schema.threads 表
查询
mysql> select processlist_id from performace_schema.threads where thread_id=113;
processlist_id:18
kill 18


找出谁持有行级锁 107页 select * from data_locks\G
该案例中涉及的performance_schema.data_ lock 表是MySQL 8.0 中新增的,在8. 0 之前的版本中不支持,这里仅作为针对MySQL5.7 的performance_schema 的一个延伸学习
如果一个事务长时间未提交,我们虽然可以从information_schema.innodb_trx 、
performance_schema.events_transactions_current 等表中查询到相应的事务信息,但却无从知
道这个事务持有了哪些锁。虽然information schema.innodb_locks 表是用于记录事务的锁信
息的,但需要在两个不同的事务发生锁等待时该表才会记录两个事务的锁信息。从
MySQL8 . 0 开始, 在performanc巳_schema 中提供了一个data_locks 表用于记录任意事务的锁
信息(同时废弃了information schema.innodb locks 表〉,不需要有锁等待关系存在( 注意,
该表中只记录InnoDB 存储引擎层的锁〉。

select * from information_schema.innodb_trx
select * from performance_schema.events_transactions_current
select * from information_schema.innodb_locks #无法访问
首先, 在MySQL8 . 0 中打开一个会话(会话l ),显式开启一个事务。
mysql> use xiaob。lu。
Database changed
mysql> select * from t luoxiaob limit l;
+----+------+---------------------+
I id I test I datet time
+- - +------+------------+
I 2 I 1 I 2017 - 09- 06 01 : 11:59 I
+----+---- --+由自由’-------------------+
1 row in set (0.00 sec)
mysql> begin ;
Query OK, 0 rows affected (0.00 sec)
mysql> update t_luoxia。b。set datet_time=now() where id=2 ;
Query OK , 1 row affected (0 . 00 sec)
Rows matched: 1 Changed: 1 Warnings: 0

接下来, 打开另一个会话(会话2, 查询data locks 表。
mysql> select * from data l。cks\G
**********************1. row ***********************
ENGINE : INNODB
ENGINE LOCK ID: 55562 : 62
ENGINE TRANSACTION ID: 55562
THREAD ID : 54 #持有锁线程内部ID
EVENT ID: 85
OBJECT SCHEMA : xiaoboluo #库名
OBJECT NAME : t_luoxiaobo #表名
PARTITION NAME : NULL
SUBPARTITION NAME : NULL
INDEX NAME: NULL #索引名称
OBJECT INSTANCE BEGIN : 140439793477144
LOCK TYPE : TABLE #表级锁
LOCK MODE : IX # IX 锁
LOCK STATUS: GRANTED #被授予状态
LOCK DATA : NULL
***************** 2 . row *************************
ENGINE: INNODB
ENGINE LOCK ID: 55562 : 2 : 4:2
第6 章阴阳
ENGINE TRANSACTION ID: 55562
THREAD ID : 54 #持有线程内部ID
EVENT ID : 85
OBJECT SCHEMA: xiaoboluo #库名
OBJECT NAME : t_luoxiaobo #表名
PARTITION NAME: NULL
SUBPARTITION NAME: NULL
INDEX NAME : PRIMARY #索引为主键
OBJECT INSTANCE BEGIN : 140439793474104
LOCK TYPE : RECORD #记录锁
LOCK MODE: X #排他锁
LOCK STATUS: GRANTED #被授予状态
LOCK DATA: 2 #被锁定的数据记录, 这里的记录对应的是INDEX NAME : PRIMARY 的value 如id=2
2 r。ws in set (0.00 sec)

从查询结果中可以看到,有两行锁记录,第一行是对t luoxiaobo 表的IX 锁,状态为
GRANTED ; 第二行为使用主键索引的X 锁,记录锁,状态为GRANTED 。
现在,我们模拟两条DML 语句发生锁等待的场景。新打开一个会话(会话刀,在会
话l 中的事务未提交的情况下,会话3 对t luo x iaobo 表执行同样的操作。
mysql > use xia。bolu。
Database changed
mysql> begin ;
Query OK , 0 rows affected (0.00 sec)
mysql > update t_luoxiaobo set datet_time=now() where id=2 ; #被阻塞
回到会话2 中,查询data locks 表,可以发现有4 行锁记录。
mysql> select * from performance_schema.data_l。cks\G
*************************** 1 . row***************************
...
THREAD ID : 55
...
L OCK TYPE : TABLE
L OCK MODE: I X
LOCK STATUS : GRANTED
LOCK DATA: NULL
************************** 2. row******************** ***** **
ENGINE: INNODB
ENGINE LOCK ID: 55563:2 : 4 : 2
ENGINE TRANSACTION ID: 55563
THREAD ID: 55 # 内部线程ID
EVENT ID : 8
OBJECT SCHEMA: xiaob 。luo
OBJECT NAME: t luoxiaobo
PARTITION NAME : NULL
SUBPARTITION NAME: NULL
INDEX NAME: PRIMARY #锁记录发生在哪个索引上
OBJECT INSTANCE BEGIN: 140439793480168
LOCK TYPE : RECORD #记录锁
LOCK MODE: X #排他锁
LOCK STATUS : WAITING # 正在等待锁被授予
LOCK DATA : 2 #锁定的索引value ,这里与内部ID 为54 的线程持有的主键值为2 的x 锁完全一样,说明这里就是被内部ID 为54 的线程阻塞了
************************* 3. row ***********************
THREAD ID: 54
LOCK TYPE : TABLE
LOCK MODE : IX
LOCK STATUS : GRANTED
LOCK DATA : NULL
*************************** 4. row **************************
...
THREAD ID: 54
...
EVENT ID : 85
OBJECT SCHEMA: xiaoboluo
OBJECT NAME: t luoxiaobo
PARTITION NAME : NULL
SUBPARTITION NAME : NULL
INDEX NAME : PRIMARY
OBJECT INSTANCE BEGIN : 140439793474104
LOCK TYPE: RECORD
LOCK MODE: X
LOCK STATUS : GRANTED
LOCK DATA : 2
4 rows in set (0 . 00 sec)

从上面的查询数据可以看到, performance_schema.data_locks 表中新增了线程ID 为55
的两行锁记录, IX 锁状态为GRANTED, X 锁状态为WAITING , 说明正在等待锁被授予。
但这里并不能很直观地查看到锁等待关系,我们可以使用sys.innodb_lock_waits 视图查看。

mysql> select * from sys.innodb_lock_waits\G
* *** * * ** * * * * * ** * ** * * * * * * * * * 1. row * *** * * ** ** * ** ** • •会*********
wait started : 2018- 01 - 14 21:51:59
wait_ age : 00:00:11
wait_age_secs : 11
locked table :xiaobolu.t_luoxiaobo
locked table schema: xiaoboluo
locked table name : t_luoxiaobo
locked_table_partition: NULL
locked_table_subpartition : NULL
locked index: PRIMARY
locked_type : RECORD
waiting_trx id : 55566
waiting_trx_started : 2018-01-14 21 : 51:59
waiting_trx_age . 00 : 00:11
waiting_trx rows_locked: 1
waiting_trx_rows_modified : 0
waiting pid: B
waiting_query : update t_luoxiaobo set datet_time =now () where id=2
waiting_lock_id: 55566:2 :4: 2
waiting_lock_mode : X
blocking_trx_id: 55562
blocking_pid: 7
blocking_query: NULL
blocking lock id: 55562 : 2:4 : 2
blocking lock mode: X
blocking trx started: 2018 - 01- 14 21:34:44
blocking trx age : 00:17:26
blocking_trx_ rows_locked : 1
blocking_trx_r。ws_m。dified: 1
sql_kill_blocking_query : KILL QUERY 7
sql_kill_blocking_connection: KILL 7
1 row in set (0 . 02 sec)
提示: 在MySQL 5 . 7 版本中,也可以使用sys.innodb_lock_ waits 视图查看, 但是在
MySQL8 . 0 中, 该视图联结查询的表不同(把之前版本中使用的information_schema.innodb_locks表和information_ schema.innodb_lock_waits 表替换为了performance_schema.data_locks
表和performance_schema.data_lock_waits 表)。另外,在MySQL5 . 6 及之前的版本中,在默认情况下并没有sys 库,我们可以使用如下语句代替。
原:SELECT r.trx_wait_started AS wait_started,
TIMEDIFF(NOW() , r.trx_wait_started) AS wait_age,
TIMESTAMPDIFF(SECOND, r.trx_wait_started , NOW()) AS wait_age_secs ,
rl.lock_table AS locked_table,
rl.lock_index AS locked_index,
rl.lock_type AS locked_type ,
r.trx_id AS waiting_trx_id ,
r.trx_started as waiting_trx_started ,
TIMEDIFF(NOW (), r.trx_started) AS waiting_trx_age ,
r.trx_rows_locked AS waiting_trx_rows_locked,
r.trx_rows_modified AS waiting_trx_rows_modified,
r.trx_mysql_thread_id AS waiting_pid,
sys.format_statement(r.trx_query) AS waiting_query ,
rl.lock_id AS waiting_lock_id,
rl.lock_mode AS waiting_lock_mode,
b.trx_id AS blocking_trx_id,
b.trx_mysql_thread_id AS blocking_pid ,
sys.format_statement(b.trx_query) AS blocking_query ,
bl.lock_id AS blocking_lock_id,
bl.lock_mode AS blocking_lock_mode,
b.trx_started AS blocking_trx_started,
TIMEDIFF(NOW(), b.trx_started ) AS blocking_trx_age,
b.trx_rows_locked AS blocking_trx_rows_locked ,
b.trx_rows_rnodified AS blocking_trx_rows_modified ,
CONCAT ( 'KILL QUERY', b.trx_mysql_thread_id) AS sql_kill_blocking_query,
CONCAT ( 'KILL', b.trx_mysql_thread_id) AS sql_kill_blocking_connection
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
INNER JOIN information_schema.innodb_locks bl ON bl.lock_id= w.blocking_lock_id
INNER JOIN information_schema.innodb_locks rl ON rl.lock_id = w.requested_lock_id
ORDER BY r.trx_wait_started;

更正中
SELECT r.trx_wait_started AS wait_started,
TIMEDIFF(NOW() , r.trx_wait_started) AS wait_age,
TIMESTAMPDIFF(SECOND, r.trx_wait_started , NOW()) AS wait_age_secs ,
rl.lock_table AS locked_table, #对象不存在
rl.lock_index AS locked_index,
rl.lock_type AS locked_type ,
r.trx_id AS waiting_trx_id ,
r.trx_started as waiting_trx_started ,
TIMEDIFF(NOW (), r.trx_started) AS waiting_trx_age ,
r.trx_rows_locked AS waiting_trx_rows_locked,
r.trx_rows_modified AS waiting_trx_rows_modified,
r.trx_mysql_thread_id AS waiting_pid,
sys.format_statement(r.trx_query) AS waiting_query ,
rl.lock_id AS waiting_lock_id,
rl.lock_mode AS waiting_lock_mode,
b.trx_id AS blocking_trx_id,
b.trx_mysql_thread_id AS blocking_pid ,
sys.format_statement(b.trx_query) AS blocking_query ,
bl.lock_id AS blocking_lock_id,
bl.lock_mode AS blocking_lock_mode,
b.trx_started AS blocking_trx_started,
TIMEDIFF(NOW(), b.trx_started ) AS blocking_trx_age,
b.trx_rows_locked AS blocking_trx_rows_locked ,
b.trx_rows_rnodified AS blocking_trx_rows_modified ,
CONCAT ( 'KILL QUERY', b.trx_mysql_thread_id) AS sql_kill_blocking_query,
CONCAT ( 'KILL', b.trx_mysql_thread_id) AS sql_kill_blocking_connection
FROM performance_schema.data_lock_waits w -- information_schema.innodb_lock_waits w -- 1109 - Unknown table 'INNODB_LOCK_WAITS' in information_schema
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
INNER JOIN performance_schema.data_locks bl ON bl.lock_id= w.blocking_lock_id -- 1109 - Unknown table 'INNODB_LOCKS' in information_schema
INNER JOIN performance_schema.data_locks rl ON rl.lock_id = w.requested_lock_id -- 1109 Unknown table 'INNODB_LOCKS' in information_schema
ORDER BY r.trx_wait_started;


更正中v3
SELECT r.trx_wait_started AS wait_started,
TIMEDIFF(NOW() , r.trx_wait_started) AS wait_age,
TIMESTAMPDIFF(SECOND, r.trx_wait_started , NOW()) AS wait_age_secs ,
-- rl.lock_table AS locked_table, 1054 - Unknown column 'rl.lock_id' in 'field list'
-- rl.lock_index AS locked_index, 1054 - Unknown column 'rl.lock_id' in 'field list'
rl.lock_type AS locked_type ,
r.trx_id AS waiting_trx_id ,
r.trx_started as waiting_trx_started ,
-- TIMEDIFF(NOW (), r.trx_started) AS waiting_trx_age , -- FUNCTION information_schema.NOW does not exis
r.trx_rows_locked AS waiting_trx_rows_locked,
r.trx_rows_modified AS waiting_trx_rows_modified,
r.trx_mysql_thread_id AS waiting_pid,
sys.format_statement(r.trx_query) AS waiting_query ,
-- rl.lock_id AS waiting_lock_id, -- 1054 - Unknown column 'rl.lock_id' in 'field list'
rl.lock_mode AS waiting_lock_mode,
b.trx_id AS blocking_trx_id,
b.trx_mysql_thread_id AS blocking_pid ,
sys.format_statement(b.trx_query) AS blocking_query ,
bl.lock_id AS blocking_lock_id,
bl.lock_mode AS blocking_lock_mode,
b.trx_started AS blocking_trx_started,
-- TIMEDIFF(NOW(), b.trx_started ) AS blocking_trx_age, -- FUNCTION information_schema.NOW does not exis
b.trx_rows_locked AS blocking_trx_rows_locked ,
b.trx_rows_rnodified AS blocking_trx_rows_modified ,
CONCAT ( 'KILL QUERY', b.trx_mysql_thread_id) AS sql_kill_blocking_query,
CONCAT ( 'KILL', b.trx_mysql_thread_id) AS sql_kill_blocking_connection
FROM performance_schema.data_lock_waits w -- information_schema.innodb_lock_waits w -- 1109 - Unknown table 'INNODB_LOCK_WAITS' in information_schema
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
INNER JOIN performance_schema.data_locks bl ON bl.lock_id= w.blocking_lock_id -- 1109 - Unknown table 'INNODB_LOCKS' in information_schema
INNER JOIN performance_schema.data_locks rl ON rl.lock_id = w.requested_lock_id -- 1109 Unknown table 'INNODB_LOCKS' in information_schema
ORDER BY r.trx_wait_started;

查看最近的SQL 语句执行信息
查看最近的TOP SQL 语句
查看最近执行失败的SQL 语句
查看SQL 语句执行阶段和进度信息
查看SOL 语句执行阶段信息
查看S Q L 语句执行进度信息 s ele c t * from sys.session where conn_id !=connection_id()\G
查看最近的事务执行信息
查看多线程复制报错详情 select * from perf 。rmance_schema.repl1cat1on_applier status by worker where
LAST ERROR MESSAGE !=”\G



42 FIO存储性能压测
数据库设置xa超时





这里需要知道 MySQL 具有 MVCC 特性,所以,在通常情况下,普通的查询属于非锁定读,不会加任何锁 (即一致性读)。还有一种是锁定读( 即当前读) ,例如
• SELECT ... FOR SHARE CMySQL 8.0 新增 方式 以前版本中上锁方式为SELECT . . LOCK IN SHARE MODE),添加S锁,其他事务可以读但修改会被阻塞。
• SELECT . . . FOR UPDATE ,添加X锁,其他事务修改或者执行 SELECT .. . FOR SHARE 会被阻塞。



插入意向锁是针对INSERT操作设置的一种特殊的间隙锁,主要是为了优化INSERT操作的并发能力。这个锁表示插入的意图,即插入具有相同索引间隙的多个事务,如果插入的值不同 ,则不需要互相等待。
假设存在值为4和7的索引记录,现在分别尝试插入值为5和6的事务,在获取插入行上的排他锁之前,会添加插入意向锁锁定4和7之间的问隙 但是不会互相阻塞,因为插入的行是不冲突的。
这里需要注意的是,插入意向锁之间是不冲突 的, 但是插入意向锁可能和其他锁是冲突的,比如 Next-Key Lock



第二种方式是通过 performance_schema.data lock 表( MySQL 8.0 版本中新增加的表,在后续章节中都是通过此表来查看加锁情况的〉 查看


select engine_lock_id,engine_transaction_id,thread_id,object_schema,object_name,index_name,lock_type,lock_mode,lock_status,lock_data from performance_schema.data_locks;

,同样会有“ emum udo-r cord ”。正如前 所讲的,
where id= 10 ,但是每次插入记 时所 成的聚集索引 DB ROW ID 是自
,每次 会在表 最后插入 就有可能插入 id = 这条记录。因此, 需要添加
条“ upremum pseudo record ”防止数据插入。

同样会有“ emum udo-r cord ”。正如前 所讲的,
where id= 10 ,但是每次插入记 时所 成的聚集索引 DB ROW ID 是自
,每次 会在表 最后插入 就有可能插入 id = 这条记录。因此, 需要添加
条“ upremum pseudo record ”防止数据插入。

「喜欢这篇文章,您的关注和赞赏是给作者最好的鼓励」
关注作者
【版权声明】本文为墨天轮用户原创内容,转载时必须标注文章的来源(墨天轮),文章链接,文章作者等基本信息,否则作者和墨天轮有权追究责任。如果您发现墨天轮中有涉嫌抄袭或者侵权的内容,欢迎发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论