简单删除查询上的 Mysql/innodb 死锁

Tri*_*ids 5 mysql innodb deadlock

Mysql/innodb 8.0.16,读取已提交的事务,事务中的几个语句在删除非拦截行时引发死锁。试图了解正在发生的事情:

------------------------
LATEST DETECTED DEADLOCK
------------------------
2019-05-14 21:57:44 0x7fe9546c6700
*** (1) TRANSACTION:
TRANSACTION 2852, ACTIVE 0 sec fetching rows
mysql tables in use 1, locked 1
LOCK WAIT 8 lock struct(s), heap size 1136, 14 row lock(s), undo log entries 25
MySQL thread id 146, OS thread handle 140640122267392, query id 1586 localhost 127.0.0.1 oc5z updating
DELETE FROM deal_product_rows_tmp WHERE batch_no=7533
*** (1) WAITING FOR THIS LOCK TO BE GRANTED:
RECORD LOCKS space id 11 page no 4 n bits 88 index PRIMARY of table `db1`.`deal_product_rows_tmp` trx id 2852 lock_mode X locks rec but not gap waiting
Record lock, heap no 14 PHYSICAL RECORD: n_fields 11; compact format; info bits 0
 0: len 4; hex 8000650e; asc   e ;;
 1: len 6; hex 000000000b25; asc      %;;
 2: len 7; hex 81000001340110; asc     4  ;;
 3: len 4; hex 80001d6e; asc    n;;
 4: len 4; hex 8010a626; asc    &;;
 5: len 4; hex 8000253a; asc   %:;;
 6: len 4; hex 8000986e; asc    n;;
 7: len 4; hex 80000002; asc     ;;
 8: len 30; hex 415254455820d184d0bed180d0bcd18b20d0bfd180d18fd0bcd0bed183d0; asc WRX                         ; (total 81 bytes);
 9: len 8; hex 0000000000208c40; asc        @;;
 10: len 1; hex 4d; asc M;;

*** (2) TRANSACTION:
TRANSACTION 2853, ACTIVE 0 sec starting index read
mysql tables in use 1, locked 1
8 lock struct(s), heap size 1136, 3 row lock(s), undo log entries 12
MySQL thread id 147, OS thread handle 140640120497920, query id 1594 localhost 127.0.0.1 oc5z updating
DELETE FROM deal_product_rows_tmp WHERE batch_no=7534
*** (2) HOLDS THE LOCK(S):
RECORD LOCKS space id 11 page no 4 n bits 88 index PRIMARY of table `db1`.`deal_product_rows_tmp` trx id 2853 lock_mode X locks rec but not gap
Record lock, heap no 14 PHYSICAL RECORD: n_fields 11; compact format; info bits 0
 0: len 4; hex 8000650e; asc   e ;;
 1: len 6; hex 000000000b25; asc      %;;
 2: len 7; hex 81000001340110; asc     4  ;;
 3: len 4; hex 80001d6e; asc    n;;
 4: len 4; hex 8010a626; asc    &;;
 5: len 4; hex 8000253a; asc   %:;;
 6: len 4; hex 8000986e; asc    n;;
 7: len 4; hex 80000002; asc     ;;
 8: len 30; hex 415254455820d184d0bed180d0bcd18b20d0bfd180d18fd0bcd0bed183d0; asc WRX                         ; (total 81 bytes);
 9: len 8; hex 0000000000208c40; asc        @;;
 10: len 1; hex 4d; asc M;;

*** (2) WAITING FOR THIS LOCK TO BE GRANTED:
RECORD LOCKS space id 11 page no 4 n bits 96 index PRIMARY of table `db1`.`deal_product_rows_tmp` trx id 2853 lock_mode X locks rec but not gap waiting
Record lock, heap no 2 PHYSICAL RECORD: n_fields 11; compact format; info bits 32
 0: len 4; hex 80006502; asc   e ;;
 1: len 6; hex 000000000b24; asc      $;;
 2: len 7; hex 0200000128012d; asc     ( -;;
 3: len 4; hex 80001d6d; asc    m;;
 4: len 4; hex 80109d40; asc    @;;
 5: len 4; hex 800023ba; asc   # ;;
 6: len 4; hex 800098e6; asc     ;;
 7: len 4; hex 80000007; asc     ;;
 8: len 9; hex 466f696c20676c7565; asc Foil glue;;
 9: len 8; hex 0000000000005940; asc       Y@;;
 10: len 1; hex 4d; asc M;;

*** WE ROLL BACK TRANSACTION (2)
Run Code Online (Sandbox Code Playgroud)

表结构:

    CREATE TABLE `deal_product_rows_tmp` (
    `batch_no` int(11) NOT NULL,
    `bitrix_id` int(11) NOT NULL,
    `deal_id` int(11) NOT NULL,
    `product_id` int(11) NOT NULL,
    `quantity` int(11) NOT NULL,
    `product_name` varchar(1000) NOT NULL,
    `price` double DEFAULT NULL,
    `status` varchar(50) NOT NULL,
    `tmp_id` int(11) NOT NULL AUTO_INCREMENT,
    PRIMARY KEY (`tmp_id`),
    KEY `idx_deal_row_tmp_deal` (`deal_id`),
    KEY `idx_deal_row_tmp_batchno` (`batch_no`)
    ) ENGINE=InnoDB AUTO_INCREMENT=7500 DEFAULT CHARSET=utf8 
Run Code Online (Sandbox Code Playgroud)

1)查询删除分隔行,如何在同一行上记录锁定?

2) 即使事务在同一行上加锁,为什么会死锁?为什么第一个事务不能等到第二个释放锁?

UPDATE1:应用程序逻辑 - Spring Boot 应用程序接收来自 crm 系统的休息调用,获取客户订单(交易)产品行并尝试在 micro dwh 中同步数据。RC事务中有多个语句,本应用中只实现了一种事务。第一条语句 REPLACE INTO deal table,这应该可以防止并行处理相同 deal_id 上的批次(目前未使用 FK 密钥)。接下来,在“tmp”表中插入接收到的产品行并在目标“dwh”表上进行比较的事务。插入丢失的交易产品,更新“已删除”产品行的状态。下一步从 deal_product_rows_tmp(死锁点)和最后一步中删除“tmp”数据 - 将批次标记为已完成(更新批次集状态=.. 表,其中 batch_no=? )

jyn*_*nus 5

通常人们对间隙锁定和额外锁定感到困惑,因为整个表上的索引错误或记录比预期的多,但这不是你的情况:

  • 你有合适的二级索引
  • 锁声明没有间隙锁定是必要的(如预期的那样)
  • 锁定/通缉的记录不同
  • 除非我删除idx_deal_row_tmp_batchno索引,否则我无法重现该问题
  • 记录在同一页上,但理论上它们不应该相互影响

根据有限的信息,我能猜到的是:

  • 您正在批量删除多条记录(因为一个持有 3 个行锁,另一个持有 14 个)
  • 您遇到了竞争条件,其中事务 1 能够锁定batch_no=7533记录,而事务 2 能够锁定batch_no=7534,但是他们还想在单独的查询(但相同的事务)上更新其他记录
  • 检测到依赖项并且 InnoDB 杀死最新的依赖项以防止无限循环(死锁)

他们不能只是等待,否则将是无限等待(因为它们相互依赖)。如果不是循环,第二个事务确实会等待innodb_lock_wait_timeout几秒钟。

您可以采取多种策略:

  • 监控死锁,如果它们不常见,就什么都不做(在应用程序上重试)。只要错误不经常发生,它们就不会是一个巨大的惩罚
  • 更改更新策略以防止冲突(例如,在单个更大的批次中删除 - 尽管这可能会增加延迟 -,每个事务删除一行,为删除设置应用程序坐标等。
  • 使用新的 8.0 功能SKIP LOCKEDNOWAIT忽略立即锁定的行,特别是批处理:https : //mysqlserverteam.com/mysql-8-0-1-using-skip-locked-and-nowait-to-handle-hot-行/