mysql replace的死锁分析(二)

文章目录

  • 1、测试场景
  • 2、测试数据准备
  • 3、操作前检查
  • 4、测试操作
  • 5、会话2被阻塞问题分析
      • 5.1 查看innodb当前的锁(字段含义见 8.3
      • 5.2 查看锁的等待关系(字段含义见 8.4
      • 5.3 查看当前运行的所有事务信息
      • 5.4 分析:
        • 1. 先查看information_schema.innodb_trx
        • 2. 再查看锁等待关系information_schema.INNODB_LOCK_WAITS
        • 3. 再查看当前innodb所有的锁信息information_schema.INNODB_LOCKS
        • 4. 查看mysql官方文档
        • 5.总结
        • 6. 验证分析
        • 7. 验证结果
        • 8. 加锁详细过程
  • 6、INSERT ... ON DUPLICATE KEY UPDATE是否也会有间隙锁?
  • 7、REPALCE INTO 死锁产生的原因
      • 7.1 产生死锁原因分析
      • 7.2 查看死锁的详细信息
  • 8、mysql分析锁的参考命令
    • 8.1关闭间隙锁:
  • 8.2 information_schema.innodb_trx字段说明–当前运行的所有事务
  • 8.3 information_schema.innodb_locks字段说明–当前出现的锁
  • 8.4 information_schema.innodb_lock_waits字段说明–锁等待的对应关系
  • 9、锁的优化建议

1、测试场景

  • MySQL版本: 5.7.34
  • 事务级别: READ-COMMITTED
  • 提交模式:手动

2、测试数据准备

-- 准备表
drop table if exists test001;
create table test001
(
    id int auto_increment primary key,
    b varchar(100),
    c int,
    unique key (b)
); 
-- 先插入1条基础数据,并提交事务
insert into test001(b,c) values ('uq_1',1);

3、操作前检查

  • 检查事务模式和事务隔离级别
  • 检查是否关闭间隙锁 ON:关闭 OFF:开启
  • 已写入的测试数据
  • 使用DataGrip打开两个会话窗口
  • 原始数据如下:
    mysql replace的死锁分析(二)_第1张图片

4、测试操作

  • replace into 跟 insert 功能类似,不同点在于:replace into 首先尝试插入数据到表中,如果发现表中已经有此行数据(根据主键或者唯一索引判断)则先删除此行数据,然后插入新的数据
  • 否则没有此行数据的话,直接插入新数据,此时和insert一样。

会话1先执行:执行成功

-- step1:插入uq_2,不提交事务
## 执行成功
replace into test001(b,c) values ('uq_2',1);

mysql replace的死锁分析(二)_第2张图片
② 会话2再执行: 执行被阻塞

-- step2:更新uq_1,不提交事务
## 执行被阻塞
replace into test001(b,c) values ('uq_1',2);

问题:唯一索引字段b的值完全不相同,会话1为什么会阻塞会话2语句的执行?

③ 会话1再执行:发生死锁**

-- step3:再次修改uq_1,不提交
replace into test001(b,c) values ('uq_1',3);

mysql replace的死锁分析(二)_第3张图片

5、会话2被阻塞问题分析

5.1 查看innodb当前的锁(字段含义见 8.3

mysql> select * from information_schema.INNODB_LOCKS;
+-------------+-------------+-----------+-----------+-------------------+------------+------------+-----------+----------+-----------+
| lock_id     | lock_trx_id | lock_mode | lock_type | lock_table        | lock_index | lock_space | lock_page | lock_rec | lock_data |
+-------------+-------------+-----------+-----------+-------------------+------------+------------+-----------+----------+-----------+
| 2262:25:4:8 | 2262        | X         | RECORD    | `mysql`.`test001` | b          |         25 |         4 |        8 | 'uq_2'    |
| 2261:25:4:8 | 2261        | X         | RECORD    | `mysql`.`test001` | b          |         25 |         4 |        8 | 'uq_2'    |
+-------------+-------------+-----------+-----------+-------------------+------------+------------+-----------+----------+-----------+
2 rows in set, 1 warning (0.00 sec)

5.2 查看锁的等待关系(字段含义见 8.4

mysql> select * from information_schema.INNODB_LOCK_WAITS;
+-------------------+-------------------+-----------------+------------------+
| requesting_trx_id | requested_lock_id | blocking_trx_id | blocking_lock_id |
+-------------------+-------------------+-----------------+------------------+
| 2262              | 2262:25:4:8       | 2261            | 2261:25:4:8      |
+-------------------+-------------------+-----------------+------------------+
1 row in set, 1 warning (0.00 sec)

5.3 查看当前运行的所有事务信息

(由于列比较多,所以改成竖排显示,(字段含义见 8.2))

mysql> select * from information_schema.innodb_trx\G;
*************************** 1. row ***************************
                    trx_id: 2262
                 trx_state: LOCK WAIT
               trx_started: 2022-08-18 14:02:43
     trx_requested_lock_id: 2262:25:4:8
          trx_wait_started: 2022-08-18 14:02:43
                trx_weight: 6
       trx_mysql_thread_id: 13
                 trx_query: /* ApplicationName=DataGrip 2021.1 */ replace into test001(b,c) values ('uq_1',2)
       trx_operation_state: updating or deleting
         trx_tables_in_use: 1
         trx_tables_locked: 1
          trx_lock_structs: 4
     trx_lock_memory_bytes: 1136
           trx_rows_locked: 3
         trx_rows_modified: 2
   trx_concurrency_tickets: 0
       trx_isolation_level: READ COMMITTED
         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: 2261
                 trx_state: RUNNING
               trx_started: 2022-08-18 14:02:39
     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: 2
     trx_lock_memory_bytes: 1136
           trx_rows_locked: 1
         trx_rows_modified: 1
   trx_concurrency_tickets: 0
       trx_isolation_level: READ COMMITTED
         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 rows in set (0.00 sec)

5.4 分析:

1. 先查看information_schema.innodb_trx

- 会话1主要信息:【trx_id: 2261 trx_state: RUNNING   trx_started:  2022-08-18 14:02:39- 会话2主要信息:【trx_id: 2262 trx_state: LOCK WAIT trx_started:  2022-08-18 14:02:43
  • 会话2的语句,事务2262在等待锁状态【trx_requested_lock_id: 2262:25:4:8】
  • 会话1的语句,事务2261是运行状态,没有等待锁

2. 再查看锁等待关系information_schema.INNODB_LOCK_WAITS

mysql replace的死锁分析(二)_第4张图片

  • 当前持有锁的会话1事务是2261,请求锁的会话2事务是2262
  • 会话2在等待会话1释放锁。

问题:会话2为什么要等待会话1释放锁?

3. 再查看当前innodb所有的锁信息information_schema.INNODB_LOCKS

mysql replace的死锁分析(二)_第5张图片

  • 会话1和会话2的事务都是行级别排他锁
  • 并且锁住的索引是唯一索引b 【lock_index:b】
  • 锁的的唯一索引值都是’uq_2’ 【lock_data :‘uq_2’】

那么问题来了,会话2更新的是’uq_1’,为什么会锁住’uq_2’?

进一步看information_schema.innodb_trx,从上面看到

- 会话2的语句,事务2262在等待锁,   锁定的行数是3【trx_rows_locked: 3 trx_rows_modified: 2- 会话1的语句,事务2261是运行状态, 锁定的行数是1【trx_rows_locked: 1 trx_rows_modified: 1

问题:在间隙锁关闭的情况下,会话2的语句为什么锁定3行数据?**

4. 查看mysql官方文档

REPALCE:

https://dev.mysql.com/doc/refman/5.7/en/innodb-locks-set.html
REPLACE is done like an INSERT if there is no collision on a unique key. Otherwise, an exclusive next-key lock is placed on the row to be replaced.

INSERT :

https://dev.mysql.com/doc/refman/5.7/en/innodb-locks-set.html
INSERT sets an exclusive lock on the inserted row. This lock is an index-record lock, not a next-key lock (that is, there is no gap lock) and does not prevent other sessions from inserting into the gap before the inserted row.

在已提交读事务隔离级别下,官方文档有如下描述:

https://dev.mysql.com/doc/refman/5.7/en/innodb-transaction-isolation-levels.html

For locking reads (SELECT with FOR UPDATE or LOCK IN SHARE MODE), UPDATE statements, and DELETE statements, InnoDB locks only index records, not the gaps before them, and thus permits the free insertion of new records next to locked records.
Gap locking is only used for foreign-key constraint checking and duplicate-key checking.

  • 第一句大意是:如果没有唯一索引冲突,REPLACE 操作相当于INSERT操作。如果有唯一索引冲突,会在行记录上加一个排他next-key锁
  • 第二句大意是:INSERT操作会在被插入行上设置一个排他锁,没有间隙锁,并且不会阻止其他会话在间隙中插入数据
  • 第三句后半句大意是:间隙锁会被用在外键约束检查和重复键检查上

咨询DBA,也反应关闭间隙锁的命令【innodb_locks_unsafe_for_binlog】不会影响外键和唯一索引对gap加锁,DBA回答如下:
mysql replace的死锁分析(二)_第6张图片

5.总结

  • 会话2的操作存在唯一键的冲突,所以REPALCE INTO操作会在当前行上放置间隙锁,即当前行和前后两行,共3行。所以会锁住索引值’uq_2’。
  • 会话1的操作不存在唯一键的冲突,所以REPALCE INTO操作就相当于普通的INSERT操作,没有间隙锁,只会锁住当前被插入行。所以同样会锁住索引值’uq_2’。
  • 因此,会话1如果事务没有提交会阻塞会话2的执行。

6. 验证分析

再插入一条基础数据

-- 插入uq_2,并提交事务
replace into test001(b,c) values ('uq_2',1);

mysql replace的死锁分析(二)_第7张图片
① 会话1执行:

-- step1:插入uq_3,不提交
## 执行成功
replace into test001(b,c) values ('uq_3',1);

mysql replace的死锁分析(二)_第8张图片

② 会话2执行:

-- step2:替换uq_1,不提交``## 执行成功``replace` `into` `test001(b,c) ``values` `(``'uq_1'``,2);

mysql replace的死锁分析(二)_第9张图片

7. 验证结果

  • 会话1的操作不存在唯一键的冲突,所以REPALCE INTO操作就相当于普通的INSERT操作,没有间隙锁,只会锁住当前被插入行。所以会锁住索引值’uq_3’。
  • 会话2的操作存在唯一键的冲突,所以REPALCE INTO操作会在当前行上放置间隙锁,即当前行和前后两行,共3行。但不包括索引值’uq_3’,所以不会被阻塞执行。
  • 由此也验证了,REPALCE INTO操作确实存在间隙锁。

8. 加锁详细过程

附加:事务2262和2261的详细加锁过程,可以看到都是由意向排他锁升级为排他锁,2262一直在等待锁。

mysql>show engine innodb status;  
------------
TRANSACTIONS
------------
Trx id counter 2263
Purge done for trx's n:o < 2261 undo n:o < 0 state: running but idle
History list length 1
LIST OF TRANSACTIONS FOR EACH SESSION:
---TRANSACTION 283411180461568, not started
0 lock struct(s), heap size 1136, 0 row lock(s)
---TRANSACTION 2262, ACTIVE 22 sec updating or deleting
mysql tables in use 1, locked 1
LOCK WAIT 4 lock struct(s), heap size 1136, 3 row lock(s), undo log entries 2
MySQL thread id 13, OS thread handle 42312, query id 6680 localhost 127.0.0.1 root update
/* ApplicationName=DataGrip 2021.1 */ replace into test001(b,c) values ('uq_1',2)
------- TRX HAS BEEN WAITING 22 SEC FOR THIS LOCK TO BE GRANTED:
RECORD LOCKS space id 25 page no 4 n bits 80 index b of table `mysql`.`test001` trx id 2262 lock_mode X waiting
Record lock, heap no 8 PHYSICAL RECORD: n_fields 2; compact format; info bits 0
 0: len 4; hex 75715f32; asc uq_2;;
 1: len 4; hex 80000043; asc    C;;
 
------------------
TABLE LOCK table `mysql`.`test001` trx id 2262 lock mode IX
RECORD LOCKS space id 25 page no 4 n bits 80 index b of table `mysql`.`test001` trx id 2262 lock_mode X
Record lock, heap no 2 PHYSICAL RECORD: n_fields 2; compact format; info bits 32
 0: len 4; hex 75715f31; asc uq_1;;
 1: len 4; hex 80000001; asc     ;;
 
RECORD LOCKS space id 25 page no 3 n bits 80 index PRIMARY of table `mysql`.`test001` trx id 2262 lock_mode X locks rec but not gap
Record lock, heap no 2 PHYSICAL RECORD: n_fields 5; compact format; info bits 32
 0: len 4; hex 80000001; asc     ;;
 1: len 6; hex 0000000008d6; asc       ;;
 2: len 7; hex 230000015303d7; asc #   S  ;;
 3: len 4; hex 75715f31; asc uq_1;;
 4: len 4; hex 80000001; asc     ;;
 
RECORD LOCKS space id 25 page no 4 n bits 80 index b of table `mysql`.`test001` trx id 2262 lock_mode X waiting
Record lock, heap no 8 PHYSICAL RECORD: n_fields 2; compact format; info bits 0
 0: len 4; hex 75715f32; asc uq_2;;
 1: len 4; hex 80000043; asc    C;;
 
---TRANSACTION 2261, ACTIVE 26 sec
2 lock struct(s), heap size 1136, 1 row lock(s), undo log entries 1
MySQL thread id 8, OS thread handle 41748, query id 6670 localhost 127.0.0.1 root
TABLE LOCK table `mysql`.`test001` trx id 2261 lock mode IX
RECORD LOCKS space id 25 page no 4 n bits 80 index b of table `mysql`.`test001` trx id 2261 lock_mode X locks rec but not gap
Record lock, heap no 8 PHYSICAL RECORD: n_fields 2; compact format; info bits 0
 0: len 4; hex 75715f32; asc uq_2;;
 1: len 4; hex 80000043; asc    C;;

6、INSERT … ON DUPLICATE KEY UPDATE是否也会有间隙锁?

① 会话1:

-- step1:插入uq_2,不提交
INSERT into test001(b,c) values ('uq_2',1) ON DUPLICATE KEY UPDATE c = VALUES(c);

② 会话2:

-- step2:更新uq_1,不提交
INSERT into test001(b,c) values ('uq_1',2) ON DUPLICATE KEY UPDATE c = VALUES(c);

mysql replace的死锁分析(二)_第10张图片

  • 将测试操作改成INSERT … ON DUPLICATE KEY UPDATE,会话2没有被阻塞,没有等待会话1事务的锁,说明该操作没有加间隙锁。

7、REPALCE INTO 死锁产生的原因

上面分析了事务1产生锁等待的原因,接着分析死锁产生的原因就很容易了。

7.1 产生死锁原因分析

  • 此时,会话1再操作索引值’uq_1’,存在唯一键的冲突,需要对当前行加间隙锁。但是会话2此时也在操作索引值’uq_1’,因此会话1需要等待会话2的锁。
  • 再加上此前会话2在等待会话1的锁,这就造成会话1和会话2相互等待锁的情况,形成死锁。
  • 在发生死锁时,如果开启死锁检测,InnoDB一般都能通过算法(wait-for graph)自动检测到。
开启主动死锁检测,默认开启:
innodb_deadlock_detect=on

7.2 查看死锁的详细信息

mysql> show engine innodb status;
 
| InnoDB |      |
=====================================
2022-08-18 14:03:14 0xb2b8 INNODB MONITOR OUTPUT
=====================================
Per second averages calculated from the last 9 seconds
-----------------
BACKGROUND THREAD
-----------------
srv_master_thread loops: 255 srv_active, 0 srv_shutdown, 31140 srv_idle
srv_master_thread log flush and writes: 31395
----------
SEMAPHORES
----------
OS WAIT ARRAY INFO: reservation count 945
OS WAIT ARRAY INFO: signal count 967
RW-shared spins 0, rounds 1260, OS waits 541
RW-excl spins 0, rounds 448, OS waits 2
RW-sx spins 0, rounds 0, OS waits 0
Spin rounds per wait: 1260.00 RW-shared, 448.00 RW-excl, 0.00 RW-sx
------------------------
LATEST DETECTED DEADLOCK
------------------------
2022-08-18 14:03:10 0xa314
*** (1) TRANSACTION:
TRANSACTION 2262, ACTIVE 27 sec updating or deleting
mysql tables in use 1, locked 1
LOCK WAIT 4 lock struct(s), heap size 1136, 3 row lock(s), undo log entries 2
MySQL thread id 13, OS thread handle 42312, query id 6680 localhost 127.0.0.1 root update
/* ApplicationName=DataGrip 2021.1 */ replace into test001(b,c) values ('uq_1',2)
*** (1) WAITING FOR THIS LOCK TO BE GRANTED:
RECORD LOCKS space id 25 page no 4 n bits 80 index b of table `mysql`.`test001` trx id 2262 lock_mode X waiting
Record lock, heap no 8 PHYSICAL RECORD: n_fields 2; compact format; info bits 0
 0: len 4; hex 75715f32; asc uq_2;;
 1: len 4; hex 80000043; asc    C;;
 
*** (2) TRANSACTION:
TRANSACTION 2261, ACTIVE 31 sec inserting
mysql tables in use 1, locked 1
3 lock struct(s), heap size 1136, 2 row lock(s), undo log entries 2
MySQL thread id 8, OS thread handle 41748, query id 6694 localhost 127.0.0.1 root update
/* ApplicationName=DataGrip 2021.1 */ replace into test001(b,c) values ('uq_1',3)
*** (2) HOLDS THE LOCK(S):
RECORD LOCKS space id 25 page no 4 n bits 80 index b of table `mysql`.`test001` trx id 2261 lock_mode X locks rec but not gap
Record lock, heap no 8 PHYSICAL RECORD: n_fields 2; compact format; info bits 0
 0: len 4; hex 75715f32; asc uq_2;;
 1: len 4; hex 80000043; asc    C;;
 
*** (2) WAITING FOR THIS LOCK TO BE GRANTED:
RECORD LOCKS space id 25 page no 4 n bits 80 index b of table `mysql`.`test001` trx id 2261 lock_mode X waiting
Record lock, heap no 2 PHYSICAL RECORD: n_fields 2; compact format; info bits 32
 0: len 4; hex 75715f31; asc uq_1;;
 1: len 4; hex 80000001; asc     ;;
 
*** WE ROLL BACK TRANSACTION (2)
------------
TRANSACTIONS
------------
Trx id counter 2267
Purge done for trx's n:o < 2267 undo n:o < 0 state: running but idle
History list length 3
LIST OF TRANSACTIONS FOR EACH SESSION:
---TRANSACTION 283411180461568, not started
0 lock struct(s), heap size 1136, 0 row lock(s)
---TRANSACTION 283411180459824, not started
0 lock struct(s), heap size 1136, 0 row lock(s)
---TRANSACTION 2262, ACTIVE 31 sec
5 lock struct(s), heap size 1136, 4 row lock(s), undo log entries 2
MySQL thread id 13, OS thread handle 42312, query id 6705 localhost 127.0.0.1 root
TABLE LOCK table `mysql`.`test001` trx id 2262 lock mode IX
RECORD LOCKS space id 25 page no 4 n bits 80 index b of table `mysql`.`test001` trx id 2262 lock_mode X
Record lock, heap no 1 PHYSICAL RECORD: n_fields 1; compact format; info bits 0
 0: len 8; hex 73757072656d756d; asc supremum;;
 
Record lock, heap no 2 PHYSICAL RECORD: n_fields 2; compact format; info bits 32
 0: len 4; hex 75715f31; asc uq_1;;
 1: len 4; hex 80000001; asc     ;;
 
RECORD LOCKS space id 25 page no 3 n bits 80 index PRIMARY of table `mysql`.`test001` trx id 2262 lock_mode X locks rec but not gap
Record lock, heap no 2 PHYSICAL RECORD: n_fields 5; compact format; info bits 32
 0: len 4; hex 80000001; asc     ;;
 1: len 6; hex 0000000008d6; asc       ;;
 2: len 7; hex 230000015303d7; asc #   S  ;;
 3: len 4; hex 75715f31; asc uq_1;;
 4: len 4; hex 80000001; asc     ;;
 
RECORD LOCKS space id 25 page no 4 n bits 80 index b of table `mysql`.`test001` trx id 2262 lock_mode X
RECORD LOCKS space id 25 page no 4 n bits 80 index b of table `mysql`.`test001` trx id 2262 lock_mode X locks gap before rec
Record lock, heap no 8 PHYSICAL RECORD: n_fields 2; compact format; info bits 0
 0: len 4; hex 75715f31; asc uq_1;;
 1: len 4; hex 80000044; asc    D;;
 
--------
FILE I/O
--------
I/O thread 0 state: wait Windows aio (insert buffer thread)
I/O thread 1 state: wait Windows aio (log thread)
I/O thread 2 state: wait Windows aio (read thread)
I/O thread 3 state: wait Windows aio (read thread)
I/O thread 4 state: wait Windows aio (read thread)
I/O thread 5 state: wait Windows aio (read thread)
I/O thread 6 state: wait Windows aio (write thread)
I/O thread 7 state: wait Windows aio (write thread)
I/O thread 8 state: wait Windows aio (write thread)
I/O thread 9 state: wait Windows aio (write thread)
Pending normal aio reads: [0, 0, 0, 0] , aio writes: [0, 0, 0, 0] ,
 ibuf aio reads:, log i/o's:, sync i/o's:
Pending flushes (fsync) log: 0; buffer pool: 0
284 OS file reads, 3790 OS file writes, 2443 OS fsyncs
0.00 reads/s, 0 avg bytes/read, 1.89 writes/s, 1.33 fsyncs/s
-------------------------------------
INSERT BUFFER AND ADAPTIVE HASH INDEX
-------------------------------------
Ibuf: size 1, free list len 0, seg size 2, 0 merges
merged operations:
 insert 0, delete mark 0, delete 0
discarded operations:
 insert 0, delete mark 0, delete 0
Hash table size 34679, node heap has 0 buffer(s)
Hash table size 34679, node heap has 0 buffer(s)
Hash table size 34679, node heap has 0 buffer(s)
Hash table size 34679, node heap has 0 buffer(s)
Hash table size 34679, node heap has 1 buffer(s)
Hash table size 34679, node heap has 0 buffer(s)
Hash table size 34679, node heap has 0 buffer(s)
Hash table size 34679, node heap has 0 buffer(s)
1.56 hash searches/s, 2.22 non-hash searches/s
---
LOG
---
Log sequence number 3095833
Log flushed up to   3095833
Pages flushed up to 3095833
Last checkpoint at  3095824
0 pending log flushes, 0 pending chkp writes
1492 log i/o's done, 0.78 log i/o's/second
----------------------
BUFFER POOL AND MEMORY
----------------------
Total large memory allocated 137297920
Dictionary memory allocated 153584
Buffer pool size   8192
Free buffers       7759
Database pages     432
Old database pages 0
Modified db pages  0
Pending reads      0
Pending writes: LRU 0, flush list 0, single page 0
Pages made young 0, not young 0
0.00 youngs/s, 0.00 non-youngs/s
Pages read 250, created 182, written 1994
0.00 reads/s, 0.00 creates/s, 1.00 writes/s
Buffer pool hit rate 1000 / 1000, young-making rate 0 / 1000 not 0 / 1000
Pages read ahead 0.00/s, evicted without access 0.00/s, Random read ahead 0.00/s
LRU len: 432, unzip_LRU len: 0
I/O sum[0]:cur[0], unzip sum[0]:cur[0]
--------------
ROW OPERATIONS
--------------
0 queries inside InnoDB, 0 queries in queue
0 read views open inside InnoDB
Process ID=37652, Main thread ID=40856, state: sleeping
Number of rows inserted 4283, updated 86, deleted 17, read 8172
0.00 inserts/s, 0.11 updates/s, 0.00 deletes/s, 0.00 reads/s
----------------------------
END OF INNODB MONITOR OUTPUT
============================

8、mysql分析锁的参考命令

-- 查看数据库版本
select version();
 
-- 查看默认值
show variables like '%engine%';
 
-- 查看事务隔离级别
show global variables like 'tx_isolation';
 
-- 查看是否开启自动提交
show variables like 'autocommit';
 
-- 查看是否开启间隙锁
show variables like 'innodb_locks_unsafe_for_binlog';
 
-- 查看锁等待的对应关系
select * from information_schema.INNODB_LOCK_WAITS;
 
-- 查看innodb当前的事务和锁
select * from information_schema.innodb_trx;
 
mysql5.6版本后,可以通过下面的命令来进行锁的状态监控
set global innodb_status_output=on;
set global innodb_status_output_locks=on;
 
-- 开启锁的状态监控后,查看详细的innodeb信息
show engine innodb status;

8.1关闭间隙锁:

-- 1、查看是否开启间隙锁:show variables like 'innodb_locks_unsafe_for_binlog';
mysql> show variables like 'innodb_locks_unsafe_for_binlog';
+--------------------------------+-------+
| Variable_name                  | Value |
+--------------------------------+-------+
| innodb_locks_unsafe_for_binlog | OFF   |
+--------------------------------+-------+
-- 注意:默认值为OFF(0),即启用gap lock。
-- 这个参数最主要的作用就是控制innodb是否对gap加锁。
-- 但是,这一设置变更并不影响外键和唯一索引(含主键)对gap进行加锁的需要。
 
-- 2、关闭间隙锁(gap lock)方法:
-- 在my.cnf里面的[mysqld]添加:
innodb_locks_unsafe_for_binlog = 1
-- 重启MySQL后生效。

8.2 information_schema.innodb_trx字段说明–当前运行的所有事务

Field Extra
trx_id 事务ID
trx_state 事务状态
trx_started 事务开始时间
trx_requested_lock_id innodb_locks.lock_id
trx_wait_started 事务开始等待的时间
trx_weight 事务权重
trx_mysql_thread_id 事务线程ID
trx_query 具体SQL语句
trx_operation_state 事务当前操作状态
trx_tables_in_use 事务中有多少个表被使用
trx_tables_locked 事务拥有多少个锁
trx_lock_structs
trx_lock_memory_bytes 事务锁住的内存大小(B)
trx_rows_locked 事务锁住的行数
trx_rows_modified 事务更改的行数
trx_concurrency_tickets 事务并发票数
trx_isolation_level 事务隔离级别
trx_unique_checks 是否唯一性检查
trx_foreign_key_checks 是否外键检查
trx_last_foreign_key_error 最后的外键错误
trx_adaptive_hash_latched
trx_adaptive_hash_timeout

8.3 information_schema.innodb_locks字段说明–当前出现的锁

Field Extra
lock_id 锁ID
lock_trx_id 拥有锁的事务ID
lock_mode 锁模式
lock_type 锁类型
lock_table 被锁的表
lock_index 被锁的索引
lock_space 被锁的表空间号
lock_page 被锁的页号
lock_rec 被锁的记录号
lock_data 被锁的数据

8.4 information_schema.innodb_lock_waits字段说明–锁等待的对应关系

Field Extra
requesting_trx_id 请求锁的事务ID
requested_lock_id 请求锁的锁ID
blocking_trx_id 当前拥有锁的事务ID
blocking_lock_id 当前拥有锁的锁ID

9、锁的优化建议

  • 尽量使用INSERT … ON DUPLICATE KEY UPDATE代替REPLACE INTO
  • REPLACE INTO有很多副作用,比如自增id会快速增大; slave 提升为 master后,可能会发生duplicate key error
  • 在能正确完成业务的前提下,为确保效率,尽量使用较低的隔离级别(必须避免脏读)
  • 设计合理的索引并尽量使用索引访问数据,使加锁更准确,减少锁冲突的机会,提高并发能力
  • 选择合理的事务大小,小事务发生锁冲突的概率小(事务越大,包含的SQL越多,可能包含更多的表资源和行资源的锁,增大了锁冲突的概率)
  • 不同的程序访问一组表时,应尽量约定以相同的顺序访问各表,对一个表而言,尽可能以- 固定的顺序存取表中的行。这样可以大大减少死锁的机会
  • 尽量用相等条件访问数据,这样可以避免间隙锁对并发插入的影响(其实等值查询也会加间隙锁)
  • 不要申请超过实际需要的锁级别
  • 除非必须,查询时不要显示加锁(在已提交读和可重复读隔离级别,MVCC提供了读取机制,不需要手动加锁)

你可能感兴趣的:(mysql,mysql,数据库,java)