[MySQL] 锁/死锁问题一例

MySQL锁/死锁问题

在MySQL中, 不同事务隔离级别下, 锁的情况表现是不同的, 另外表的设计上有无索引也是一个因素.

做一个小的实验测试InnoDB锁表现 -😃

  • 说明

    • 事务隔离级别

      READ-COMMITED和REPEATABLE-READ

    • 测试语句

      SELECT .. FOR UPDATE(LIMIT)

      INSERT/UPDATE

  • 测试数据准备

    • 建表

      CREATE TABLE RENO (name VARCHAR(10), cycle INT, expires DATE, session VARCHAR(10)) ENGINE=InnoDB DEFAULT CHARSET=utf8;

    • 初始化数据

      INSERT INTO reno VALUES ('reno1', 1, '2016-03-06', 'session1');
      INSERT INTO reno VALUES ('reno2', 1, '2016-04-06', 'session1');
      INSERT INTO reno VALUES ('reno3', 1, '2016-05-06', 'session1');
      INSERT INTO reno VALUES ('reno4', 1, '2016-06-06', 'session1');
      INSERT INTO reno VALUES ('reno5', 1, '2016-07-06', 'session1');

  • CASE 1

    事务隔离级别是READ-COMMITTED, expires上无索引.

    Time Session 1
    (thread_id: 5690860
    tx_id: 6497569)
    Session 2
    (thread_id: 5690839
    tx_id: 6497636)
    Status
    1 SET tx_isolation='READ-COMMITTED'; START TRANSACTION; SET tx_isolation='READ-COMMITTED'; START TRANSACTION;
    2 SELECT * FROM reno WHERE expires < '2016-05-06' ORDER BY expires LIMIT 1 FOR UPDATE;
    3 INSERT INTO reno VALUES ('reno6', 1, '2016-08-06', 'session2'); Success
    4 INSERT INTO reno VALUES ('reno6', 1, '2016-02-06', 'session2'); Success
    5 INSERT INTO reno VALUES ('reno6', 1, '2016-04-20', 'session2'); Success
    6 INSERT INTO reno VALUES ('reno6', 1, '2016-03-20', 'session2'); Success
    7 INSERT INTO reno VALUES ('reno6', 1, '2016-03-06', 'session2'); Success
    8 SELECT * FROM reno WHERE expires < '2016-09-06' ORDER BY expires LIMIT 1 FOR UPDATE; Hang
    ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction
    9 SELECT * FROM reno WHERE expires < '2016-09-06' ORDER BY expires LIMIT 1 FOR UPDATE; SELECT * FROM reno WHERE expires < '2016-09-06' ORDER BY expires LIMIT 1 FOR UPDATE; Session 1: Deadlock
    ERROR 1213 (40001): Deadlock found when trying to get lock; try restarting transaction

    ------------------------
    LATEST DETECTED DEADLOCK
    ------------------------
    2016-03-06 14:21:10 2b6720080700
    *** (1) TRANSACTION:
    TRANSACTION 6497569, ACTIVE 383 sec fetching rows
    mysql tables in use 1, locked 1
    LOCK WAIT 3 lock struct(s), heap size 1184, 7 row lock(s)
    MySQL thread id 5690860, OS thread handle 0x2b6716080700, query id 21623406 localhost root Creating sort index
    SELECT * FROM reno WHERE expires < '2016-09-06' ORDER BY expires LIMIT 1 FOR UPDATE
    *** (1) HOLDS THE LOCK(S):
    RECORD LOCKS space id 13 page no 3 n bits 80 index GEN_CLUST_INDEX of table test.reno trx id 6497569 lock_mode X locks rec but not gap
    Record lock, heap no 2 PHYSICAL RECORD: n_fields 7; compact format; info bits 0
    0: len 6; hex 000000000200; asc ;;
    1: len 6; hex 000000632148; asc c!H;;
    2: len 7; hex a3000001460110; asc F ;;
    3: len 5; hex 72656e6f31; asc reno1;;
    4: len 4; hex 80000001; asc ;;
    5: len 3; hex 8fc066; asc f;;
    6: len 8; hex 73657373696f6e31; asc session1;;

    Record lock, heap no 3 PHYSICAL RECORD: n_fields 7; compact format; info bits 0
    0: len 6; hex 000000000201; asc ;;
    1: len 6; hex 000000632159; asc c!Y;;
    2: len 7; hex ac000001c30110; asc ;;
    3: len 5; hex 72656e6f32; asc reno2;;
    4: len 4; hex 80000001; asc ;;
    5: len 3; hex 8fc086; asc ;;
    6: len 8; hex 73657373696f6e31; asc session1;;

    Record lock, heap no 4 PHYSICAL RECORD: n_fields 7; compact format; info bits 0
    0: len 6; hex 000000000202; asc ;;
    1: len 6; hex 000000632161; asc c!a;;
    2: len 7; hex b1000001c40110; asc ;;
    3: len 5; hex 72656e6f33; asc reno3;;
    4: len 4; hex 80000001; asc ;;
    5: len 3; hex 8fc0a6; asc ;;
    6: len 8; hex 73657373696f6e31; asc session1;;

    Record lock, heap no 5 PHYSICAL RECORD: n_fields 7; compact format; info bits 0
    0: len 6; hex 000000000203; asc ;;
    1: len 6; hex 000000632163; asc c!c;;
    2: len 7; hex b2000001c50110; asc ;;
    3: len 5; hex 72656e6f34; asc reno4;;
    4: len 4; hex 80000001; asc ;;
    5: len 3; hex 8fc0c6; asc ;;
    6: len 8; hex 73657373696f6e31; asc session1;;

    Record lock, heap no 6 PHYSICAL RECORD: n_fields 7; compact format; info bits 0
    0: len 6; hex 000000000204; asc ;;
    1: len 6; hex 000000632164; asc c!d;;
    2: len 7; hex b3000001c60110; asc ;;
    3: len 5; hex 72656e6f35; asc reno5;;
    4: len 4; hex 80000001; asc ;;
    5: len 3; hex 8fc0e6; asc ;;
    6: len 8; hex 73657373696f6e31; asc session1;;

    *** (1) WAITING FOR THIS LOCK TO BE GRANTED:
    RECORD LOCKS space id 13 page no 3 n bits 80 index GEN_CLUST_INDEX of table test.reno trx id 6497569 lock_mode X locks rec but not gap waiting
    Record lock, heap no 7 PHYSICAL RECORD: n_fields 7; compact format; info bits 0
    0: len 6; hex 000000000206; asc ;;
    1: len 6; hex 000000632564; asc c%d;;
    2: len 7; hex b6000001e00110; asc ;;
    3: len 5; hex 72656e6f36; asc reno6;;
    4: len 4; hex 80000001; asc ;;
    5: len 3; hex 8fc106; asc ;;
    6: len 8; hex 73657373696f6e32; asc session2;;

    *** (2) TRANSACTION:
    TRANSACTION 6497636, ACTIVE 199 sec starting index read, thread declared inside InnoDB 5000
    mysql tables in use 1, locked 1
    3 lock struct(s), heap size 360, 2 row lock(s), undo log entries 1
    MySQL thread id 5690839, OS thread handle 0x2b6720080700, query id 21623519 localhost root Creating sort index
    SELECT * FROM reno WHERE expires < '2016-09-06' ORDER BY expires LIMIT 1 FOR UPDATE
    *** (2) HOLDS THE LOCK(S):
    RECORD LOCKS space id 13 page no 3 n bits 80 index GEN_CLUST_INDEX of table test.reno trx id 6497636 lock_mode X locks rec but not gap
    Record lock, heap no 7 PHYSICAL RECORD: n_fields 7; compact format; info bits 0
    0: len 6; hex 000000000206; asc ;;
    1: len 6; hex 000000632564; asc c%d;;
    2: len 7; hex b6000001e00110; asc ;;
    3: len 5; hex 72656e6f36; asc reno6;;
    4: len 4; hex 80000001; asc ;;
    5: len 3; hex 8fc106; asc ;;
    6: len 8; hex 73657373696f6e32; asc session2;;

    *** (2) WAITING FOR THIS LOCK TO BE GRANTED:
    RECORD LOCKS space id 13 page no 3 n bits 80 index GEN_CLUST_INDEX of table test.reno trx id 6497636 lock_mode X locks rec but not gap waiting
    Record lock, heap no 2 PHYSICAL RECORD: n_fields 7; compact format; info bits 0
    0: len 6; hex 000000000200; asc ;;
    1: len 6; hex 000000632148; asc c!H;;
    2: len 7; hex a3000001460110; asc F ;;
    3: len 5; hex 72656e6f31; asc reno1;;
    4: len 4; hex 80000001; asc ;;
    5: len 3; hex 8fc066; asc f;;
    6: len 8; hex 73657373696f6e31; asc session1;;

    *** WE ROLL BACK TRANSACTION (1)

    select * from 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 |
    +---------+-----------+---------------------+-----------------------+---------------------+------------+---------------------+-------------------------------------------------------------------------------------+---------------------+-------------------+-------------------+------------------+-----------------------+-----------------+-------------------+-------------------------+---------------------+-------------------+------------------------+----------------------------+---------------------------+---------------------------+------------------+----------------------------+
    | 6497734 | LOCK WAIT | 2016-03-06 14:21:48 | 6497734:13:3:2 | 2016-03-06 14:21:48 | 2 | 5690860 | SELECT * FROM reno WHERE expires < '2016-09-06' ORDER BY expires LIMIT 1 FOR UPDATE | starting index read | 1 | 1 | 2 | 360 | 1 | 0 | 0 | READ COMMITTED | 1 | 1 | NULL | 0 | 10000 | 0 | 0 |
    | 6497636 | RUNNING | 2016-03-06 14:17:51 | NULL | NULL | 4 | 5690839 | NULL | NULL | 0 | 0 | 3 | 360 | 6 | 1 | 0 | READ COMMITTED | 1 | 1 | NULL | 0 | 10000 | 0 | 0 |
    +---------+-----------+---------------------+-----------------------+---------------------+------------+---------------------+-------------------------------------------------------------------------------------+---------------------+-------------------+-------------------+------------------+-----------------------+-----------------+-------------------+-------------------------+---------------------+-------------------+------------------------+----------------------------+---------------------------+---------------------------+------------------+----------------------------+

    select * from INNODB_LOCKS;
    +----------------+-------------+-----------+-----------+---------------+-----------------+------------+-----------+----------+----------------+
    | lock_id | lock_trx_id | lock_mode | lock_type | lock_table | lock_index | lock_space | lock_page | lock_rec | lock_data |
    +----------------+-------------+-----------+-----------+---------------+-----------------+------------+-----------+----------+----------------+
    | 6497734:13:3:2 | 6497734 | X | RECORD | test.reno | GEN_CLUST_INDEX | 13 | 3 | 2 | 0x000000000200 |
    | 6497636:13:3:2 | 6497636 | X | RECORD | test.reno | GEN_CLUST_INDEX | 13 | 3 | 2 | 0x000000000200 |
    +----------------+-------------+-----------+-----------+---------------+-----------------+------------+-----------+----------+----------------+

    select * from INNODB_LOCK_WAITS;
    +-------------------+-------------------+-----------------+------------------+
    | requesting_trx_id | requested_lock_id | blocking_trx_id | blocking_lock_id |
    +-------------------+-------------------+-----------------+------------------+
    | 6497734 | 6497734:13:3:2 | 6497636 | 6497636:13:3:2 |
    +-------------------+-------------------+-----------------+------------------+

    从以上的测试过程以及InnoDB状态可以看出:

    • 在READ-COMMITTED隔离级别下, 即使Session 1锁住了纪录expires < '2016-05-06', Session 2可以插入任何范围的记录(Session使用的是lock_mode X locks rec but not gap);
    • 在Session 1视图请求更高范围的锁(expires < '2016-09-06')时, 则会hang住, 如果接下来Session 2做同样的请求, 那么Session 1则会出现Deadlock而被回滚;
    • 当InnoDB表无主键时, InnoDB默认会创建自己的cluster index(GEN_CLUST_INDEX);
  • CASE 2

    事务隔离级别是REPEATABLE-READ, expires上无索引.

    Time Session 1
    (thread_id: 5690860
    tx_id: 6498890)
    Session 2
    (thread_id: 5690839
    tx_id: 6498893)
    Status
    1 SET tx_isolation='REPEATABLE-READ'; START TRANSACTION; SET tx_isolation='REPEATABLE-READ'; START TRANSACTION;
    2 SELECT * FROM reno WHERE expires < '2016-05-06' ORDER BY expires LIMIT 1 FOR UPDATE;
    3 INSERT INTO reno VALUES ('reno6', 1, '2016-08-06', 'session2'); Hang
    ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction

    ------------
    TRANSACTIONS
    ------------
    Trx id counter 6498920
    Purge done for trx's n:o < 6498920 undo n:o < 0 state: running but idle
    History list length 3502
    LIST OF TRANSACTIONS FOR EACH SESSION:
    ---TRANSACTION 6498893, ACTIVE 72 sec inserting
    mysql tables in use 1, locked 1
    LOCK WAIT 2 lock struct(s), heap size 360, 2 row lock(s)
    MySQL thread id 5690839, OS thread handle 0x2b6720080700, query id 21628179 localhost root update
    INSERT INTO reno VALUES ('reno6', 1, '2016-08-06 12:00:00', 'session2')
    ------- TRX HAS BEEN WAITING 51 SEC FOR THIS LOCK TO BE GRANTED:
    RECORD LOCKS space id 13 page no 3 n bits 80 index GEN_CLUST_INDEX of table test.reno trx id 6498893 lock_mode X insert intention waiting
    Record lock, heap no 1 PHYSICAL RECORD: n_fields 1; compact format; info bits 0
    0: len 8; hex 73757072656d756d; asc supremum;;

    ------------------
    ---TRANSACTION 6498890, ACTIVE 83 sec
    2 lock struct(s), heap size 360, 6 row lock(s)
    MySQL thread id 5690860, OS thread handle 0x2b6716080700, query id 21628134 localhost root cleaning up

    select * from 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 |
    +---------+-----------+---------------------+-----------------------+---------------------+------------+---------------------+-------------------------------------------------------------------------+---------------------+-------------------+-------------------+------------------+-----------------------+-----------------+-------------------+-------------------------+---------------------+-------------------+------------------------+----------------------------+---------------------------+---------------------------+------------------+----------------------------+
    | 6498893 | LOCK WAIT | 2016-03-06 15:14:10 | 6498893:13:3:1 | 2016-03-06 15:14:31 | 2 | 5690839 | INSERT INTO reno VALUES ('reno6', 1, '2016-08-06', 'session2') | inserting | 1 | 1 | 2 | 360 | 2 | 0 | 0 | REPEATABLE READ | 1 | 1 | NULL | 0 | 10000 | 0 | 0 |
    | 6498890 | RUNNING | 2016-03-06 15:13:59 | NULL | NULL | 2 | 5690860 | NULL | NULL | 0 | 0 | 2 | 360 | 6 | 0 | 0 | REPEATABLE READ | 1 | 1 | NULL | 0 | 10000 | 0 | 0 |
    +---------+-----------+---------------------+-----------------------+---------------------+------------+---------------------+-------------------------------------------------------------------------+---------------------+-------------------+-------------------+------------------+-----------------------+-----------------+-------------------+-------------------------+---------------------+-------------------+------------------------+----------------------------+---------------------------+---------------------------+------------------+----------------------------+

    select * from INNODB_LOCKS;
    +----------------+-------------+-----------+-----------+---------------+-----------------+------------+-----------+----------+------------------------+
    | lock_id | lock_trx_id | lock_mode | lock_type | lock_table | lock_index | lock_space | lock_page | lock_rec | lock_data |
    +----------------+-------------+-----------+-----------+---------------+-----------------+------------+-----------+----------+------------------------+
    | 6498893:13:3:1 | 6498893 | X | RECORD | test.reno | GEN_CLUST_INDEX | 13 | 3 | 1 | supremum pseudo-record |
    | 6498890:13:3:1 | 6498890 | X | RECORD | test.reno | GEN_CLUST_INDEX | 13 | 3 | 1 | supremum pseudo-record |
    +----------------+-------------+-----------+-----------+---------------+-----------------+------------+-----------+----------+------------------------+

    select * from INNODB_LOCK_WAITS;
    +-------------------+-------------------+-----------------+------------------+
    | requesting_trx_id | requested_lock_id | blocking_trx_id | blocking_lock_id |
    +-------------------+-------------------+-----------------+------------------+
    | 6498893 | 6498893:13:3:1 | 6498890 | 6498890:13:3:1 |
    +-------------------+-------------------+-----------------+------------------+

    从以上的测试过程以及InnoDB状态可以看出:

    • 在REPEATABLE-READ隔离级别下, 当Session 1锁住了纪录expires < '2016-05-06', Session 2是无法插入任何记录的(lock_mode X insert intention waiting);
    • 在Session 1使用了gap lock, insert intention也是一种gap lock;
    • 当InnoDB表无主键时, InnoDB默认会创建自己的cluster index(GEN_CLUST_INDEX);
  • CASE 3

    事务隔离级别是READ-COMMITTED, name有主键, expires有索引.

    CREATE TABLE RENO1 (name VARCHAR(10), cycle INT, expires DATE, session VARCHAR(10), PRIMARY KEY (name), INDEX(expires)) ENGINE=InnoDB DEFAULT CHARSET=utf8;

    Time Session 1
    (thread_id: 5690860
    tx_id: 6499370)
    Session 2
    (thread_id: 5690839
    tx_id: 6499379)
    Status
    1 SET tx_isolation='READ-COMMITTED'; START TRANSACTION; SET tx_isolation='READ-COMMITTED'; START TRANSACTION;
    2 SELECT * FROM reno1 WHERE expires < '2016-05-06' ORDER BY expires LIMIT 1 FOR UPDATE;
    3 INSERT INTO reno1 VALUES ('reno6', 1, '2016-08-06', 'session2'); Success
    4 SELECT * FROM reno1 WHERE expires < '2016-08-06' ORDER BY expires LIMIT 1 FOR UPDATE; Hang
    ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction
    5 SELECT * FROM reno1 WHERE expires < '2016-09-06' ORDER BY expires LIMIT 1 FOR UPDATE; Success
    6 INSERT INTO reno1 VALUES ('reno7', 1, '2016-02-06', 'session2'); Success
    7 INSERT INTO reno1 VALUES ('reno8', 1, '2016-03-12', 'session2'); Success
    8 INSERT INTO reno1 VALUES ('reno9', 1, '2016-04-12', 'session2'); Success
    9 INSERT INTO reno1 VALUES ('reno10', 1, '2016-05-12', 'session2'); Success
    10 INSERT INTO reno1 VALUES ('reno11', 1, '2016-03-06', 'session2'); Success

    ------------
    TRANSACTIONS
    ------------
    Trx id counter 6499386
    Purge done for trx's n:o < 6499386 undo n:o < 0 state: running but idle
    History list length 3669
    LIST OF TRANSACTIONS FOR EACH SESSION:
    ---TRANSACTION 6499379, ACTIVE 15 sec starting index read
    mysql tables in use 1, locked 1
    LOCK WAIT 2 lock struct(s), heap size 360, 1 row lock(s), undo log entries 1
    MySQL thread id 5690839, OS thread handle 0x2b6720080700, query id 21629889 localhost root Sending data
    SELECT * FROM reno1 WHERE expires < '2016-08-06' ORDER BY expires LIMIT 1 FOR UPDATE
    ------- TRX HAS BEEN WAITING 6 SEC FOR THIS LOCK TO BE GRANTED:
    RECORD LOCKS space id 14 page no 4 n bits 80 index expires of table test.reno1 trx id 6499379 lock_mode X locks rec but not gap waiting
    Record lock, heap no 2 PHYSICAL RECORD: n_fields 2; compact format; info bits 0
    0: len 3; hex 8fc066; asc f;;
    1: len 5; hex 72656e6f31; asc reno1;;

    ------------------
    ---TRANSACTION 6499370, ACTIVE 36 sec
    3 lock struct(s), heap size 360, 2 row lock(s)
    MySQL thread id 5690860, OS thread handle 0x2b6716080700, query id 21629845 localhost root cleaning up

    select * from 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 |
    +---------+-----------+---------------------+-----------------------+---------------------+------------+---------------------+--------------------------------------------------------------------------------------+---------------------+-------------------+-------------------+------------------+-----------------------+-----------------+-------------------+-------------------------+---------------------+-------------------+------------------------+----------------------------+---------------------------+---------------------------+------------------+----------------------------+
    | 6499379 | LOCK WAIT | 2016-03-06 15:33:57 | 6499379:14:4:2 | 2016-03-06 15:34:06 | 3 | 5690839 | SELECT * FROM reno1 WHERE expires < '2016-08-06' ORDER BY expires LIMIT 1 FOR UPDATE | starting index read | 1 | 1 | 2 | 360 | 1 | 1 | 0 | READ COMMITTED | 1 | 1 | NULL | 0 | 10000 | 0 | 0 |
    | 6499370 | RUNNING | 2016-03-06 15:33:36 | NULL | NULL | 3 | 5690860 | NULL | NULL | 0 | 0 | 3 | 360 | 2 | 0 | 0 | READ COMMITTED | 1 | 1 | NULL | 0 | 10000 | 0 | 0 |
    +---------+-----------+---------------------+-----------------------+---------------------+------------+---------------------+--------------------------------------------------------------------------------------+---------------------+-------------------+-------------------+------------------+-----------------------+-----------------+-------------------+-------------------------+---------------------+-------------------+------------------------+----------------------------+---------------------------+---------------------------+------------------+----------------------------+

    select * from INNODB_LOCKS;
    +----------------+-------------+-----------+-----------+----------------+------------+------------+-----------+----------+------------------+
    | lock_id | lock_trx_id | lock_mode | lock_type | lock_table | lock_index | lock_space | lock_page | lock_rec | lock_data |
    +----------------+-------------+-----------+-----------+----------------+------------+------------+-----------+----------+------------------+
    | 6499379:14:4:2 | 6499379 | X | RECORD | test.reno1 | expires | 14 | 4 | 2 | 1032294, 'reno1' |
    | 6499370:14:4:2 | 6499370 | X | RECORD | test.reno1 | expires | 14 | 4 | 2 | 1032294, 'reno1' |
    +----------------+-------------+-----------+-----------+----------------+------------+------------+-----------+----------+------------------+

    select * from INNODB_LOCK_WAITS;
    +-------------------+-------------------+-----------------+------------------+
    | requesting_trx_id | requested_lock_id | blocking_trx_id | blocking_lock_id |
    +-------------------+-------------------+-----------------+------------------+
    | 6499379 | 6499379:14:4:2 | 6499370 | 6499370:14:4:2 |
    +-------------------+-------------------+-----------------+------------------+

    从以上的测试过程以及InnoDB状态可以看出:

    • 在READ-COMMITTED隔离级别下, 当expires有索引时, Session 1可以获取更高范围的锁查询;
    • 但Session 2无法获取锁, 因为Session 1已经持有范围锁资源;

    这里把LIMIT去掉看看有什么现象

    Time Session 1
    (thread_id: 5690860
    tx_id: 6500454)
    Session 2
    (thread_id: 5690839
    tx_id: 6500459)
    Status
    1 SET tx_isolation='READ-COMMITTED'; START TRANSACTION; SET tx_isolation='READ-COMMITTED'; START TRANSACTION;
    2 SELECT * FROM reno1 WHERE expires < '2016-05-06' ORDER BY expires FOR UPDATE;
    3 INSERT INTO reno1 VALUES ('reno6', 1, '2016-08-06', 'session2'); Success
    4 SELECT * FROM reno1 WHERE expires < '2016-08-06' ORDER BY expires FOR UPDATE; Hang
    ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction
    5 SELECT * FROM reno1 WHERE expires < '2016-08-06' ORDER BY expires FOR UPDATE; SELECT * FROM reno1 WHERE expires < '2016-08-06' ORDER BY expires FOR UPDATE; Session 2: Deadlock
    ERROR 1213 (40001): Deadlock found when trying to get lock; try restarting transaction
    5 INSERT INTO reno1 VALUES ('reno7', 1, '2016-02-06', 'session2'); Success
    6 INSERT INTO reno1 VALUES ('reno8', 1, '2016-03-12', 'session2'); Success
    7 INSERT INTO reno1 VALUES ('reno9', 1, '2016-04-12', 'session2'); Success
    8 INSERT INTO reno1 VALUES ('reno10', 1, '2016-05-12', 'session2'); Success
    9 INSERT INTO reno1 VALUES ('reno11', 1, '2016-03-06', 'session2'); Success

    ------------------------
    LATEST DETECTED DEADLOCK
    ------------------------
    2016-03-06 16:16:14 2b6720080700
    *** (1) TRANSACTION:
    TRANSACTION 6500454, ACTIVE 52 sec fetching rows
    mysql tables in use 1, locked 1
    LOCK WAIT 4 lock struct(s), heap size 1184, 9 row lock(s)
    MySQL thread id 5690860, OS thread handle 0x2b6716080700, query id 21633511 localhost root Creating sort index
    SELECT * FROM reno1 WHERE expires < '2016-08-06' ORDER BY expires FOR UPDATE
    *** (1) HOLDS THE LOCK(S):
    RECORD LOCKS space id 16 page no 3 n bits 80 index PRIMARY of table test.reno1 trx id 6500454 lock_mode X locks rec but not gap
    Record lock, heap no 2 PHYSICAL RECORD: n_fields 6; compact format; info bits 0
    0: len 5; hex 72656e6f31; asc reno1;;
    1: len 6; hex 000000632f38; asc c/8;;
    2: len 7; hex b4000002080110; asc ;;
    3: len 4; hex 80000001; asc ;;
    4: len 3; hex 8fc066; asc f;;
    5: len 8; hex 73657373696f6e31; asc session1;;

    Record lock, heap no 3 PHYSICAL RECORD: n_fields 6; compact format; info bits 0
    0: len 5; hex 72656e6f32; asc reno2;;
    1: len 6; hex 000000632f39; asc c/9;;
    2: len 7; hex b5000002090110; asc ;;
    3: len 4; hex 80000001; asc ;;
    4: len 3; hex 8fc086; asc ;;
    5: len 8; hex 73657373696f6e31; asc session1;;

    Record lock, heap no 4 PHYSICAL RECORD: n_fields 6; compact format; info bits 0
    0: len 5; hex 72656e6f33; asc reno3;;
    1: len 6; hex 000000632f3c; asc c/<;;
    2: len 7; hex b70000020a0110; asc ;;
    3: len 4; hex 80000001; asc ;;
    4: len 3; hex 8fc0a6; asc ;;
    5: len 8; hex 73657373696f6e31; asc session1;;

    Record lock, heap no 5 PHYSICAL RECORD: n_fields 6; compact format; info bits 0
    0: len 5; hex 72656e6f34; asc reno4;;
    1: len 6; hex 000000632f44; asc c/D;;
    2: len 7; hex bc000001da0110; asc ;;
    3: len 4; hex 80000001; asc ;;
    4: len 3; hex 8fc0c6; asc ;;
    5: len 8; hex 73657373696f6e31; asc session1;;

    Record lock, heap no 6 PHYSICAL RECORD: n_fields 6; compact format; info bits 0
    0: len 5; hex 72656e6f35; asc reno5;;
    1: len 6; hex 000000632f4c; asc c/L;;
    2: len 7; hex c0000001de0110; asc ;;
    3: len 4; hex 80000001; asc ;;
    4: len 3; hex 8fc0e6; asc ;;
    5: len 8; hex 73657373696f6e31; asc session1;;

    *** (1) WAITING FOR THIS LOCK TO BE GRANTED:
    RECORD LOCKS space id 16 page no 3 n bits 80 index PRIMARY of table test.reno1 trx id 6500454 lock_mode X locks rec but not gap waiting
    Record lock, heap no 7 PHYSICAL RECORD: n_fields 6; compact format; info bits 0
    0: len 5; hex 72656e6f36; asc reno6;;
    1: len 6; hex 00000063306b; asc c0k;;
    2: len 7; hex d2000001540110; asc T ;;
    3: len 4; hex 80000001; asc ;;
    4: len 3; hex 8fc106; asc ;;
    5: len 8; hex 73657373696f6e32; asc session2;;

    *** (2) TRANSACTION:
    TRANSACTION 6500459, ACTIVE 41 sec starting index read, thread declared inside InnoDB 5000
    mysql tables in use 1, locked 1
    3 lock struct(s), heap size 360, 2 row lock(s), undo log entries 1
    MySQL thread id 5690839, OS thread handle 0x2b6720080700, query id 21633522 localhost root Creating sort index
    SELECT * FROM reno1 WHERE expires < '2016-08-06' ORDER BY expires FOR UPDATE
    *** (2) HOLDS THE LOCK(S):
    RECORD LOCKS space id 16 page no 3 n bits 80 index PRIMARY of table test.reno1 trx id 6500459 lock_mode X locks rec but not gap
    Record lock, heap no 7 PHYSICAL RECORD: n_fields 6; compact format; info bits 0
    0: len 5; hex 72656e6f36; asc reno6;;
    1: len 6; hex 00000063306b; asc c0k;;
    2: len 7; hex d2000001540110; asc T ;;
    3: len 4; hex 80000001; asc ;;
    4: len 3; hex 8fc106; asc ;;
    5: len 8; hex 73657373696f6e32; asc session2;;

    *** (2) WAITING FOR THIS LOCK TO BE GRANTED:
    RECORD LOCKS space id 16 page no 3 n bits 80 index PRIMARY of table test.reno1 trx id 6500459 lock_mode X locks rec but not gap waiting
    Record lock, heap no 2 PHYSICAL RECORD: n_fields 6; compact format; info bits 0
    0: len 5; hex 72656e6f31; asc reno1;;
    1: len 6; hex 000000632f38; asc c/8;;
    2: len 7; hex b4000002080110; asc ;;
    3: len 4; hex 80000001; asc ;;
    4: len 3; hex 8fc066; asc f;;
    5: len 8; hex 73657373696f6e31; asc session1;;

    *** WE ROLL BACK TRANSACTION (2)

    select * from 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 |
    +---------+-----------+---------------------+-----------------------+---------------------+------------+---------------------+------------------------------------------------------------------------------+---------------------+-------------------+-------------------+------------------+-----------------------+-----------------+-------------------+-------------------------+---------------------+-------------------+------------------------+----------------------------+---------------------------+---------------------------+------------------+----------------------------+
    | 6500512 | RUNNING | 2016-03-06 16:17:59 | NULL | NULL | 7 | 5690839 | NULL | NULL | 0 | 0 | 2 | 360 | 2 | 5 | 0 | READ COMMITTED | 1 | 1 | NULL | 0 | 10000 | 0 | 0 |
    | 6500454 | LOCK WAIT | 2016-03-06 16:15:22 | 6500454:16:3:14 | 2016-03-06 16:19:10 | 5 | 5690860 | SELECT * FROM reno1 WHERE expires < '2016-08-06' ORDER BY expires FOR UPDATE | fetching rows | 1 | 1 | 5 | 1184 | 9 | 0 | 0 | READ COMMITTED | 1 | 1 | NULL | 0 | 10000 | 0 | 0 |
    +---------+-----------+---------------------+-----------------------+---------------------+------------+---------------------+------------------------------------------------------------------------------+---------------------+-------------------+-------------------+------------------+-----------------------+-----------------+-------------------+-------------------------+---------------------+-------------------+------------------------+----------------------------+---------------------------+---------------------------+------------------+----------------------------+

    select * from INNODB_LOCKS;
    +-----------------+-------------+-----------+-----------+----------------+------------+------------+-----------+----------+-----------+
    | lock_id | lock_trx_id | lock_mode | lock_type | lock_table | lock_index | lock_space | lock_page | lock_rec | lock_data |
    +-----------------+-------------+-----------+-----------+----------------+------------+------------+-----------+----------+-----------+
    | 6500454:16:3:14 | 6500454 | X | RECORD | test.reno1 | PRIMARY | 16 | 3 | 14 | 'reno10' |
    | 6500512:16:3:14 | 6500512 | X | RECORD | test.reno1 | PRIMARY | 16 | 3 | 14 | 'reno10' |
    +-----------------+-------------+-----------+-----------+----------------+------------+------------+-----------+----------+-----------+

    select * from INNODB_LOCK_WAITS;
    +-------------------+-------------------+-----------------+------------------+
    | requesting_trx_id | requested_lock_id | blocking_trx_id | blocking_lock_id |
    +-------------------+-------------------+-----------------+------------------+
    | 6500454 | 6500454:16:3:14 | 6500512 | 6500512:16:3:14 |
    +-------------------+-------------------+-----------------+------------------+

    当去掉LIMIT 1时, 该逻辑即发生死锁.

  • CASE 4

    事务隔离级别是REPEATABLE-READ, name有主键, expires有索引.

    Time Session 1
    (thread_id: 5690860
    tx_id: 6500940)
    Session 2
    (thread_id: 5690839
    tx_id: 6500945)
    Status
    1 SET tx_isolation='REPEATABLE-READ'; START TRANSACTION; SET tx_isolation='REPEATABLE-READ'; START TRANSACTION;
    2 SELECT * FROM reno1 WHERE expires < '2016-05-06' ORDER BY expires LIMIT 1 FOR UPDATE;
    3 INSERT INTO reno1 VALUES ('reno6', 1, '2016-08-06', 'session2'); Success
    4 SELECT * FROM reno1 WHERE expires < '2016-08-06' ORDER BY expires LIMIT 1 FOR UPDATE; Success
    5 SELECT * FROM reno1 WHERE expires < '2016-08-06' ORDER BY expires FOR UPDATE; Session 2: Hang
    ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction
    5 INSERT INTO reno1 VALUES ('reno7', 1, '2016-02-06', 'session2'); Session 2: Hang
    ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction
    6 INSERT INTO reno1 VALUES ('reno8', 1, '2016-03-12', 'session2'); Success
    7 INSERT INTO reno1 VALUES ('reno9', 1, '2016-04-12', 'session2'); Success
    8 INSERT INTO reno1 VALUES ('reno10', 1, '2016-05-12', 'session2'); Success
    9 INSERT INTO reno1 VALUES ('reno11', 1, '2016-03-06', 'session2'); Success

    ------------
    TRANSACTIONS
    ------------
    Trx id counter 6501148
    Purge done for trx's n:o < 6501148 undo n:o < 0 state: running but idle
    History list length 3739
    LIST OF TRANSACTIONS FOR EACH SESSION:
    ---TRANSACTION 6500945, ACTIVE 474 sec inserting
    mysql tables in use 1, locked 1
    LOCK WAIT 2 lock struct(s), heap size 1184, 5 row lock(s), undo log entries 6
    MySQL thread id 5690839, OS thread handle 0x2b6720080700, query id 21635931 localhost root update
    INSERT INTO reno1 VALUES ('reno7', 1, '2016-02-06', 'session2')
    ------- TRX HAS BEEN WAITING 5 SEC FOR THIS LOCK TO BE GRANTED:
    RECORD LOCKS space id 16 page no 4 n bits 88 index expires of table test.reno1 trx id 6500945 lock_mode X locks gap before rec insert intention waiting
    Record lock, heap no 2 PHYSICAL RECORD: n_fields 2; compact format; info bits 0
    0: len 3; hex 8fc066; asc f;;
    1: len 5; hex 72656e6f31; asc reno1;;

    ------------------
    ---TRANSACTION 6500940, ACTIVE 483 sec
    3 lock struct(s), heap size 360, 2 row lock(s)
    MySQL thread id 5690860, OS thread handle 0x2b6716080700, query id 21635410 localhost root cleaning up

    select * from 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 |
    +---------+-----------+---------------------+-----------------------+---------------------+------------+---------------------+-----------------------------------------------------------------+---------------------+-------------------+-------------------+------------------+-----------------------+-----------------+-------------------+-------------------------+---------------------+-------------------+------------------------+----------------------------+---------------------------+---------------------------+------------------+----------------------------+
    | 6500945 | LOCK WAIT | 2016-03-06 16:36:21 | 6500945:16:4:2 | 2016-03-06 16:44:10 | 8 | 5690839 | INSERT INTO reno1 VALUES ('reno7', 1, '2016-02-06', 'session2') | inserting | 1 | 1 | 2 | 1184 | 5 | 6 | 0 | REPEATABLE READ | 1 | 1 | NULL | 0 | 10000 | 0 | 0 |
    | 6500940 | RUNNING | 2016-03-06 16:36:12 | NULL | NULL | 3 | 5690860 | NULL | NULL | 0 | 0 | 3 | 360 | 2 | 0 | 0 | REPEATABLE READ | 1 | 1 | NULL | 0 | 10000 | 0 | 0 |
    +---------+-----------+---------------------+-----------------------+---------------------+------------+---------------------+-----------------------------------------------------------------+---------------------+-------------------+-------------------+------------------+-----------------------+-----------------+-------------------+-------------------------+---------------------+-------------------+------------------------+----------------------------+---------------------------+---------------------------+------------------+----------------------------+

    select * from INNODB_LOCKS;
    +----------------+-------------+-----------+-----------+----------------+------------+------------+-----------+----------+------------------+
    | lock_id | lock_trx_id | lock_mode | lock_type | lock_table | lock_index | lock_space | lock_page | lock_rec | lock_data |
    +----------------+-------------+-----------+-----------+----------------+------------+------------+-----------+----------+------------------+
    | 6500945:16:4:2 | 6500945 | X,GAP | RECORD | test.reno1 | expires | 16 | 4 | 2 | 1032294, 'reno1' |
    | 6500940:16:4:2 | 6500940 | X | RECORD | test.reno1 | expires | 16 | 4 | 2 | 1032294, 'reno1' |
    +----------------+-------------+-----------+-----------+----------------+------------+------------+-----------+----------+------------------+

    select * from INNODB_LOCK_WAITS;
    +-------------------+-------------------+-----------------+------------------+
    | requesting_trx_id | requested_lock_id | blocking_trx_id | blocking_lock_id |
    +-------------------+-------------------+-----------------+------------------+
    | 6500945 | 6500945:16:4:2 | 6500940 | 6500940:16:4:2 |
    +-------------------+-------------------+-----------------+------------------+

    从以上的测试过程以及InnoDB状态可以看出:

    • 在REPEATABLE-READ隔离级别下, 当Session 1使用expires < '2016-08-06' LIMIT 1后, Session 2可以插入任何大于最小的expires值的记录, 但是无法插入小于最小的expires值的记录, 因为无法获得gap的意向锁(lock_mode X locks gap before rec insert intention waiting);

    这里把LIMIT去掉看看有什么现象

    Time Session 1
    (thread_id: 5690860
    tx_id: 6500454)
    Session 2
    (thread_id: 5690839
    tx_id: 6500459)
    Status
    1 SET tx_isolation='REPEATABLE-READ'; START TRANSACTION; SET tx_isolation='REPEATABLE-READ'; START TRANSACTION;
    2 SELECT * FROM reno1 WHERE expires < '2016-05-06' ORDER BY expires FOR UPDATE;
    3 INSERT INTO reno1 VALUES ('reno6', 1, '2016-08-06', 'session2'); Success
    4 INSERT INTO reno1 VALUES ('reno7', 1, '2016-02-06', 'session2'); Hang
    ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction
    5 INSERT INTO reno1 VALUES ('reno8', 1, '2016-03-12', 'session2'); Hang
    ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction
    6 INSERT INTO reno1 VALUES ('reno9', 1, '2016-04-12', 'session2'); Hang
    ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction
    7 INSERT INTO reno1 VALUES ('reno10', 1, '2016-05-12', 'session2'); Success
    8 INSERT INTO reno1 VALUES ('reno11', 1, '2016-10-06', 'session2'); Success
    9 SELECT * FROM reno1 WHERE expires < '2016-08-06' ORDER BY expires FOR UPDATE; Hang
    ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction
    10 SELECT * FROM reno1 WHERE expires < '2016-08-06' ORDER BY expires FOR UPDATE; Hang
    ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction

    从以上的测试过程以及InnoDB状态可以看出:

    • 在REPEATABLE-READ隔离级别下, 不使用LIMIT情况下, Session 1使用的是间隙锁(gap lock), Session 2是无法在expires < '2016-05-06'的间隙中插入数据的
posted @ 2016-03-06 17:14  Renolei  阅读(592)  评论(0)    收藏  举报