MySQL: xóa bit where..in () vs xóa..from..join và các bảng bị khóa khi xóa bằng subselect


9

Tuyên bố miễn trừ trách nhiệm: xin miễn cho sự thiếu hiểu biết của tôi về nội bộ cơ sở dữ liệu. Nó đi từ đây:

Chúng tôi chạy một ứng dụng (không phải do chúng tôi viết) có vấn đề về hiệu năng lớn trong công việc dọn dẹp định kỳ trong cơ sở dữ liệu. Truy vấn trông như thế này:

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

Thẳng thắn, dễ đọc và SQL chuẩn. Nhưng tiếc là rất chậm. Giải thích truy vấn cho thấy rằng chỉ mục hiện có trên VARIABLE_SUBSTITUTION.BUILDRESULTSUMMARY_IDkhông được sử dụng:

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 |

Điều này làm cho nó rất chậm (120 giây trở lên). Ngoài ra, nó dường như chặn các truy vấn cố gắng chèn vào BUILDRESULTSUMMARY, xuất ra từ 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')

Điều này làm chậm hệ thống và buộc chúng tôi phải tăng lên innodb_lock_wait_timeout.

Khi chúng tôi chạy MySQL, chúng tôi viết lại truy vấn xóa để sử dụng "xóa khỏi tham gia":

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

Điều này dễ đọc hơn một chút, tiếc là không có SQL tiêu chuẩn (theo như tôi có thể tìm hiểu), nhưng nhanh hơn rất nhiều (0,02 giây hoặc lâu hơn) vì nó sử dụng chỉ mục:

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                     |

Thông tin bổ sung:

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 |

(một số thứ bị bỏ qua, nó là một bảng khá rộng).

Vì vậy, tôi có một vài câu hỏi về điều này:

  • Tại sao trình tối ưu hóa truy vấn không thể sử dụng chỉ mục để xóa khi phiên bản truy vấn con, trong khi đó là trong khi sử dụng phiên bản tham gia?
  • Có cách nào (lý tưởng phù hợp tiêu chuẩn) để lừa nó sử dụng chỉ mục không? hoặc là
  • Có một cách di động để viết một delete from join? Ứng dụng hỗ trợ PostgreSQL, MySQL, Oracle và Microsoft SQL Server, được sử dụng thông qua jdbc và Hibernate.
  • Tại sao xóa khỏi VARIABLE_SUBSTITUTIONchặn chèn vào BUILDRESULTSUMMARY, chỉ được sử dụng trong phần chọn phụ?

Máy chủ Percona 5.6.24-72.2-1.jessie resp 5.6.24-72.2-1.wheezy (trên hệ thống kiểm tra).
0x89

Có, toàn bộ cơ sở dữ liệu sử dụng innodb.
0x89

Vì vậy, có vẻ như 5.6 đã không được chú ý nhiều trong việc cải thiện trình tối ưu hóa. Bạn sẽ phải chờ 5.7 (nhưng hãy thử MariaDB nếu có thể. Các cải tiến trình tối ưu hóa của họ đã được thực hiện trở lại trong phiên bản 5.3 và 5.5 của họ.)
ypercubeᵀᴹ

@ypercube AFAIK không có ngã ba có một cải tiến để làm cho truy vấn xóa được tối ưu hóa cũng như 5.7. Xóa tối ưu hóa khác nhau từ các câu lệnh CHỌN.
Morgan Tocker

Câu trả lời:


7
  • Tại sao trình tối ưu hóa truy vấn không thể sử dụng chỉ mục để xóa khi phiên bản truy vấn con, trong khi đó là trong khi sử dụng phiên bản tham gia?

Bởi vì trình tối ưu hóa là / hơi ngu ngốc trong vấn đề đó. Không chỉ cho DELETEUPDATEmà còn cho các SELECTbáo cáo, mọi thứ như WHERE column IN (SELECT ...)không được tối ưu hóa hoàn toàn. Kế hoạch thực hiện thường liên quan đến việc chạy truy vấn con cho mỗi hàng của bảng bên ngoài ( VARIABLE_SUBSTITUTIONtrong trường hợp này). Nếu cái bàn đó nhỏ, mọi thứ đều ổn. Nếu nó lớn, không có hy vọng. Trong các phiên bản thậm chí cũ hơn, một INtruy vấn phụ với một INtruy vấn phụ sẽ làm cho ngay cả việc EXPLAINchạy trong thời gian dài.

Những gì bạn có thể làm - nếu bạn muốn giữ truy vấn này - là sử dụng các phiên bản mới nhất đã thực hiện một số tối ưu hóa và kiểm tra lại. Các phiên bản mới nhất có nghĩa: MySQL 5.6 (và 5.7 khi ra bản beta) và MariaDB 5.5 / 10.0

(cập nhật) Bạn đã sử dụng 5.6 có cải tiến tối ưu hóa và điều này có liên quan: Tối ưu hóa các truy vấn con với Chuyển đổi bán tham gia
Tôi khuyên bạn nên thêm một chỉ mục (BUILD_KEY)một mình. Có một tổng hợp nhưng điều đó không hữu ích cho truy vấn này.

  • Có cách nào (lý tưởng phù hợp tiêu chuẩn) để lừa nó sử dụng chỉ mục không?

Không ai mà tôi có thể nghĩ ra. Theo tôi, không có nhiều giá trị trong việc cố gắng sử dụng SQL tiêu chuẩn. Có rất nhiều sự khác biệt và các yêu cầu nhỏ mà mỗi DBMS có ( UPDATEvà các DELETEcâu lệnh là ví dụ tốt về sự khác biệt đó) mà khi bạn cố gắng sử dụng một cái gì đó hoạt động ở mọi nơi, kết quả là một tập hợp con rất hạn chế của SQL.

  • Có một cách di động để viết xóa từ tham gia? Ứng dụng hỗ trợ PostgreSQL, MySQL, Oracle và Microsoft SQL Server, được sử dụng thông qua jdbc và Hibernate.

Câu trả lời tương tự như câu hỏi trước.

  • tại sao xóa khỏi VARIABLE_SUBSTITNING chặn chèn vào BUILDRESULTSUMMARY, chỉ được sử dụng trong phần phụ?

Không chắc chắn 100% nhưng tôi nghĩ nó phải làm với việc chạy truy vấn con nhiều lần và loại khóa nào đang sử dụng trên bàn.


"3775190 khóa hàng (s)" từ innodb_status (của giao dịch xóa) có tính gợi ý cao. Nhưng cũng "bảng mysql sử dụng 2, khóa 2" có vẻ không tốt lắm đối với tôi ..
0x89

2

đây là câu trả lời cho hai câu hỏi của bạn

  • Trình tối ưu hóa không thể sử dụng chỉ mục vì mệnh đề where thay đổi cho mỗi hàng. Câu lệnh xóa sẽ trông giống như thế này sau khi nó vượt qua trình tối ưu hóa

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

nhưng khi bạn thực hiện tham gia, Máy chủ có thể xác định các hàng phải xóa.

  • mẹo là sử dụng một biến để giữ BUILDRESULTSUMMARY_IDvà sử dụng biến thay vì truy vấn. Lưu ý rằng cả khởi tạo biến và truy vấn xóa phải chạy trong một phiên. Một cái gì đó như thế này.

    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;

    bạn có thể gặp vấn đề với điều này nếu truy vấn trả về quá nhiều id và đây không phải là cách chuẩn. Nó chỉ là một cách giải quyết.

    Và tôi không có câu trả lời cho hai câu hỏi khác của bạn :)


Được rồi, bạn đã bỏ lỡ quan điểm của tôi. Tôi nghĩ rằng những gì bạn đã không xem xét là cả hai VARIABLE_SUBSTITUTION và BUILDRESULTSUMMARY có một cột tên là BUILDRESULTSUMMARY_ID, vì vậy nó cần được: 'xóa từ VARIABLE_SUBSTITUTION nơi EXISTS (chọn BUILDRESULTSUMMARY_ID từ BUILDRESULTSUMMARY nơi BUILDRESULTSUMMARY.BUILDRESULTSUMMARY_ID = VARIABLE_SUBSTITUTION.BUILDRESULTSUMMARY_ID VÀ BUILDRESULTSUMMARY.BUILD_KEY = "BAM -1 "); '. Sau đó, nó có ý nghĩa và cả hai truy vấn đều làm như vậy.
0x89

1
vâng tôi chỉ thiếu một tham chiếu đến bảng bên ngoài. Nhưng đó không phải là vấn đề. Đó chỉ là một minh họa về cách nó sẽ được xử lý trong trình tối ưu hóa.
Masoud

Với sự khác biệt nhỏ mà trình tối ưu hóa sẽ tạo ra một truy vấn tương đương.
0x89
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.