索引合并导致锁超时

本文示例数据表版本:mysql8.0.32。隔离级别:读已提交。

什么是索引合并?

索引合并指将多个索引的扫描结果合并起来,作为最终的扫描结果。 下面举例说明:

初始化表:

CREATE TABLE `t8` (
  `id` int unsigned NOT NULL AUTO_INCREMENT,
  `a` int NOT NULL DEFAULT '0',
  `b` int NOT NULL DEFAULT '0',
  `c` int NOT NULL DEFAULT '0',
  PRIMARY KEY (`id`),
  KEY `idx_a` (`a`),
  KEY `idx_b` (`b`)
ENGINE=InnoDB AUTO_INCREMENT=10 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci

初始化数据:

INSERT INTO `rclx`.`t8`(`id``a``b``c`VALUES (1111);
INSERT INTO `rclx`.`t8`(`id``a``b``c`VALUES (2121);
INSERT INTO `rclx`.`t8`(`id``a``b``c`VALUES (3131);
INSERT INTO `rclx`.`t8`(`id``a``b``c`VALUES (4212);
INSERT INTO `rclx`.`t8`(`id``a``b``c`VALUES (5223);
INSERT INTO `rclx`.`t8`(`id``a``b``c`VALUES (6234);
INSERT INTO `rclx`.`t8`(`id``a``b``c`VALUES (7311);
INSERT INTO `rclx`.`t8`(`id``a``b``c`VALUES (8322);
INSERT INTO `rclx`.`t8`(`id``a``b``c`VALUES (9333);

执行如下语句:

explain update t8 set c=11 where a = 1 and b=2\G;

结果如下:

*************************** 1. row ***************************
           id: 1
  select_type: UPDATE
        table: t8
   partitions: NULL
         type: index_merge
possible_keys: idx_a,idx_b
          key: idx_a,idx_b
      key_len: 4,4
          ref: NULL
         rows: 1
     filtered: 100.00
        Extra: Using intersect(idx_a,idx_b); Using where
1 row in set, 1 warning (0.00 sec)

可以看到其同时使用了index_a,index_b两个索引。

索引合并导致死锁示例

当update语句使用到索引合并时,有可能会导致锁超时,甚至死锁。下面举个简单例子。 表结构及数据:如上。

事务1:

begin;
update t8 set c=11 where a = 1 and b=2;

事务2:

begin;
update t8 set c=12 where b=2 and a=3;

从表面上看,这两个事务并没有冲突,但实际上会发生阻塞。

下面来查看一下相关锁信息。

select * from performance_schema.data_locks\G;

结果如下:

*************************** 1. row ***************************
               ENGINE: INNODB
       ENGINE_LOCK_ID: 4859925960:1246:4454376392
ENGINE_TRANSACTION_ID: 1023931
            THREAD_ID: 55
             EVENT_ID: 59
        OBJECT_SCHEMA: rclx
          OBJECT_NAME: t8
       PARTITION_NAME: NULL
    SUBPARTITION_NAME: NULL
           INDEX_NAME: NULL
OBJECT_INSTANCE_BEGIN: 4454376392
            LOCK_TYPE: TABLE
            LOCK_MODE: IX
          LOCK_STATUS: GRANTED
            LOCK_DATA: NULL
*************************** 2. row ***************************
               ENGINE: INNODB
       ENGINE_LOCK_ID: 4859925960:90:5:8:5369132056
ENGINE_TRANSACTION_ID: 1023931
            THREAD_ID: 55
             EVENT_ID: 59
        OBJECT_SCHEMA: rclx
          OBJECT_NAME: t8
       PARTITION_NAME: NULL
    SUBPARTITION_NAME: NULL
           INDEX_NAME: idx_a
OBJECT_INSTANCE_BEGIN: 5369132056
            LOCK_TYPE: RECORD
            LOCK_MODE: X,REC_NOT_GAP
          LOCK_STATUS: GRANTED
            LOCK_DATA: 3, 7
*************************** 3. row ***************************
               ENGINE: INNODB
       ENGINE_LOCK_ID: 4859925960:90:4:8:5369132400
ENGINE_TRANSACTION_ID: 1023931
            THREAD_ID: 55
             EVENT_ID: 59
        OBJECT_SCHEMA: rclx
          OBJECT_NAME: t8
       PARTITION_NAME: NULL
    SUBPARTITION_NAME: NULL
           INDEX_NAME: PRIMARY
OBJECT_INSTANCE_BEGIN: 5369132400
            LOCK_TYPE: RECORD
            LOCK_MODE: X,REC_NOT_GAP
          LOCK_STATUS: GRANTED
            LOCK_DATA: 7
*************************** 4. row ***************************
               ENGINE: INNODB
       ENGINE_LOCK_ID: 4859925960:90:6:3:5369133088
ENGINE_TRANSACTION_ID: 1023931
            THREAD_ID: 55
             EVENT_ID: 60
        OBJECT_SCHEMA: rclx
          OBJECT_NAME: t8
       PARTITION_NAME: NULL
    SUBPARTITION_NAME: NULL
           INDEX_NAME: idx_b
OBJECT_INSTANCE_BEGIN: 5369133088
            LOCK_TYPE: RECORD
            LOCK_MODE: X,REC_NOT_GAP
          LOCK_STATUS: WAITING
            LOCK_DATA: 2, 2
*************************** 5. row ***************************
               ENGINE: INNODB
       ENGINE_LOCK_ID: 4859923584:1246:4454373320
ENGINE_TRANSACTION_ID: 1023930
            THREAD_ID: 49
             EVENT_ID: 86
        OBJECT_SCHEMA: rclx
          OBJECT_NAME: t8
       PARTITION_NAME: NULL
    SUBPARTITION_NAME: NULL
           INDEX_NAME: NULL
OBJECT_INSTANCE_BEGIN: 4454373320
            LOCK_TYPE: TABLE
            LOCK_MODE: IX
          LOCK_STATUS: GRANTED
            LOCK_DATA: NULL
*************************** 6. row ***************************
               ENGINE: INNODB
       ENGINE_LOCK_ID: 4859923584:90:5:3:5369114136
ENGINE_TRANSACTION_ID: 1023930
            THREAD_ID: 49
             EVENT_ID: 86
        OBJECT_SCHEMA: rclx
          OBJECT_NAME: t8
       PARTITION_NAME: NULL
    SUBPARTITION_NAME: NULL
           INDEX_NAME: idx_a
OBJECT_INSTANCE_BEGIN: 5369114136
            LOCK_TYPE: RECORD
            LOCK_MODE: X,REC_NOT_GAP
          LOCK_STATUS: GRANTED
            LOCK_DATA: 1, 2
*************************** 7. row ***************************
               ENGINE: INNODB
       ENGINE_LOCK_ID: 4859923584:90:4:3:5369114480
ENGINE_TRANSACTION_ID: 1023930
            THREAD_ID: 49
             EVENT_ID: 86
        OBJECT_SCHEMA: rclx
          OBJECT_NAME: t8
       PARTITION_NAME: NULL
    SUBPARTITION_NAME: NULL
           INDEX_NAME: PRIMARY
OBJECT_INSTANCE_BEGIN: 5369114480
            LOCK_TYPE: RECORD
            LOCK_MODE: X,REC_NOT_GAP
          LOCK_STATUS: GRANTED
            LOCK_DATA: 2
*************************** 8. row ***************************
               ENGINE: INNODB
       ENGINE_LOCK_ID: 4859923584:90:6:3:5369114824
ENGINE_TRANSACTION_ID: 1023930
            THREAD_ID: 49
             EVENT_ID: 86
        OBJECT_SCHEMA: rclx
          OBJECT_NAME: t8
       PARTITION_NAME: NULL
    SUBPARTITION_NAME: NULL
           INDEX_NAME: idx_b
OBJECT_INSTANCE_BEGIN: 5369114824
            LOCK_TYPE: RECORD
            LOCK_MODE: X,REC_NOT_GAP
          LOCK_STATUS: GRANTED
            LOCK_DATA: 2, 2
8 rows in set (0.00 sec)

我们可以看到,事务2(3931)在等待index_b上的锁。

总结

虽然索引合并在某些情况下可以提高查询效率,但是在实际使用中,也会带来一些问题,比如死锁、锁等待超时等。所以,还是建议把update语句 where条件涉及到的字段都放到一个联合索引中,这样可以避免索引合并带来的问题。

本文由 mdnice 多平台发布

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值