Haftungsausschluss: Bitte entschuldigen Sie mein mangelndes Wissen über Datenbank-Interna. Hier kommt's:
Wir führen eine Anwendung aus (nicht von uns geschrieben), die bei einem regelmäßigen Bereinigungsjob in der Datenbank ein großes Leistungsproblem aufweist. Die Abfrage sieht folgendermaßen aus:
delete from VARIABLE_SUBSTITUTION where BUILDRESULTSUMMARY_ID in (
select BUILDRESULTSUMMARY_ID from BUILDRESULTSUMMARY
where BUILDRESULTSUMMARY.BUILD_KEY = "BAM-1");
Einfach, einfach zu lesen und Standard-SQL. Aber leider sehr langsam. Das Erläutern der Abfrage zeigt, dass der vorhandene Index für VARIABLE_SUBSTITUTION.BUILDRESULTSUMMARY_ID
nicht verwendet wird:
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 |
Dies macht es sehr langsam (120 Sekunden und mehr). Hinzu kommt , dass, so scheint es zu Block Abfragen , die in einzufügen versuchen BUILDRESULTSUMMARY
aus, Ausgabe 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')
Dies verlangsamt das System und zwingt uns zu erhöhen innodb_lock_wait_timeout
.
Während wir MySQL ausführen, haben wir die Löschabfrage neu geschrieben, um "Aus Join löschen" zu verwenden:
delete VARIABLE_SUBSTITUTION from VARIABLE_SUBSTITUTION join BUILDRESULTSUMMARY
on VARIABLE_SUBSTITUTION.BUILDRESULTSUMMARY_ID = BUILDRESULTSUMMARY.BUILDRESULTSUMMARY_ID
where BUILDRESULTSUMMARY.BUILD_KEY = "BAM-1";
Dies ist etwas weniger einfach zu lesen, leider kein Standard-SQL (soweit ich es herausfinden konnte), aber viel schneller (0,02 Sekunden oder so), da es den Index verwendet:
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 |
Zusätzliche Information:
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 |
(einige Sachen weggelassen, es ist ein ziemlich breiter Tisch).
Ich habe also ein paar Fragen dazu:
- Warum kann das Abfrageoptimierungsprogramm den Index nicht zum Löschen der Unterabfrageversion verwenden, während die Join-Version verwendet wird?
- Gibt es eine (im Idealfall standardkonforme) Möglichkeit, den Index zu verwenden? oder
- Gibt es eine tragbare Möglichkeit, eine zu schreiben
delete from join
? Die Anwendung unterstützt PostgreSQL, MySQL, Oracle und Microsoft SQL Server, die über jdbc und Hibernate verwendet werden. - Warum wird das Löschen aus
VARIABLE_SUBSTITUTION
blockierenden Einfügungen inBUILDRESULTSUMMARY
, das nur in der Unterauswahl verwendet wird?
Antworten:
Weil der Optimierer in dieser Hinsicht etwas dumm ist / war. Nicht nur für
DELETE
undUPDATE
sondern auch fürSELECT
Aussagen wurde so etwasWHERE column IN (SELECT ...)
nicht vollständig optimiert. Der Ausführungsplan umfasste normalerweise das Ausführen der Unterabfrage für jede Zeile der externen Tabelle (VARIABLE_SUBSTITUTION
in diesem Fall). Wenn dieser Tisch klein ist, ist alles in Ordnung. Wenn es groß ist, keine Hoffnung. In noch älteren Versionen würde eineIN
Unterabfrage mit einerIN
Unterunterabfrage sogar dasEXPLAIN
für Ewigkeiten laufen lassen.Wenn Sie diese Abfrage beibehalten möchten, können Sie die neuesten Versionen verwenden, die mehrere Optimierungen implementiert haben, und erneut testen. Neueste Versionen bedeuten: MySQL 5.6 (und 5.7, wenn es aus der Beta kommt) und MariaDB 5.5 / 10.0
(Update) Sie verwenden bereits 5.6 mit Optimierungsverbesserungen, und diese ist relevant: Optimieren von Unterabfragen mit Semi-Join-Transformationen
Ich empfehle, nur einen Index hinzuzufügen
(BUILD_KEY)
. Es gibt eine zusammengesetzte, aber das ist für diese Abfrage nicht sehr nützlich.Keine, an die ich denken kann. Meiner Meinung nach lohnt es sich nicht, Standard-SQL zu verwenden. Es gibt so viele Unterschiede und kleinere Macken, die jedes DBMS aufweist (
UPDATE
undDELETE
Anweisungen sind gute Beispiele für solche Unterschiede), dass das Ergebnis eine sehr begrenzte Teilmenge von SQL ist, wenn Sie versuchen, etwas zu verwenden, das überall funktioniert.Gleiche Antwort wie die vorherige Frage.
Nicht 100% sicher, aber ich denke, es hat damit zu tun, dass die Unterabfrage mehrmals ausgeführt wird und welche Art von Sperren sie für die Tabelle verwendet.
quelle
Hier finden Sie die Antworten auf zwei Ihrer Fragen
Das Optimierungsprogramm kann den Index nicht verwenden, da sich die where-Klausel für jede Zeile ändert. Die delete-Anweisung sieht ungefähr so aus, nachdem sie den Optimierer bestanden hat
Wenn Sie jedoch den Join ausführen, kann der Server die zu löschenden Zeilen identifizieren.
Trick ist, eine Variable zu verwenden, um die zu halten
BUILDRESULTSUMMARY_ID
und die Variable anstelle der Abfrage zu verwenden. Beachten Sie, dass sowohl die Variableninitialisierung als auch die Löschabfrage innerhalb einer Sitzung ausgeführt werden müssen. Etwas wie das.Dies kann zu Problemen führen, wenn die Abfrage zu viele IDs zurückgibt und dies kein Standardweg ist. Es ist nur eine Problemumgehung.
Und ich habe keine Antwort auf deine beiden anderen Fragen :)
quelle