【问题标题】:MySQL: Deadlock in a single table UPDATE...WHEREMySQL:单个表中的死锁更新...在哪里
【发布时间】:2016-11-07 10:03:22
【问题描述】:

此配置引发的下一个问题:

  • MySQL 5.7.10
  • 春季 4.0.5
  • 春季批次 3.0.1
  • 10 到 20 个线程之间的 Spring ThreadPoolTask​​Executor

当某些线程尝试在单个表中执行 UPDATE...WHERE 时,问题是死锁。

表格是:

CREATE TABLE IF NOT EXISTS `invoice_events` (
  `INTERNAL_ID` bigint(20) NOT NULL,
  `FECHA_FAC` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
  `PERIOD_TYPE` varchar(50) COLLATE utf8_spanish_ci DEFAULT NULL,
  `PRODUCT_ID` varchar(50) COLLATE utf8_spanish_ci DEFAULT NULL,
  `RATE_ID` varchar(50) COLLATE utf8_spanish_ci DEFAULT NULL,
  `INVOICE_INTERNAL_ID` bigint(20) unsigned DEFAULT NULL,
  `COUNTRY_CODE` varchar(4) COLLATE utf8_spanish_ci DEFAULT NULL,
  `SOURCE_MSISDN` varchar(50) COLLATE utf8_spanish_ci DEFAULT NULL,
  `TARGET_MSISDN` varchar(100) CHARACTER SET utf8 DEFAULT NULL,
  `CATEGORY` varchar(50) COLLATE utf8_spanish_ci DEFAULT NULL,
  `SERVICE` varchar(50) COLLATE utf8_spanish_ci DEFAULT NULL,
  `USAGE_TYPE` varchar(50) CHARACTER SET utf8 DEFAULT NULL,
  `BT_COST` double(22,6) DEFAULT NULL,
  PRIMARY KEY (`INTERNAL_ID`,`FECHA_FAC`),
  KEY `IDX_INV_INT_ID` (`INVOICE_INTERNAL_ID`),
  KEY `IDX_MSISDN` (`SOURCE_MSISDN`),
  KEY `IDX_FECHA_FAC` (`FECHA_FAC`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_spanish_ci
PARTITION BY RANGE( TO_DAYS(FECHA_FAC) ) (
    PARTITION p201511 VALUES LESS THAN (TO_DAYS('2015-12-01')),
    PARTITION p201512 VALUES LESS THAN (TO_DAYS('2016-01-01')),
    PARTITION p201601 VALUES LESS THAN (TO_DAYS('2016-02-01')),
    PARTITION p201602 VALUES LESS THAN (TO_DAYS('2016-03-01')),
    PARTITION p201603 VALUES LESS THAN (TO_DAYS('2016-04-01')),
    PARTITION p201604 VALUES LESS THAN (TO_DAYS('2016-05-01')),
    PARTITION p201605 VALUES LESS THAN (TO_DAYS('2016-06-01')),
    PARTITION p201606 VALUES LESS THAN (TO_DAYS('2016-07-01')),
    PARTITION p201607 VALUES LESS THAN (TO_DAYS('2016-08-01')),
    PARTITION p201608 VALUES LESS THAN (TO_DAYS('2016-09-01')),
    PARTITION p201609 VALUES LESS THAN (TO_DAYS('2016-10-01')),
    PARTITION p201610 VALUES LESS THAN (TO_DAYS('2016-11-01')),
    PARTITION p201611 VALUES LESS THAN (TO_DAYS('2016-12-01')),
    PARTITION future VALUES LESS THAN MAXVALUE
  );

UPDATE 语句是:

update invoice_events  
set invoice_internal_id = 978202
where fecha_fac between '2016-02-01 00:00:00' and '2016-05-31 23:59:59.999' 
and source_msisdn = '239642983472' 
and invoice_internal_id is null  
and country_code = 'ES';

MySQL 对此语句的解释是:

问题的显示状态引擎是:

2016-06-03 11:08:23 0x7f8bcc1aa700
*** (1) TRANSACTION:
TRANSACTION 7031093, ACTIVE 0 sec fetching rows
mysql tables in use 1, locked 1
LOCK WAIT 406 lock struct(s), heap size 41168, 2884 row lock(s), undo log entries 375
MySQL thread id 4726, OS thread handle 140238271616768, query id 10226209 172.30.6.9 BDUSER updating
update invoice_events  set invoice_internal_id = 978173 where fecha_fac between '2016-02-01 00:00:00' and '2016-05-31 23:59:59.999' and source_msisdn in ( '239642983345' )  and invoice_internal_id is null  and country_code = 'ES'
*** (1) WAITING FOR THIS LOCK TO BE GRANTED:
RECORD LOCKS space id 12252 page no 54015 n bits 512 index IDX_MSISDN of table `my_schema`.`invoice_events` /* Partition `p201603` */ trx id 7031093 lock_mode X waiting
Record lock, heap no 101 PHYSICAL RECORD: n_fields 3; compact format; info bits 0
 0: len 16; hex 33303835303732313033323532303132; asc 3085072103252012;;
 1: len 8; hex 80000000005d2fa3; asc      ]/ ;;
 2: len 5; hex 9998fe0000; asc      ;;

*** (2) TRANSACTION:
TRANSACTION 7031094, ACTIVE 0 sec starting index read, thread declared inside InnoDB 4999
mysql tables in use 1, locked 1
424 lock struct(s), heap size 41168, 3945 row lock(s), undo log entries 628
MySQL thread id 4731, OS thread handle 140238401480448, query id 10226408 172.30.6.9 BDUSER updating
update invoice_events  set invoice_internal_id = 978202 where fecha_fac between '2016-02-01 00:00:00' and '2016-05-31 23:59:59.999' and source_msisdn in ( '239642983472' )  and invoice_internal_id is null  and country_code = 'ES'
*** (2) HOLDS THE LOCK(S):
RECORD LOCKS space id 12252 page no 54015 n bits 512 index IDX_MSISDN of table `my_schema`.`invoice_events` /* Partition `p201603` */ trx id 7031094 lock_mode X
Record lock, heap no 1 PHYSICAL RECORD: n_fields 1; compact format; info bits 0
 0: len 8; hex 73757072656d756d; asc supremum;;

从 innodb 数据库变量中,我注意到 innodb_thread_concurrency 的值为 32。

所选分区包含 21853907 行。

我试着做一个简单的 UPDATE 语句。我只使用 fecha_fac 过滤以访问所需的分区,然后使用索引字段进行过滤。

所以,我的第一个也是主要的问题是:如何修复这个事务锁?有什么建议,提示,...?

任何其他重要的问题是:

  • 来自 EXPLAIN 输出:即使在单个表更新中,类型(范围)是否会更好?还是单表更新的最佳类型?
  • 来自 EXPLAIN 输出:最终密钥是 IDX_INV_INT_ID 而不是 IDX_MSISDN 是否正确? IDX_INV_INT_ID 索引一个空列。
  • innodb_thread_concurrency 参数设置为 0(无限并发)是否有帮助?
  • SHOW STATUS 输出中是否有任何数据可以帮助我而我没有注意到?

除了我的问题,非常感谢任何帮助或建议。

提前致谢。

【问题讨论】:

  • 您现有的索引似乎并不能真正帮助此更新查询,因此 MySQL 可能需要一段时间来执行更新,并且可能必须扫描范围广泛的索引和实际数据记录,从而导致死锁。我认为您应该尝试根据 where 条件创建一个覆盖范围更广的索引。
  • @Shadow,非常感谢。你的建议对我有帮助。

标签: mysql transactions database-deadlocks database-concurrency


【解决方案1】:

正如@Shadow 建议的那样,我查看了索引,但只有 IDX_MSISDN 留下了。

我为数据库设计和避免死锁所做的其他良好做法是:

  • 对列重新排序。可能的 NULL 和/或未索引列已移至末尾。
  • 使用 PROCEDURE ANALYSE() 调整列数据类型和大小。例如,SOURCE_MSISDN 已减少,那么 key_len 也已减少。
  • 同步事务。每个 Spring Batch 项目仅执行一个事务。在我的设计中,吞吐量下降了大约 5-10%,而且是可控的。

希望对你有帮助。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-02-08
    • 1970-01-01
    • 1970-01-01
    • 2017-10-18
    • 1970-01-01
    相关资源
    最近更新 更多