MySQL:delete…where..in()対delete..from..join、およびサブセレクトを使用した削除時にロックされたテーブル


9

免責事項:データベースの内部に関する知識が不足していることをお許しください。ここに行く:

データベースの定期的なクリーンアップジョブで大きなパフォーマンスの問題があるアプリケーション(私たちが作成したものではありません)を実行します。クエリは次のようになります。

delete from VARIABLE_SUBSTITUTION where BUILDRESULTSUMMARY_ID in (
       select BUILDRESULTSUMMARY_ID from BUILDRESULTSUMMARY
       where BUILDRESULTSUMMARY.BUILD_KEY = "BAM-1");

わかりやすく、読みやすく、標準SQL。しかし、残念ながら非常に遅いです。クエリを説明すると、既存のインデックスVARIABLE_SUBSTITUTION.BUILDRESULTSUMMARY_IDが使用されていないことがわかります。

mysql> explain delete from VARIABLE_SUBSTITUTION where BUILDRESULTSUMMARY_ID in (
    ->        select BUILDRESULTSUMMARY_ID from BUILDRESULTSUMMARY
    ->        where BUILDRESULTSUMMARY.BUILD_KEY = "BAM-1");
| id | select_type        | table                 | type            | possible_keys                    | key     | key_len | ref  | rows    | Extra       |
+----+--------------------+-----------------------+-----------------+----------------------------------+---------+---------+------+---------+-------------+
|  1 | PRIMARY            | VARIABLE_SUBSTITUTION | ALL             | NULL                             | NULL    | NULL    | NULL | 7300039 | Using where |
|  2 | DEPENDENT SUBQUERY | BUILDRESULTSUMMARY    | unique_subquery | PRIMARY,key_number_results_index | PRIMARY | 8       | func |       1 | Using where |

これにより、非常に遅くなります(120秒以上)。それに加えて、それが挿入しようとするブロッククエリに思えるBUILDRESULTSUMMARYからの出力、show engine innodb status

---TRANSACTION 68603695, ACTIVE 157 sec starting index read
mysql tables in use 1, locked 1
LOCK WAIT 2 lock struct(s), heap size 360, 1 row lock(s)
MySQL thread id 127964, OS thread handle 0x7facd0670700, query id 956555826 localhost 127.0.0.1 bamboosrv updating
update BUILDRESULTSUMMARY set CREATED_DATE='2015-06-18 09:22:05', UPDATED_DATE='2015-06-18 09:22:32', BUILD_KEY='BLA-RELEASE1-JOB1', BUILD_NUMBER=8, BUILD_STATE='Unknown', LIFE_CYCLE_STATE='InProgress', BUILD_DATE='2015-06-18 09:22:31.792', BUILD_CANCELLED_DATE=null, BUILD_COMPLETED_DATE='2015-06-18 09:52:02.483', DURATION=1770691, PROCESSING_DURATION=1770691, TIME_TO_FIX=null, TRIGGER_REASON='com.atlassian.bamboo.plugin.system.triggerReason:CodeChangedTriggerReason', DELTA_STATE=null, BUILD_AGENT_ID=199688199, STAGERESULT_ID=230943366, RESTART_COUNT=0, QUEUE_TIME='2015-06-18 09:22:04.52
------- TRX HAS BEEN WAITING 157 SEC FOR THIS LOCK TO BE GRANTED:
RECORD LOCKS space id 38 page no 30140 n bits 112 index `PRIMARY` of table `bamboong`.`BUILDRESULTSUMMARY` trx id 68603695 lock_mode X locks rec but not gap waiting
------------------
---TRANSACTION 68594818, ACTIVE 378 sec starting index read
mysql tables in use 2, locked 2
646590 lock struct(s), heap size 63993384, 3775190 row lock(s), undo log entries 117
MySQL thread id 127845, OS thread handle 0x7facc6bf8700, query id 956652201 localhost 127.0.0.1 bamboosrv preparing
delete from VARIABLE_SUBSTITUTION  where BUILDRESULTSUMMARY_ID in   (select BUILDRESULTSUMMARY_ID from BUILDRESULTSUMMARY where BUILDRESULTSUMMARY.BUILD_KEY = 'BLA-BLUBB10-SON')

これにより、システムの速度が低下し、増加を余儀なくされましたinnodb_lock_wait_timeout

MySQLを実行しながら、「結合から削除」を使用するように削除クエリを書き直しました。

delete VARIABLE_SUBSTITUTION from VARIABLE_SUBSTITUTION join BUILDRESULTSUMMARY
   on VARIABLE_SUBSTITUTION.BUILDRESULTSUMMARY_ID = BUILDRESULTSUMMARY.BUILDRESULTSUMMARY_ID
   where BUILDRESULTSUMMARY.BUILD_KEY = "BAM-1";

これは読みやすさはやや劣りますが、残念ながら標準SQLはありませんが(私が知る限り)、インデックスを使用しているため、はるかに高速(0.02秒程度)です。

mysql> explain delete VARIABLE_SUBSTITUTION from VARIABLE_SUBSTITUTION join BUILDRESULTSUMMARY
    ->    on VARIABLE_SUBSTITUTION.BUILDRESULTSUMMARY_ID = BUILDRESULTSUMMARY.BUILDRESULTSUMMARY_ID
    ->    where BUILDRESULTSUMMARY.BUILD_KEY = "BAM-1";
| id | select_type | table                 | type | possible_keys                    | key                      | key_len | ref                                                    | rows | Extra                    |
+----+-------------+-----------------------+------+----------------------------------+--------------------------+---------+--------------------------------------------------------+------+--------------------------+
|  1 | SIMPLE      | BUILDRESULTSUMMARY    | ref  | PRIMARY,key_number_results_index | key_number_results_index | 768     | const                                                  |    1 | Using where; Using index |
|  1 | SIMPLE      | VARIABLE_SUBSTITUTION | ref  | var_subst_result_idx             | var_subst_result_idx     | 8       | bamboo_latest.BUILDRESULTSUMMARY.BUILDRESULTSUMMARY_ID |   26 | NULL                     |

追加情報:

mysql> SHOW CREATE TABLE VARIABLE_SUBSTITUTION;
| Table                 | Create Table |
| VARIABLE_SUBSTITUTION | CREATE TABLE `VARIABLE_SUBSTITUTION` (
  `VARIABLE_SUBSTITUTION_ID` bigint(20) NOT NULL,
  `VARIABLE_KEY` varchar(255) COLLATE utf8_bin NOT NULL,
  `VARIABLE_VALUE` varchar(4000) COLLATE utf8_bin DEFAULT NULL,
  `VARIABLE_TYPE` varchar(255) COLLATE utf8_bin DEFAULT NULL,
  `BUILDRESULTSUMMARY_ID` bigint(20) NOT NULL,
  PRIMARY KEY (`VARIABLE_SUBSTITUTION_ID`),
  KEY `var_subst_result_idx` (`BUILDRESULTSUMMARY_ID`),
  KEY `var_subst_type_idx` (`VARIABLE_TYPE`),
  CONSTRAINT `FK684A7BE0A958B29F` FOREIGN KEY (`BUILDRESULTSUMMARY_ID`) REFERENCES `BUILDRESULTSUMMARY` (`BUILDRESULTSUMMARY_ID`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin |

mysql> SHOW CREATE TABLE BUILDRESULTSUMMARY;
| Table              | Create Table |
| BUILDRESULTSUMMARY | CREATE TABLE `BUILDRESULTSUMMARY` (
  `BUILDRESULTSUMMARY_ID` bigint(20) NOT NULL,
....
  `SKIPPED_TEST_COUNT` int(11) DEFAULT NULL,
  PRIMARY KEY (`BUILDRESULTSUMMARY_ID`),
  KEY `FK26506D3B9E6537B` (`CHAIN_RESULT`),
  KEY `FK26506D3BCCACF65` (`MERGERESULT_ID`),
  KEY `key_number_delta_state` (`DELTA_STATE`),
  KEY `brs_build_state_idx` (`BUILD_STATE`),
  KEY `brs_life_cycle_state_idx` (`LIFE_CYCLE_STATE`),
  KEY `brs_deletion_idx` (`MARKED_FOR_DELETION`),
  KEY `brs_stage_result_id_idx` (`STAGERESULT_ID`),
  KEY `key_number_results_index` (`BUILD_KEY`,`BUILD_NUMBER`),
  KEY `brs_agent_idx` (`BUILD_AGENT_ID`),
  KEY `rs_ctx_baseline_idx` (`VARIABLE_CONTEXT_BASELINE_ID`),
  KEY `brs_chain_result_summary_idx` (`CHAIN_RESULT`),
  KEY `brs_log_size_idx` (`LOG_SIZE`),
  CONSTRAINT `FK26506D3B9E6537B` FOREIGN KEY (`CHAIN_RESULT`) REFERENCES `BUILDRESULTSUMMARY` (`BUILDRESULTSUMMARY_ID`),
  CONSTRAINT `FK26506D3BCCACF65` FOREIGN KEY (`MERGERESULT_ID`) REFERENCES `MERGE_RESULT` (`MERGERESULT_ID`),
  CONSTRAINT `FK26506D3BCEDEEF5F` FOREIGN KEY (`STAGERESULT_ID`) REFERENCES `CHAIN_STAGE_RESULT` (`STAGERESULT_ID`),
  CONSTRAINT `FK26506D3BE3B5B062` FOREIGN KEY (`VARIABLE_CONTEXT_BASELINE_ID`) REFERENCES `VARIABLE_CONTEXT_BASELINE` (`VARIABLE_CONTEXT_BASELINE_ID`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin |

(いくつかのものは省略されています、それはかなり広い表です)。

だから私はこれについていくつか質問があります:

  • クエリオプティマイザがサブクエリバージョンのときにインデックスを使用して削除できないのはなぜですか?
  • それをだましてインデックスを使用させる方法(理想的には標準に準拠)はありますか?または
  • を書くためのポータブルな方法はありdelete from joinますか?アプリケーションは、jdbcおよびHibernateを介して使用されるPostgreSQL、MySQL、Oracle、およびMicrosoft SQL Serverをサポートします。
  • 副選択でのみ使用されるVARIABLE_SUBSTITUTIONへの挿入のブロックからの削除はなぜBUILDRESULTSUMMARYですか?

Perconaサーバー5.6.24-72.2-1.jessieまたは5.6.24-72.2-1.wheezy(テストシステム上)。
0x89

はい、データベース全体がinnodbを使用しています。
0x89

したがって、5.6はオプティマイザの改善についてあまり注目されていないようです。5.7まで待つ必要があります(可能な場合はMariaDBを試してください。オプティマイザの改善は5.3と5.5バージョンで行われました。)
ypercubeᵀᴹ2015年

@ypercube AFAIK no forkには、削除サブクエリを最適化するための拡張機能も5.7もありません。削除はSELECTステートメントとは異なる方法で最適化されます。
Morgan

回答:


7
  • クエリオプティマイザがサブクエリバージョンのときにインデックスを使用して削除できないのはなぜですか?

オプティマイザはその点で少しばかげているからです。以下のためだけではなくDELETEUPDATEなく、ためSELECTだけでなく、文、のようなものはWHERE column IN (SELECT ...)完全に最適化されていませんでした。通常、実行プランには、外部テーブルのすべての行(VARIABLE_SUBSTITUTIONこの場合)のサブクエリの実行が含まれていました。そのテーブルが小さい場合は、すべて問題ありません。大きければ望みはありません。さらに古いバージョンでは、INサブクエリを持つINサブクエリはEXPLAIN、年齢を問わず実行することもできます。

あなたができること-このクエリを保持したい場合-いくつかの最適化を実装した最新バージョンを使用して、もう一度テストすることです。最新バージョンの意味:MySQL 5.6(およびベータ版がリリースされると5.7)およびMariaDB 5.5 / 10.0

(更新)あなたは既に最適化の改善を持っている5.6を使用しており、この1は関連性がある:半結合の変換と最適化サブクエリ
私は上のインデックスの追加提案(BUILD_KEY)を単独で。複合的なものがありますが、このクエリにはあまり役に立ちません。

  • それをだましてインデックスを使用させる方法(理想的には標準に準拠)はありますか?

何も考えられない。私の意見では、標準SQLを使用する価値はあまりありません。各DBMSには非常に多くの相違点とマイナーな癖があります(UPDATEそしてDELETEステートメントはそのような相違点の良い例です)。

  • 結合から削除を書き込むポータブルな方法はありますか?アプリケーションは、jdbcおよびHibernateを介して使用されるPostgreSQL、MySQL、Oracle、およびMicrosoft SQL Serverをサポートします。

前の質問と同じ答え。

  • VARIABLE_SUBSTITUTIONからの削除が、副選択でのみ使用されるBUILDRESULTSUMMARYへの挿入をブロックするのはなぜですか?

100%確実ではありませんが、サブクエリを複数回実行することと、テーブルに対してどのような種類のロックがかかっているかに関係していると思います。


(削除トランザクションの)innodb_statusからの「3775190行ロック」が強く示唆されます。しかし、また、私にはあまりにも良い見ていない「使用中2 MySQLのテーブルは、2ロック」..
0x89

2

ここにあなたの二つの質問に対する答えがあります

  • 行ごとにwhere句が変更されるため、オプティマイザはインデックスを使用できません。deleteステートメントは、オプティマイザを通過した後、次のようになります。

    delete from VARIABLE_SUBSTITUTION where EXISTS (
    select BUILDRESULTSUMMARY_ID from BUILDRESULTSUMMARY
    where BUILDRESULTSUMMARY.BUILD_KEY = BUILDRESULTSUMMARY_ID AND BUILDRESULTSUMMARY.BUILD_KEY = "BAM-1");

ただし、結合を実行すると、サーバーは削除対象の行を識別できます。

  • トリックは、変数を使用してを保持しBUILDRESULTSUMMARY_ID、クエリの代わりに変数を使用することです。変数の初期化と削除クエリの両方がセッション内で実行される必要があることに注意してください。このようなもの。

    SET @ids = (SELECT GROUP_CONCAT(BUILDRESULTSUMMARY_ID) 
            from BUILDRESULTSUMMARY where BUILDRESULTSUMMARY.BUILD_KEY = "BAM-1" ); 
    delete from VARIABLE_SUBSTITUTION where FIND_IN_SET(BUILDRESULTSUMMARY_ID,@ids) > 0;

    クエリが返すIDが多すぎて、これが標準的な方法ではない場合、この問題に直面する可能性があります。これは単なる回避策です。

    そして、私はあなたの他の2つの質問に対する答えを持っていません:)


さて、あなたは私のポイントを逃しました。あなたが考慮しなかったのは、VARIABLE_SUBSTITUTIONとBUILDRESULTSUMMARYの両方にBUILDRESULTSUMMARY_IDという名前の列があることです。したがって、次のようになります。 -1 "); '。その後、それは理にかなっており、両方のクエリは同じことを行います。
0x89

1
ええ、私は外側のテーブルへの参照が欠けているだけです。しかし、それは重要ではありません。これは、オプティマイザでそれがどのように扱われるかを示す単なる例です。
Masoud 2015年

オプティマイザが同等のクエリを生成するというわずかな違いがあります。
0x89
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.