一、表级锁(Table Lock)

1、表锁(Table Lock)
  • 表共享读锁(Table Read Lock):通过 LOCK TABLES table_name READ 语句获取,允许其他会话读取表,但不允许写入。
  • 表独占写锁(Table Write Lock):通过 LOCK TABLES table_name WRITE 语句获取,不允许其他会话读取或写入表。
2、元数据锁(Metadata Lock,MDL)
  • 当执行DDL操作(如ALTER TABLE、DROP TABLE等)或者某些特定的操作(如LOCK TABLES、FLUSH TABLES WITH READ LOCK等)时,会自动获取MDL锁。
  • MDL锁用于保护表的元数据,防止其他会话同时修改表结构。
3、出现表级锁的情况
  1. 显式使用 LOCK TABLES 语句获取表锁。
  2. 执行 DDL 操作时自动获取 MDL 锁。
  3. 使用 MyISAM 存储引擎时,读写操作会自动获取表锁。
  4. FLUSH TABLES WITH READ LOCK 语句也会获取表级别的读锁,用于备份等场景。
4、定位和排查表级锁
  1. 使用 SHOW PROCESSLIST 或 INFORMATION_SCHEMA.INNODB_TRX 表查看当前的锁信息和事务状态。
  2. 通过 MySQL 错误日志或慢查询日志分析锁等待和阻塞情况。
  3. 使用 MySQL 性能分析工具,如 MySQL Enterprise Monitor、Percona Toolkit 等,分析锁的使用情况。
5、表级锁的影响
  1. 并发性能下降:表级锁会阻塞其他会话对该表的访问,降低并发性能。
  2. 锁等待和死锁:如果多个会话同时请求表锁,可能导致锁等待或死锁,影响系统可用性。
  3. DDL 操作阻塞:当执行 DDL 操作时,会获取 MDL 锁,阻塞其他会话对该表的访问。
6、模拟表级锁:
1、使用 LOCK TABLES 语句显式获取表锁:
  1. 创建数据
(root@localhost) [test]> create table t1(id int,name varchar(20));
Query OK, 0 rows affected (0.00 sec)

(root@localhost) [test]> desc t1;
+-------+-------------+------+-----+---------+-------+
| Field | Type        | Null | Key | Default | Extra |
+-------+-------------+------+-----+---------+-------+
| id    | int         | YES  |     | NULL    |       |
| name  | varchar(20) | YES  |     | NULL    |       |
+-------+-------------+------+-----+---------+-------+
2 rows in set (0.00 sec)

(root@localhost) [test]> insert into t1 values(1,'yc');
Query OK, 1 row affected (0.01 sec)

(root@localhost) [test]> insert into t1 values(2,'wz');
Query OK, 1 row affected (0.00 sec)

(root@localhost) [test]> insert into t1 values(3,'zw');
Query OK, 1 row affected (0.01 sec)

(root@localhost) [test]> insert into t1 values(3,'szs');
Query OK, 1 row affected (0.00 sec)

(root@localhost) [test]> commit;
Query OK, 0 rows affected (0.00 sec)

(root@localhost) [test]> select * from t1;
+------+------+
| id   | name |
+------+------+
|    1 | yc   |
|    2 | wz   |
|    3 | zw   |
|    3 | szs  |
+------+------+
4 rows in set (0.00 sec)

模拟锁

--session 1

(root@localhost) [test]> lock table t1 write;
Query OK, 0 rows affected (0.00 sec)

--session 2
(root@localhost) [test]> insert into t1 values(6,'sh');

--session 3

(root@localhost) [test]> show processlist;
+----+------+-----------+------+---------+------+---------------------------------+-------------------------------+
| Id | User | Host      | db   | Command | Time | State                           | Info                          |
+----+------+-----------+------+---------+------+---------------------------------+-------------------------------+
|  7 | root | localhost | test | Sleep   |   77 |                                 | NULL                          |
|  8 | root | localhost | test | Query   |   48 | Waiting for table metadata lock | insert into t1 values(6,'sh') |
|  9 | root | localhost | test | Query   |    0 | init                            | show processlist              |
+----+------+-----------+------+---------+------+---------------------------------+-------------------------------+
3 rows in set (0.00 sec)

由Waiting for table metadata lock可以看出等待获取元数据锁。

--session 1
(root@localhost) [test]> lock table t1 read;
Query OK, 0 rows affected (0.00 sec)

--session 2 
(root@localhost) [test]> insert into t1 values(6,'sh');


--session 3
(root@localhost) [test]> show processlist;
+----+------+-----------+------+---------+------+---------------------------------+-------------------------------+
| Id | User | Host      | db   | Command | Time | State                           | Info                          |
+----+------+-----------+------+---------+------+---------------------------------+-------------------------------+
|  7 | root | localhost | test | Sleep   |   22 |                                 | NULL                          |
|  8 | root | localhost | test | Query   |    7 | Waiting for table metadata lock | insert into t1 values(6,'sh') |
|  9 | root | localhost | test | Query   |    0 | init                            | show processlist              |
+----+------+-----------+------+---------+------+---------------------------------+-------------------------------+
3 rows in set (0.00 sec)

为了避免表级锁的影响,可以采取以下措施:

  1. 尽量避免显式使用 LOCK TABLES 语句。
  2. 使用 InnoDB 存储引擎,它支持行级锁,可以提高并发性能。
  3. 合理设计索引,加快查询速度,减少锁的持有时间。
  4. 优化SQL语句,避免长时间的事务和锁等待。
  5. 合理设置MySQL参数,如 innodb_lock_wait_timeout,防止长时间的锁等待。

二、行级锁(Row Lock)

1、行级锁的类型:
  • 共享锁(S Lock):允许事务读取一行数据。
  • 排他锁(X Lock):允许事务删除或更新一行数据。
2、出现行级锁的情况
  • 当事务对数据行执行 SELECT … FOR UPDATE 或 SELECT … LOCK IN SHARE MODE 时,会获取该行的排他锁或共享锁。
  • 当事务对数据行执行 UPDATE、DELETE 或 INSERT 操作时,会自动获取该行的排他锁。
  • 当查询条件没有使用到索引,或者使用了索引但未完全匹配时,可能会进行全表扫描,导致大量的行被锁定。
3、定位和排查行级锁问题:
  • 使用 SHOW ENGINE INNODB STATUS 命令查看 InnoDB 的锁情况,包括事务的锁等待和锁争用情况。
  • 查询 information_schema.INNODB_TRX 表,了解当前运行的事务及其状态。
  • 查询 information_schema.INNODB_LOCKS 和 information_schema.INNODB_LOCK_WAITS 表,了解锁的详细信息和等待情况。
  • 使用 performance_schema 数据库中的 data_locks 和 data_lock_waits 表,获取更详细的锁信息。
  • 通过慢查询日志和 EXPLAIN 分析 SQL 语句的执行计划,优化查询条件和索引使用。
4、行级锁的影响
  • 行级锁可以提高数据库的并发性能,因为锁定粒度更小,不会阻塞其他行的读写操作。
  • 但是,如果查询条件没有合适的索引,或者锁争用频繁,也可能导致性能下降和死锁问题。
  • 频繁的行级锁操作会增加系统开销,因为需要维护锁信息和进行锁的获取和释放。
5、模拟行级锁
  1. 模拟行级锁
--session 1
(root@localhost) [test]> update t1 set name='bj' where id=1;
Query OK, 1 row affected (0.00 sec)
Rows matched: 1  Changed: 1  Warnings: 0

--session 2
(root@localhost) [test]> update t1 set name='gz' where id=1;
  1. 查看锁
(root@localhost) [test]> SELECT * FROM information_schema.INNODB_TRX;
+-----------------+-----------+---------------------+----------------------------------------+---------------------+------------+---------------------+---------------------------------------------+---------------------+-------------------+-------------------+------------------+-----------------------+-----------------+-------------------+-------------------------+---------------------+-------------------+------------------------+----------------------------+---------------------------+---------------------------+------------------+----------------------------+---------------------+
| trx_id          | trx_state | trx_started         | trx_requested_lock_id                  | trx_wait_started    | trx_weight | trx_mysql_thread_id | trx_query                                   | trx_operation_state | trx_tables_in_use | trx_tables_locked | trx_lock_structs | trx_lock_memory_bytes | 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 | trx_is_read_only | trx_autocommit_non_locking | trx_schedule_weight |
+-----------------+-----------+---------------------+----------------------------------------+---------------------+------------+---------------------+---------------------------------------------+---------------------+-------------------+-------------------+------------------+-----------------------+-----------------+-------------------+-------------------------+---------------------+-------------------+------------------------+----------------------------+---------------------------+---------------------------+------------------+----------------------------+---------------------+
|           17432 | LOCK WAIT | 2024-03-15 11:43:38 | 140187079622656:83:4:2:140186994401432 | 2024-03-15 11:45:50 |          2 |                   8 | update t1 set name='gz' where id=1          | starting index read |                 1 |                 1 |                2 |                  1128 |               2 |                 0 |                       0 | REPEATABLE READ     |                 1 |                      1 | NULL                       |                         0 |                         0 |                0 |                          0 |                   1 |
|           17431 | RUNNING   | 2024-03-15 11:43:42 | NULL                                   | NULL                |          3 |                   7 | NULL                                        | NULL                |                 0 |                 1 |                2 |                  1128 |               5 |                 1 |                       0 | REPEATABLE READ     |                 1 |                      1 | NULL                       |                         0 |                         0 |                0 |                          0 |                NULL |
| 421662056332504 | RUNNING   | 2024-03-15 11:43:33 | NULL                                   | NULL                |          0 |                  10 | SELECT * FROM information_schema.INNODB_TRX | NULL                |                 0 |                 0 |                0 |                  1128 |               0 |                 0 |                       0 | REPEATABLE READ     |                 1 |                      1 | NULL                       |                         0 |                         0 |                0 |                          0 |                NULL |
+-----------------+-----------+---------------------+----------------------------------------+---------------------+------------+---------------------+---------------------------------------------+---------------------+-------------------+-------------------+------------------+-----------------------+-----------------+-------------------+-------------------------+---------------------+-------------------+------------------------+----------------------------+---------------------------+---------------------------+------------------+----------------------------+---------------------+
3 rows in set (0.00 sec)
(root@localhost) [test]> SELECT * FROM performance_schema.data_lock_waits;
+--------+----------------------------------------+----------------------------------+----------------------+---------------------+----------------------------------+----------------------------------------+--------------------------------+--------------------+-------------------+--------------------------------+
| ENGINE | REQUESTING_ENGINE_LOCK_ID              | REQUESTING_ENGINE_TRANSACTION_ID | REQUESTING_THREAD_ID | REQUESTING_EVENT_ID | REQUESTING_OBJECT_INSTANCE_BEGIN | BLOCKING_ENGINE_LOCK_ID                | BLOCKING_ENGINE_TRANSACTION_ID | BLOCKING_THREAD_ID | BLOCKING_EVENT_ID | BLOCKING_OBJECT_INSTANCE_BEGIN |
+--------+----------------------------------------+----------------------------------+----------------------+---------------------+----------------------------------+----------------------------------------+--------------------------------+--------------------+-------------------+--------------------------------+
| INNODB | 140187079622656:83:4:2:140186994401432 |                            17432 |                   47 |                  11 |                  140186994401432 | 140187079623464:83:4:2:140186994407072 |                          17431 |                 46 |                18 |                140186994407072 |
+--------+----------------------------------------+----------------------------------+----------------------+---------------------+----------------------------------+----------------------------------------+--------------------------------+--------------------+-------------------+--------------------------------+
1 row in set (0.00 sec)
(root@localhost) [test]> SELECT * FROM performance_schema.data_locks;
+--------+----------------------------------------+-----------------------+-----------+----------+---------------+-------------+----------------+-------------------+-----------------+-----------------------+-----------+-----------+-------------+------------------------+
| ENGINE | ENGINE_LOCK_ID                         | ENGINE_TRANSACTION_ID | THREAD_ID | EVENT_ID | OBJECT_SCHEMA | OBJECT_NAME | PARTITION_NAME | SUBPARTITION_NAME | INDEX_NAME      | OBJECT_INSTANCE_BEGIN | LOCK_TYPE | LOCK_MODE | LOCK_STATUS | LOCK_DATA              |
+--------+----------------------------------------+-----------------------+-----------+----------+---------------+-------------+----------------+-------------------+-----------------+-----------------------+-----------+-----------+-------------+------------------------+
| INNODB | 140187079622656:1336:140186994404000   |                 17432 |        47 |       10 | test          | t1          | NULL           | NULL              | NULL            |       140186994404000 | TABLE     | IX        | GRANTED     | NULL                   |
| INNODB | 140187079622656:83:4:2:140186994401432 |                 17432 |        47 |       11 | test          | t1          | NULL           | NULL              | GEN_CLUST_INDEX |       140186994401432 | RECORD    | X         | WAITING     | 0x000000007400         |
| INNODB | 140187079623464:1336:140186994409984   |                 17431 |        46 |       18 | test          | t1          | NULL           | NULL              | NULL            |       140186994409984 | TABLE     | IX        | GRANTED     | NULL                   |
| INNODB | 140187079623464:83:4:1:140186994407072 |                 17431 |        46 |       18 | test          | t1          | NULL           | NULL              | GEN_CLUST_INDEX |       140186994407072 | RECORD    | X         | GRANTED     | supremum pseudo-record |
| INNODB | 140187079623464:83:4:2:140186994407072 |                 17431 |        46 |       18 | test          | t1          | NULL           | NULL              | GEN_CLUST_INDEX |       140186994407072 | RECORD    | X         | GRANTED     | 0x000000007400         |
| INNODB | 140187079623464:83:4:3:140186994407072 |                 17431 |        46 |       18 | test          | t1          | NULL           | NULL              | GEN_CLUST_INDEX |       140186994407072 | RECORD    | X         | GRANTED     | 0x000000007401         |
| INNODB | 140187079623464:83:4:4:140186994407072 |                 17431 |        46 |       18 | test          | t1          | NULL           | NULL              | GEN_CLUST_INDEX |       140186994407072 | RECORD    | X         | GRANTED     | 0x000000007402         |
| INNODB | 140187079623464:83:4:5:140186994407072 |                 17431 |        46 |       18 | test          | t1          | NULL           | NULL              | GEN_CLUST_INDEX |       140186994407072 | RECORD    | X         | GRANTED     | 0x000000007403         |
+--------+----------------------------------------+-----------------------+-----------+----------+---------------+-------------+----------------+-------------------+-----------------+-----------------------+-----------+-----------+-------------+------------------------+
8 rows in set (0.00 sec)
(root@localhost) [test]> show processlist;
+----+------+-----------+------+---------+------+----------+------------------------------------+
| Id | User | Host      | db   | Command | Time | State    | Info                               |
+----+------+-----------+------+---------+------+----------+------------------------------------+
|  7 | root | localhost | test | Sleep   |   19 |          | NULL                               |
|  8 | root | localhost | test | Query   |   15 | updating | update t1 set name='gz' where id=1 |
| 10 | root | localhost | test | Query   |    0 | init     | show processlist                   |
+----+------+-----------+------+---------+------+----------+------------------------------------+

以上可以看到,最方便的定位方法是

select ENGINE,REQUESTING_ENGINE_TRANSACTION_ID,BLOCKING_ENGINE_TRANSACTION_ID from performance_schema.data_lock_waits;

--阻塞者
select a.trx_id,a.trx_state,a.trx_started,a.trx_wait_started,a.trx_query from information_schema.INNODB_TRX a,performance_schema.data_lock_waits b where a.trx_id=b.BLOCKING_ENGINE_TRANSACTION_ID;

--被阻塞者
select a.trx_id,a.trx_state,a.trx_started,a.trx_wait_started,a.trx_query from information_schema.INNODB_TRX a,performance_schema.data_lock_waits b where a.trx_id=b.REQUESTING_ENGINE_TRANSACTION_ID ;
(root@localhost) [test]> SELECT * FROM performance_schema.data_locks;
+--------+----------------------------------------+-----------------------+-----------+----------+---------------+-------------+----------------+-------------------+-----------------+-----------------------+-----------+-----------+-------------+------------------------+
| ENGINE | ENGINE_LOCK_ID                         | ENGINE_TRANSACTION_ID | THREAD_ID | EVENT_ID | OBJECT_SCHEMA | OBJECT_NAME | PARTITION_NAME | SUBPARTITION_NAME | INDEX_NAME      | OBJECT_INSTANCE_BEGIN | LOCK_TYPE | LOCK_MODE | LOCK_STATUS | LOCK_DATA              |
+--------+----------------------------------------+-----------------------+-----------+----------+---------------+-------------+----------------+-------------------+-----------------+-----------------------+-----------+-----------+-------------+------------------------+
| INNODB | 140187079622656:1336:140186994404000   |                 17434 |        50 |       11 | test          | t1          | NULL           | NULL              | NULL            |       140186994404000 | TABLE     | IX        | GRANTED     | NULL                   |
| INNODB | 140187079623464:1336:140186994409984   |       421662056334120 |        52 |       11 | test          | t1          | NULL           | NULL              | NULL            |       140186994409984 | TABLE     | IS        | GRANTED     | NULL                   |
| INNODB | 140187079623464:83:4:1:140186994407072 |       421662056334120 |        52 |       11 | test          | t1          | NULL           | NULL              | GEN_CLUST_INDEX |       140186994407072 | RECORD    | S         | GRANTED     | supremum pseudo-record |
| INNODB | 140187079623464:83:4:2:140186994407072 |       421662056334120 |        52 |       11 | test          | t1          | NULL           | NULL              | GEN_CLUST_INDEX |       140186994407072 | RECORD    | S         | GRANTED     | 0x000000007400         |
| INNODB | 140187079623464:83:4:3:140186994407072 |       421662056334120 |        52 |       11 | test          | t1          | NULL           | NULL              | GEN_CLUST_INDEX |       140186994407072 | RECORD    | S         | GRANTED     | 0x000000007401         |
| INNODB | 140187079623464:83:4:4:140186994407072 |       421662056334120 |        52 |       11 | test          | t1          | NULL           | NULL              | GEN_CLUST_INDEX |       140186994407072 | RECORD    | S         | GRANTED     | 0x000000007402         |
| INNODB | 140187079623464:83:4:5:140186994407072 |       421662056334120 |        52 |       11 | test          | t1          | NULL           | NULL              | GEN_CLUST_INDEX |       140186994407072 | RECORD    | S         | GRANTED     | 0x000000007403         |
+--------+----------------------------------------+-----------------------+-----------+----------+---------------+-------------+----------------+-------------------+-----------------+-----------------------+-----------+-----------+-------------+------------------------+

可以看到IX和IS冲突

三、意向锁

意向锁是MySQL InnoDB存储引擎中的一种表级锁,用于在行级锁之前指示事务意图获取哪种类型的锁(共享锁或排他锁)。意向锁可以提高并发性能,减少锁冲突。

1、锁种类
  1. 意向共享锁(IS Lock):事务打算在表中的某些行上设置共享锁(S Lock)。
  2. 意向排他锁(IX Lock):事务打算在表中的某些行上设置排他锁(X Lock)。
2、出现意向锁的情况
  1. 当事务准备在表中的某些行上设置共享锁时,会在表级别上请求意向共享锁。
  2. 当事务准备在表中的某些行上设置排他锁时,会在表级别上请求意向排他锁。
3、定位和排查意向锁问题
  1. 使用 SHOW ENGINE INNODB STATUS 命令查看 InnoDB 的锁情况,包括意向锁的争用情况。
  2. 查询 information_schema.INNODB_TRX、information_schema.INNODB_LOCKS 和 information_schema.INNODB_LOCK_WAITS 表,了解事务、锁的详细信息和等待情况。
  3. 使用 performance_schema 数据库中的 data_locks 和 data_lock_waits 表,获取更详细的锁信息。
4、意向锁的影响
  1. 意向锁本身不会阻塞其他事务的读写操作,它们只是表明事务意图获取行级锁的一种机制。
  2. 意向锁可以提高并发性能,因为它们允许多个事务同时在表级别上表明自己的意图,而不需要实际获取行级锁。
  3. 意向锁与行级锁的兼容性规则可以减少锁冲突,提高并发性。例如,多个事务可以同时持有意向共享锁,但意向共享锁与意向排他锁是互斥的。
5、模拟意向锁
(root@localhost) [test]> select * from t1;
+------+------+
| id   | name |
+------+------+
|    1 | gz   |
|    2 | wz   |
|    3 | zw   |
|    3 | szs  |
+------+------+
4 rows in set (0.00 sec)


(root@localhost) [test]> select trx_id,trx_state,trx_started,trx_requested_lock_id,trx_wait_started,trx_tables_locked,trx_lock_structs,trx_rows_locked from information_schema.INNODB_TRX;
+-----------------+-----------+---------------------+-----------------------+------------------+-------------------+------------------+-----------------+
| trx_id          | trx_state | trx_started         | trx_requested_lock_id | trx_wait_started | trx_tables_locked | trx_lock_structs | trx_rows_locked |
+-----------------+-----------+---------------------+-----------------------+------------------+-------------------+------------------+-----------------+
| 421662056334120 | RUNNING   | 2024-03-15 15:15:23 | NULL                  | NULL             |                 0 |                0 |               0 |
| 421662056333312 | RUNNING   | 2024-03-15 15:10:46 | NULL                  | NULL             |                 0 |                0 |               0 |
| 421662056332504 | RUNNING   | 2024-03-15 11:43:33 | NULL                  | NULL             |                 0 |                0 |               0 |
+-----------------+-----------+---------------------+-----------------------+------------------+-------------------+------------------+-----------------+
3 rows in set (0.00 sec)
(root@localhost) [test]> select * from t1 where id=1 lock in share mode;
+------+------+
| id   | name |
+------+------+
|    1 | gz   |
+------+------+
1 row in set (0.00 sec)

(root@localhost) [test]> select trx_id,trx_state,trx_started,trx_requested_lock_id,trx_wait_started,trx_tables_locked,trx_lock_structs,trx_rows_locked from information_schema.INNODB_TRX;
+-----------------+-----------+---------------------+-----------------------+------------------+-------------------+------------------+-----------------+
| trx_id          | trx_state | trx_started         | trx_requested_lock_id | trx_wait_started | trx_tables_locked | trx_lock_structs | trx_rows_locked |
+-----------------+-----------+---------------------+-----------------------+------------------+-------------------+------------------+-----------------+
| 421662056334120 | RUNNING   | 2024-03-15 15:15:23 | NULL                  | NULL             |                 0 |                0 |               0 |
| 421662056333312 | RUNNING   | 2024-03-15 15:10:46 | NULL                  | NULL             |                 0 |                0 |               0 |
| 421662056332504 | RUNNING   | 2024-03-15 11:43:33 | NULL                  | NULL             |                 0 |                0 |               0 |
+-----------------+-----------+---------------------+-----------------------+------------------+-------------------+------------------+-----------------+
3 rows in set (0.00 sec)
--session 1
(root@localhost) [test]> select * from t1 where id=1 lock in share mode;
+------+------+
| id   | name |
+------+------+
|    1 | gz   |
+------+------+
1 row in set (0.00 sec)

--session 2 
(root@localhost) [test]> update t1 set name='bj' where id=1;


--session 3

(root@localhost) [test]> select trx_id,trx_state,trx_started,trx_requested_lock_id,trx_wait_started,trx_tables_locked,trx_lock_structs,trx_rows_locked from information_schema.INNODB_TRX;
+-----------------+-----------+---------------------+----------------------------------------+---------------------+-------------------+------------------+-----------------+
| trx_id          | trx_state | trx_started         | trx_requested_lock_id                  | trx_wait_started    | trx_tables_locked | trx_lock_structs | trx_rows_locked |
+-----------------+-----------+---------------------+----------------------------------------+---------------------+-------------------+------------------+-----------------+
|           17434 | LOCK WAIT | 2024-03-15 15:10:46 | 140187079622656:83:4:2:140186994401088 | 2024-03-15 15:16:55 |                 1 |                2 |               1 |
| 421662056334120 | RUNNING   | 2024-03-15 15:15:23 | NULL                                   | NULL                |                 1 |                2 |               5 |
| 421662056332504 | RUNNING   | 2024-03-15 11:43:33 | NULL                                   | NULL                |                 0 |                0 |               0 |
+-----------------+-----------+---------------------+----------------------------------------+---------------------+-------------------+------------------+-----------------+
3 rows in set (0.00 sec)

利用前面sql看一下

(root@localhost) [test]> select a.trx_id,a.trx_state,a.trx_started,a.trx_wait_started,a.trx_query from information_schema.INNODB_TRX a,performance_schema.data_lock_waits b where a.trx_id=b.BLOCKING_ENGINE_TRANSACTION_ID;
+-----------------+-----------+---------------------+------------------+-----------+
| trx_id          | trx_state | trx_started         | trx_wait_started | trx_query |
+-----------------+-----------+---------------------+------------------+-----------+
| 421662056334120 | RUNNING   | 2024-03-15 15:15:23 | NULL             | NULL      |
+-----------------+-----------+---------------------+------------------+-----------+
1 row in set (0.00 sec)

(root@localhost) [test]> select a.trx_id,a.trx_state,a.trx_started,a.trx_wait_started,a.trx_query from information_schema.INNODB_TRX a,performance_schema.data_lock_waits b where a.trx_id=b.REQUESTING_ENGINE_TRANSACTION_ID ;
+--------+-----------+---------------------+---------------------+------------------------------------+
| trx_id | trx_state | trx_started         | trx_wait_started    | trx_query                          |
+--------+-----------+---------------------+---------------------+------------------------------------+
|  17434 | LOCK WAIT | 2024-03-15 15:10:46 | 2024-03-15 15:18:47 | update t1 set name='bj' where id=1 |
+--------+-----------+---------------------+---------------------+------------------------------------+
1 row in set (0.00 sec)

前面的意向锁因为与update不兼容阻塞了后面的update

四、间隙锁

MySQL间隙锁(Gap Lock)是InnoDB存储引擎为了防止幻读(Phantom Read)而设计的一种锁机制。它锁定的是索引记录之间的间隙,而不是记录本身。

1、间隙锁种类
  1. 记录锁(Record Lock):锁定单个索引记录。
  2. 间隙锁(Gap Lock):锁定索引记录之间的间隙。
  3. 次级间隙锁(Next-Key Lock):记录锁和间隙锁的结合,锁定记录本身和记录之前的间隙。
2、间隙锁场景
  1. 事务隔离级别为可重复读(REPEATABLE READ)时。
  2. 使用了范围条件检索记录,如between、>、<、>=、<=等操作符。
  3. 执行了加锁的操作,如SELECT … FOR UPDATE。
3、出现间隙锁的定位排查
  1. 通过SHOW ENGINE INNODB STATUS检查事务是否被阻塞。
  2. 使用INFORMATION_SCHEMA.INNODB_TRX表查看当前运行的事务。
  3. 使用INFORMATION_SCHEMA.INNODB_LOCKS表查看加锁的对象。
  4. 开启innodb_lock_monitor输出被阻塞的锁信息。
4、间隙锁的影响
  1. 并发性能降低,因为间隙被锁住了,其他事务需要等待。
  2. 可能导致死锁的发生。
  3. 如果间隙很大,会消耗大量内存。
5、模拟间隙锁
--session 1

(root@localhost) [test]> select * from t1 where id <5 for update;
+------+------+
| id   | name |
+------+------+
|    1 | gz   |
|    2 | wz   |
|    3 | zw   |
|    3 | szs  |
+------+------+
4 rows in set (0.00 sec)

--session 2


(root@localhost) [test]> insert into t1 values(4,'sh');
--session 3

(root@localhost) [test]> show processlist;
+----+------+-----------+------+---------+------+--------+-------------------------------+
| Id | User | Host      | db   | Command | Time | State  | Info                          |
+----+------+-----------+------+---------+------+--------+-------------------------------+
| 10 | root | localhost | test | Query   |    0 | init   | show processlist              |
| 14 | root | localhost | test | Query   |    2 | update | insert into t1 values(4,'sh') |
| 15 | root | localhost | test | Sleep   |  152 |        | NULL                          |
+----+------+-----------+------+---------+------+--------+-------------------------------+
3 rows in set (0.00 sec)


(root@localhost) [test]> SELECT * FROM performance_schema.data_locks;
+--------+----------------------------------------+-----------------------+-----------+----------+---------------+-------------+----------------+-------------------+-----------------+-----------------------+-----------+--------------------+-------------+------------------------+
| ENGINE | ENGINE_LOCK_ID                         | ENGINE_TRANSACTION_ID | THREAD_ID | EVENT_ID | OBJECT_SCHEMA | OBJECT_NAME | PARTITION_NAME | SUBPARTITION_NAME | INDEX_NAME      | OBJECT_INSTANCE_BEGIN | LOCK_TYPE | LOCK_MODE          | LOCK_STATUS | LOCK_DATA              |
+--------+----------------------------------------+-----------------------+-----------+----------+---------------+-------------+----------------+-------------------+-----------------+-----------------------+-----------+--------------------+-------------+------------------------+
| INNODB | 140187079622656:1336:140186994404000   |                 17436 |        53 |       10 | test          | t1          | NULL           | NULL              | NULL            |       140186994404000 | TABLE     | IX                 | GRANTED     | NULL                   |
| INNODB | 140187079622656:83:4:1:140186994401432 |                 17436 |        53 |       11 | test          | t1          | NULL           | NULL              | GEN_CLUST_INDEX |       140186994401432 | RECORD    | X,INSERT_INTENTION | WAITING     | supremum pseudo-record |
| INNODB | 140187079623464:1336:140186994409984   |                 17435 |        54 |       11 | test          | t1          | NULL           | NULL              | NULL            |       140186994409984 | TABLE     | IX                 | GRANTED     | NULL                   |
| INNODB | 140187079623464:83:4:1:140186994407072 |                 17435 |        54 |       11 | test          | t1          | NULL           | NULL              | GEN_CLUST_INDEX |       140186994407072 | RECORD    | X                  | GRANTED     | supremum pseudo-record |
| INNODB | 140187079623464:83:4:2:140186994407072 |                 17435 |        54 |       11 | test          | t1          | NULL           | NULL              | GEN_CLUST_INDEX |       140186994407072 | RECORD    | X                  | GRANTED     | 0x000000007400         |
| INNODB | 140187079623464:83:4:3:140186994407072 |                 17435 |        54 |       11 | test          | t1          | NULL           | NULL              | GEN_CLUST_INDEX |       140186994407072 | RECORD    | X                  | GRANTED     | 0x000000007401         |
| INNODB | 140187079623464:83:4:4:140186994407072 |                 17435 |        54 |       11 | test          | t1          | NULL           | NULL              | GEN_CLUST_INDEX |       140186994407072 | RECORD    | X                  | GRANTED     | 0x000000007402         |
| INNODB | 140187079623464:83:4:5:140186994407072 |                 17435 |        54 |       11 | test          | t1          | NULL           | NULL              | GEN_CLUST_INDEX |       140186994407072 | RECORD    | X                  | GRANTED     | 0x000000007403         |
+--------+----------------------------------------+-----------------------+-----------+----------+---------------+-------------+----------------+-------------------+-----------------+-----------------------+-----------+--------------------+-------------+------------------------+

可以看到X,INSERT_INTENTION

(root@localhost) [test]> select a.trx_id,a.trx_state,a.trx_started,a.trx_wait_started,a.trx_query from information_schema.INNODB_TRX a,performance_schema.data_lock_waits b where a.trx_id=b.BLOCKING_ENGINE_TRANSACTION_ID;
+--------+-----------+---------------------+------------------+-----------+
| trx_id | trx_state | trx_started         | trx_wait_started | trx_query |
+--------+-----------+---------------------+------------------+-----------+
|  17435 | RUNNING   | 2024-03-15 15:45:48 | NULL             | NULL      |
+--------+-----------+---------------------+------------------+-----------+
1 row in set (0.00 sec)

(root@localhost) [test]> select a.trx_id,a.trx_state,a.trx_started,a.trx_wait_started,a.trx_query from information_schema.INNODB_TRX a,performance_schema.data_lock_waits b where a.trx_id=b.REQUESTING_ENGINE_TRANSACTION_ID ;
+--------+-----------+---------------------+---------------------+-------------------------------+
| trx_id | trx_state | trx_started         | trx_wait_started    | trx_query                     |
+--------+-----------+---------------------+---------------------+-------------------------------+
|  17436 | LOCK WAIT | 2024-03-15 15:45:39 | 2024-03-15 15:48:05 | insert into t1 values(4,'sh') |
+--------+-----------+---------------------+---------------------+-------------------------------+
1 row in set (0.00 sec)

五、自增锁

MySQL的自增锁是一种特殊的表级锁,用于协调多个事务对AUTO_INCREMENT列的插入操作,以保证自增值的唯一性和连续性。

1、MySQL的自增锁类型
  1. AUTO-INC锁:用于INSERT语句对AUTO_INCREMENT列的插入操作。
  2. 轻量级AUTO-INC锁:用于"简单插入"(不使用INSERT … SELECT等复杂语句)对AUTO_INCREMENT列的插入操作。
2、出现自增锁的情况
  1. 当多个事务同时对一个包含AUTO_INCREMENT列的表进行插入操作时,就可能出现自增锁的竞争。
  2. 当语句中包含多个AUTO_INCREMENT列时,也会出现自增锁。
  3. 当INSERT语句比较复杂(如INSERT … SELECT)时,会使用AUTO-INC锁而不是轻量级AUTO-INC锁。
3.定位和排查自增锁
  1. 通过查询 INFORMATION_SCHEMA.INNODB_LOCKS 和 INFORMATION_SCHEMA.INNODB_LOCK_WAITS 表,可以查看当前的锁情况和锁等待情况。
  2. 通过SHOW ENGINE INNODB STATUS命令,可以查看InnoDB的状态信息,其中包括锁的信息。
  3. 通过设置innodb_print_all_deadlocks参数为ON,可以在发生死锁时打印死锁信息到错误日志中。
4、自增锁的影响
  1. 自增锁的竞争可能导致插入操作的性能下降,因为多个事务需要等待锁的释放。
  2. 在某些情况下,自增锁可能导致死锁。例如,一个事务获取了AUTO-INC锁,然后尝试获取另一个事务持有的行锁,而那个事务又在等待AUTO-INC锁,就会导致死锁。

总的来说,自增锁是MySQL为了保证AUTO_INCREMENT列的一致性而设计的一种锁机制。在实际应用中,我们需要注意监控自增锁的竞争情况,优化插入操作以减少自增锁的影响,并且要注意避免可能导致死锁的情况。