web-dev-qa-db-ja.com

ロック待機タイムアウトの超過を回避し、MySQL InnoDB書き込み速度を向上させる方法

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();

これは、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();

そしてこれは監査テーブルスキーマです:

  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;
2
Dio Phung

たぶん、いくつかの「マスター」APIがあり、それぞれが250-300挿入のサブセットを実行しているのでしょうか。

ACIDの場合、各transactionにはディスクへの書き込みが必要です-これが速度を制限しています。 SQLの詳細がわからないので、役立つことしか推測できません。

  • INSERTsをテーブルにバッチ処理する(単一のトランザクションで複数の行)。
  • 25のスレッドはそれぞれ、何百ものINSERTsに対して1つ(または少数)のトランザクションのみを実行します。 (これは、複数のテーブルが関係している場合は正常に機能します。)注意:タイムアウトの可能性が(劇的に)削減されますが、デッドロックの可能性が高くなります。ロールバックされるトランザクションを再生する準備をします。
  • TRIGGERsを再考してください。 APIを持っているので、個別のクエリで監査を行うことができ、それによりトランザクションを構築する際の柔軟性が高まります。
  • データのまとまりを行うには、ストアドプロシージャを検討してください。 (私は通常、アプリのコードで同等のものを実行することを好みますが、SPは問題ありません。)

その他の確認事項...メインテーブルと監査テーブルの行数は? innodb_buffer_pool_sizeの値は何ですか? RAMはどれくらいですか?

1
Rick James

根本的な原因を特定しました。それはinnodb_autoinc_lock_mode = 1です。

これが 公式ドキュメント からの要約です:

  • 0:traditional lock mode、下位互換性、パフォーマンステスト、およびセマンティクスの違いの可能性があるため、「混合モード挿入」の問題を回避するために提供されています。
  • 1:consecutive lock mode:このモードでは、「一括挿入」は特別なAUTO-INCテーブルレベルロックを使用し、ステートメントの終わりまでそれを保持します。これは、すべてのINSERT ... SELECT、REPLACE ... SELECT、およびLOAD DATAステートメントに適用されます。 AUTO-INCロックを保持するステートメントは一度に1つだけ実行できます
  • 2:interleaved lock mode:このロックモードでは、「INSERTのような」ステートメントはテーブルレベルのAUTO-INCロックを使用せず、複数のステートメントを同時に実行できます。これは最も高速でスケーラブルなロックモードですが、SQLログがバイナリログから再生されるときにステートメントベースのレプリケーションまたはリカバリシナリオを使用する場合は安全ではありません。

私たちのケースでは、複数のAPIがINSERTステートメントを呼び出したときに、1つのAPI呼び出しが長引いた場合、そのINSERTステートメントが宛先テーブルでTABLEロックを保持し、タイムアウトが発生します。

それをinnodb_autoinc_lock_mode = 2に切り替え、サーバーを再起動してください。これで、複数のAPIを呼び出して、タイムアウトなしで同じテーブルに挿入できます。

1
Dio Phung