如何避免锁等待超时超过并提高 MySQL InnoDB 写入速度

Dio*_*ung 5 mysql innodb performance timeout aurora performance-tuning

我运行了一个产生 25 个线程的多线程客户端来进行并发 API 调用并将数据插入到 AWS Aurora 服务器。

一段时间后,我开始看到超时错误:lock wait timeout exceeded try restarting transaction.我们对运行 MySQL 5.6.10 的服务器运行相同的测试,并没有发生锁等待超时。

有没有办法避免这种超时?

在 AWS Aurora 服务器上,SHOW ENGINE INNODB STATUS显示:

---TRANSACTION 8530565676, ACTIVE 81 sec setting auto-inc lock
mysql tables in use 2, locked 2
LOCK WAIT 6 lock struct(s), heap size 376, 2 row lock(s), undo log entries 1
MySQL thread id 405, OS thread handle 0x2ae270b03700, query id 11045 10.50.101.56 app_migration
INSERT INTO contacts_contactaudit (action,
    contact_id,
    date_created,
    date_updated,
    external_contact_id,
    entity_name,
    first_name,
    last_name,
    middle_name,
    actor_created_id,
    actor_updated_id,
    email,
    phone_number_id,
    external_contact_guid,
    external_shared_contact_id,
    active_timezone, audit_date)
SELECT 'I' as action, new.id,
    new.date_created,
    new.date_updated,
    new.external_contact_id,
    new.entity_name,
    new.first_name,
    new.last_name,
    new.middle_name,
    new.actor_created_id,
    new.actor_updated_id,
    new.email,
    new.phone_number_id,
    new.external_contact_guid,
    new.external_shared_contact_id,
    new.active_timezone, now();
Run Code Online (Sandbox Code Playgroud)

这是我们为 INSERTs 语句创建的触发器:

CREATE TRIGGER contacts_contact_insert_audit
AFTER INSERT ON contacts_contact
FOR EACH ROW
    INSERT INTO contacts_contactaudit (action,
    ...
    audit_date)
SELECT 'I' as action, new.id,
    ... 
now();
Run Code Online (Sandbox Code Playgroud)

这是审计表架构:

  CREATE TABLE `contacts_contactaudit` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `date_created` datetime(6) DEFAULT NULL,
  `date_updated` datetime(6) DEFAULT NULL,
  `action` varchar(1) NOT NULL,
  `audit_date` datetime(6) NOT NULL,
  `contact_id` int(11) DEFAULT NULL,
  `external_contact_id` bigint(20) DEFAULT NULL,
  `entity_name` varchar(128) DEFAULT NULL,
  `first_name` varchar(128) DEFAULT NULL,
  `last_name` varchar(128) DEFAULT NULL,
  `middle_name` varchar(128) DEFAULT NULL,
  `actor_created_id` int(11) DEFAULT NULL,
  `actor_updated_id` int(11) DEFAULT NULL,
  `email` varchar(256) DEFAULT NULL,
  `phone_number_id` int(11) DEFAULT NULL,
  `external_contact_guid` varchar(128) DEFAULT NULL,
  `external_shared_contact_id` bigint(20) DEFAULT NULL,
  `active_timezone` varchar(128),
  PRIMARY KEY (`id`),
  KEY `contacts_contactaud_actor_created_id_3f6f4269_fk_actors_actor_id` (`actor_created_id`),
  KEY `contacts_contactaud_actor_updated_id_2fafc937_fk_actors_actor_id` (`actor_updated_id`),
  KEY `contacts_contactaudit_contact_id_9b809fe7_uniq` (`contact_id`),
  CONSTRAINT `contacts_contactaud_actor_created_id_3f6f4269_fk_actors_actor_id` FOREIGN KEY (`actor_created_id`) REFERENCES `actors_actor` (`id`),
  CONSTRAINT `contacts_contactaud_actor_updated_id_2fafc937_fk_actors_actor_id` FOREIGN KEY (`actor_updated_id`) REFERENCES `actors_actor` (`id`)
) 
ENGINE=InnoDB 
AUTO_INCREMENT=21577 
DEFAULT CHARSET=utf8;
Run Code Online (Sandbox Code Playgroud)

Ric*_*mes 1

也许可能有一些“主”API,每个 API 都执行 250-300 次插入的某个子集?

对于 ACID,每个事务都需要一次磁盘写入——这就是限制速度的原因。不知道你的 SQL 细节,我只能猜测一些有帮助的事情:

  • 批处理INSERTs到表中(单个事务中的多行)。
  • 25 个线程中的每个线程仅对数百个INSERTs. (如果涉及多个表,这可以正常工作。) 注意:虽然它(大大)减少了超时的可能性,但它确实增加了死锁的可能性。准备好重放回滚的事务。
  • 重新思考TRIGGERs. 由于您有一个 API,它可以在单独的查询中进行审核,从而在构建事务时提供更大的灵活性。
  • 考虑使用存储过程来处理数据块。(我通常更喜欢在应用程序代码中执行相同的操作,但 SP 也很好。)

其他一些需要检查的事情...主表和审计表中有多少行?的价值是什么innodb_buffer_pool_size?多少内存?