• MySQL Lock--Index intersect导致的死锁


    涉及表结构

    CREATE TABLE `am_friend_send_link` (
      `id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT 'id',
      `task_id` int(11) NOT NULL COMMENT '任务id',
      `robot` int(11) DEFAULT NULL COMMENT '机器人logicId',
      `wechat_username` varchar(30) DEFAULT NULL COMMENT '用户微信id',
      `has_send` tinyint(1) NOT NULL DEFAULT '0' COMMENT '是否发送链接 0未发送/1已发送',
      `has_join` tinyint(1) NOT NULL DEFAULT '0' COMMENT '是否已进群 0未加入/1已加入',
      `create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT 'create_time',
      `update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT 'update_time',
      `send_type` tinyint(1) unsigned DEFAULT '0' COMMENT '发送类型 0:二维码 1:短链',
      PRIMARY KEY (`id`),
      KEY `idx_create_time` (`create_time`),
      KEY `idx_task_id` (`task_id`),
      KEY `idx_wechat_username` (`wechat_username`)
    ) ENGINE=InnoDB AUTO_INCREMENT=1137187 DEFAULT CHARSET=utf8mb4 COMMENT='加粉号-发送链接统计'
    

    涉及SQL

    update um.am_friend_send_link 
    set has_join = true 
    where task_id in (154) 
    and wechat_username = 'user0002';
    
    update um.am_friend_send_link 
    set has_join = true 
    where task_id in (154) 
    and wechat_username = 'user0001';
    

    三种执行计划(可能)

    *************************** 1. row ***************************
               id: 1
      select_type: SIMPLE
            table: am_friend_send_link
             type: index_merge
    possible_keys: idx_task_id,idx_wechat_username
              key: idx_wechat_username,idx_task_id
          key_len: 123,4
              ref: NULL
             rows: 1
            Extra: Using intersect(idx_wechat_username,idx_task_id); Using where; Using temporary
    1 row in set (0.00 sec)
    
    
    *************************** 1. row ***************************
               id: 1
      select_type: UPDATE
            table: am_friend_send_link
       partitions: NULL
             type: range
    possible_keys: idx_task_id,idx_wechat_username
              key: idx_wechat_username
          key_len: 123
              ref: const
             rows: 1
         filtered: 100.00
            Extra: Using where
    1 row in set (0.00 sec)
    
    
    *************************** 1. row ***************************
               id: 1
      select_type: UPDATE
            table: am_friend_send_link
       partitions: NULL
             type: range
    possible_keys: idx_task_id,idx_wechat_username
              key: idx_task_id
          key_len: 123
              ref: const
             rows: 1
         filtered: 100.00
            Extra: Using where
    1 row in set (0.00 sec)
    
    

    死锁信息

    LATEST DETECTED DEADLOCK
    ------------------------
    2020-06-15 23:00:28 7efcc9d29700
    *** (1) TRANSACTION:
    TRANSACTION 206352932923, ACTIVE 0.005 sec fetching rows
    mysql tables in use 3, locked 3
    LOCK WAIT 122 lock struct(s), heap size 30248, 4 row lock(s)
    LOCK BLOCKING MySQL thread id: 746657886 block 696343511
    MySQL thread id 696343511, OS thread handle 0x7efc98463700, query id 287972470324 172.16.1.176 um Searching rows for update
    update um.am_friend_send_link set has_join = true where task_id in (154) and wechat_username = 'user0002'
    *** (1) WAITING FOR THIS LOCK TO BE GRANTED:
    RECORD LOCKS space id 11663 page no 11196 n bits 272 index `PRIMARY` of table `um`.`am_friend_send_link` trx id 206352932923 lock_mode X locks rec but not gap waiting
    Record lock, heap no 169 PHYSICAL RECORD: n_fields 11; compact format; info bits 0
     0: len 4; hex 0011397e; asc   9~;;   ## id=1128830
     1: len 6; hex 00300b951d5d; asc  0   ];;
     2: len 7; hex aa000100390110; asc     9  ;;
     3: len 4; hex 8000009a; asc     ;;		## task_id=154
     4: len 4; hex 8002bfcd; asc     ;;
     5: len 11; hex 6861696c692d7a68616e67; asc user0001;;
     6: len 1; hex 80; asc  ;;
     7: len 1; hex 80; asc  ;;
     8: len 5; hex 99a69f6e92; asc    n ;;
     9: len 5; hex 99a69f6e92; asc    n ;;
     10: len 1; hex 00; asc  ;;
    
    
    *** (2) TRANSACTION:
    TRANSACTION 206352932938, ACTIVE 0.002 sec fetching rows, thread declared inside InnoDB 3894
    mysql tables in use 3, locked 3
    118 lock struct(s), heap size 30248, 3 row lock(s)
    MySQL thread id 746657886, OS thread handle 0x7efcc9d29700, query id 287972470345 172.16.1.176 um Searching rows for update
    update um.am_friend_send_link set has_join = true where task_id in (154) and wechat_username = 'user0001'
    *** (2) HOLDS THE LOCK(S):
    RECORD LOCKS space id 11663 page no 11196 n bits 272 index `PRIMARY` of table `um`.`am_friend_send_link` trx id 206352932938 lock_mode X locks rec but not gap
    Record lock, heap no 169 PHYSICAL RECORD: n_fields 11; compact format; info bits 0
     0: len 4; hex 0011397e; asc   9~;;		id=1128830
     1: len 6; hex 00300b951d5d; asc  0   ];;
     2: len 7; hex aa000100390110; asc     9  ;;
     3: len 4; hex 8000009a; asc     ;;		task_id=154
     4: len 4; hex 8002bfcd; asc     ;;
     5: len 11; hex 6861696c692d7a68616e67; asc user0001;; ## wechat_username='user0001'
     6: len 1; hex 80; asc  ;;
     7: len 1; hex 80; asc  ;;
     8: len 5; hex 99a69f6e92; asc    n ;;
     9: len 5; hex 99a69f6e92; asc    n ;;
     10: len 1; hex 00; asc  ;;
    
    *** (2) WAITING FOR THIS LOCK TO BE GRANTED:
    RECORD LOCKS space id 11663 page no 9224 n bits 552 index `idx_task_id` of table `um`.`am_friend_send_link` trx id 206352932938 lock_mode X locks rec but not gap waiting
    Record lock, heap no 481 PHYSICAL RECORD: n_fields 2; compact format; info bits 0
     0: len 4; hex 8000009a; asc     ;;	 task_id=154
     1: len 4; hex 0011397e; asc   9~;;  id=1128830
    
    *** WE ROLL BACK TRANSACTION (2)
    

    加锁顺序(猜测):

    1、事务206352932938按照wechat_username='user0001'在索引idx_wechat_username(wechat_username,id)上找到索引记录('user0001',1128830)并加锁成功。
    2、事务206352932938按照id=1128830在主键索引Primary Key(id)上找到索引记录(1128830)并加锁成功。
    3、事务206352932923按照task_id=154在索引idx_task_id(task_id,id)上找到索引记录(154,1128830)并加锁成功。
    4、事务206352932923按照id=1128830在主键索引Primary Key(id)上找到索引记录(1128830)并尝试加锁,该锁资源已被事务206352932938持有,加锁失败并等待锁资源。
    5、事务206352932938按照task_id=154在索引idx_task_id(task_id,id)上找到索引记录(154,1128830)并尝试加锁,但该锁资源已被事务206352932923持有,加锁失败并等待锁资源,触发死锁检测机制。
    6、死锁检测机制发现死锁环路,回滚事务206352932938。
    

    问题思考

    在MySQL官方文档中对intersect操作描述如下:
    The Index Merge intersection algorithm performs simultaneous scans on all used indexes and produces the intersection of row sequences that it receives from the merged index scans.
    
    intersect操作需要对所有涉及到的索引进行范围扫描,然后将有序的扫描结果求交集,如本例中对idx_task_id和idx_wechat_username分布按照task_id=154 和wechat_username='user0001' 进行范围扫描,得到的索引记录在id列上是有序的,能快速执行intersect操作,再按照intersect操作后的id值做Primary key lookup操作。intersect操作主要用于减少Primary key lookup次数。
    
    在本例中事务206352932938已经对主键索引加锁成功,即已经读取到id=1128830的数据记录,可以在数据记录上进行task_id=154的条件过来,为何还会对索引idx_task_id上的记录进行范围扫描和加锁?
    
  • 相关阅读:
    24/3=8 睡觉8工作8 8????
    linux上使用redis--宝塔面板
    Ruby--strftime
    JS-页面操作
    JS-确认框
    Rails--bundle exec rake db:migrate
    Jquery--array
    Ruby--hash
    Jquery--string
    Jquery--ajax
  • 原文地址:https://www.cnblogs.com/gaogao67/p/13152079.html
Copyright © 2020-2023  润新知