면책 조항 : 데이터베이스 내부에 대한 지식이 부족하다는 것을 변명하십시오. 여기 간다:
데이터베이스의 주기적 정리 작업에서 성능 문제가 큰 응용 프로그램 (우리가 작성하지 않은)을 실행합니다. 쿼리는 다음과 같습니다.
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
무엇입니까?이 하위 선택에서만 사용됩니까?
답변
- 쿼리 최적화 프로그램이 조인 버전을 사용하는 동안 하위 쿼리 버전을 삭제할 때 인덱스를 사용할 수없는 이유는 무엇입니까?
그 점에서 옵티마이 저는 약간 바보이기 때문입니다. 에 대한뿐만 아니라 DELETE
및 UPDATE
하지만위한 SELECT
뿐만 아니라 문, 이런 건 WHERE column IN (SELECT ...)
완전히 최적화되지 않았습니다. 실행 계획에는 일반적으로 외부 테이블의 모든 행 ( VARIABLE_SUBSTITUTION
이 경우)에 대해 하위 쿼리를 실행하는 것이 포함 되었습니다. 해당 테이블이 작 으면 모든 것이 정상입니다. 그것이 크면 희망이 없습니다. 이전 버전의 경우 IN
하위 하위 쿼리가 포함 된 IN
하위 쿼리는 EXPLAIN
연령에 따라 실행되도록 할 수도 있습니다.
이 쿼리를 유지하려는 경우 수행 할 수있는 작업은 여러 가지 최적화를 구현 한 최신 버전을 사용하고 다시 테스트하는 것입니다. 최신 버전 : MySQL 5.6 (및 베타 버전에서는 5.7) 및 MariaDB 5.5 / 10.0
(업데이트) 이미 최적화 개선 기능이있는 5.6을 사용하고 있으며, 이와 관련이 있습니다. 세미 조인 변환으로 하위 쿼리 최적화
하기 인덱스 (BUILD_KEY)
만 추가하는 것이 좋습니다 . 복합적인 것이 있지만이 쿼리에는별로 유용하지 않습니다.
- 색인을 사용하도록 속일 수있는 (이상적인 표준 준수) 방법이 있습니까?
내가 생각할 수있는 것은 없습니다. 제 생각에는 표준 SQL을 사용하려고 시도 할 가치가 없습니다. 각 DBMS가있다 (너무 많은 차이와 약간의 단점이 있습니다 UPDATE
및 DELETE
문은 차이의 좋은 예입니다) 당신은 모든 곳에서 작동하는 무언가를 사용하려고하면, 결과는 SQL의 매우 제한된 부분 집합이라고는.
- 조인에서 삭제를 작성하는 이식 가능한 방법이 있습니까? 이 응용 프로그램은 jdbc 및 Hibernate를 통해 사용되는 PostgreSQL, MySQL, Oracle 및 Microsoft SQL Server를 지원합니다.
이전 질문과 같은 답변입니다.
- VARIABLE_SUBSTITUTION 차단에서 삭제가 BUILDRESULTSUMMARY에 삽입되는 이유는 무엇입니까?이 하위 선택에서만 사용됩니까?
100 % 확실하지는 않지만 하위 쿼리를 여러 번 실행하고 테이블에서 어떤 유형의 잠금을 수행하는지와 관련이 있다고 생각합니다.
답변
다음은 두 가지 질문에 대한 답변입니다.
-
where 절이 모든 행에 대해 변경되므로 Optimizer는 인덱스를 사용할 수 없습니다. 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를 반환하고 이것이 표준 방법이 아닌 경우 문제가 발생할 수 있습니다. 해결 방법 일뿐입니다.
그리고 나는 당신의 다른 두 가지 질문에 대한 답이 없습니다 🙂