MySQL: ลบ…ใน .. ใน () เทียบกับลบ .. จาก .. เข้าร่วมและล็อคตารางเมื่อลบด้วยการเลือกย่อย


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? แอพพลิเคชั่นรองรับ PostgreSQL, MySQL, Oracle และ Microsoft SQL Server ที่ใช้ผ่าน jdbc และ Hibernate
  • เหตุใดการลบจากVARIABLE_SUBSTITUTIONการบล็อกการแทรกBUILDRESULTSUMMARYจึงใช้ในการเลือกย่อยเท่านั้น

เซิร์ฟเวอร์ Percona 5.6.24-72.2-1.jessie resp 5.6.24-72.2-1.wheezy (บนระบบทดสอบ)
0x89

ใช่ฐานข้อมูลทั้งหมดใช้ innodb
0x89

ดังนั้นดูเหมือนว่า 5.6 ไม่ได้รับความสนใจมากในการปรับปรุงเครื่องมือเพิ่มประสิทธิภาพ คุณจะต้องรอ 5.7 (แต่ลองใช้ MariaDB หากทำได้การปรับปรุงเครื่องมือเพิ่มประสิทธิภาพของพวกเขากลับมาในรุ่น 5.3 และ 5.5)
ypercubeᵀᴹ

@ypercube AFAIK ไม่มีทางแยกมีการปรับปรุงเพื่อทำการลบแบบสอบถามย่อยที่เหมาะสมที่สุดหรือ 5.7 ลบการปรับให้เหมาะสมแตกต่างจากคำสั่ง SELECT
Morgan Tocker

คำตอบ:


7
  • เหตุใดเครื่องมือเพิ่มประสิทธิภาพคิวรีจึงไม่สามารถใช้ดัชนีสำหรับการลบเมื่อเวอร์ชันย่อยของแบบสอบถามในขณะที่ใช้เวอร์ชันเข้าร่วมได้

เนื่องจากเครื่องมือเพิ่มประสิทธิภาพเป็น / เป็นบิตที่น่าสนใจในเรื่องนั้น ไม่เพียงแต่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

  • มีวิธีพกพาในการเขียนการลบจากการเข้าร่วม? แอพพลิเคชั่นรองรับ PostgreSQL, MySQL, Oracle และ Microsoft SQL Server ที่ใช้ผ่าน jdbc และ Hibernate

คำตอบเดียวกับคำถามก่อนหน้า

  • เหตุใดการลบจาก VARIABLE_SUBSTITUTION การบล็อกการแทรกลงใน BUILDRESULTSUMMARY ซึ่งใช้ในการเลือกย่อยเท่านั้น

ไม่แน่ใจ 100% แต่ฉันคิดว่ามันเกี่ยวกับการเรียกใช้แบบสอบถามย่อยหลายครั้งและการล็อคประเภทใดที่อยู่บนโต๊ะ


"3775190 การล็อกแถว" จาก innodb_status (ของการทำธุรกรรมการลบ) เป็นสิ่งที่มีการชี้นำ แต่ด้วย "ตาราง mysql ที่ใช้ 2 ถูกล็อค 2" ดูไม่ดีสำหรับฉัน ..
0x89

2

นี่คือคำตอบสำหรับคำถามสองข้อของคุณ

  • เครื่องมือเพิ่มประสิทธิภาพไม่สามารถใช้ดัชนีได้เนื่องจากส่วนคำสั่งเปลี่ยนแปลงทุกแถว คำสั่งลบจะมีลักษณะเช่นนี้หลังจากผ่านเครื่องมือเพิ่มประสิทธิภาพ

    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;

    คุณอาจประสบปัญหานี้ถ้าแบบสอบถามส่งคืนรหัสมากเกินไปและนี่ไม่ใช่วิธีมาตรฐาน มันเป็นเพียงวิธีแก้ปัญหา

    และฉันไม่มีคำตอบสำหรับคำถามอีกสองข้อของคุณ :)


ตกลงคุณพลาดจุดของฉัน ผมคิดว่าสิ่งที่คุณไม่ได้พิจารณาคือทั้ง VARIABLE_SUBSTITUTION และ BUILDRESULTSUMMARY มีคอลัมน์ชื่อ BUILDRESULTSUMMARY_ID ดังนั้นจึงควรจะเป็น: 'ลบออกจาก VARIABLE_SUBSTITUTION ที่ EXISTS (เลือก BUILDRESULTSUMMARY_ID จาก BUILDRESULTSUMMARY ที่ BUILDRESULTSUMMARY.BUILDRESULTSUMMARY_ID = VARIABLE_SUBSTITUTION.BUILDRESULTSUMMARY_ID และ BUILDRESULTSUMMARY.BUILD_KEY = "BAM -1" );' ถ้าอย่างนั้นก็สมเหตุสมผลแล้วทั้งสองข้อก็ทำเช่นเดียวกัน
0x89

1
ใช่ฉันแค่พลาดการอ้างอิงไปยังตารางด้านนอก แต่นั่นไม่ใช่ประเด็น นี่เป็นเพียงตัวอย่างของวิธีการใช้งานเครื่องมือเพิ่มประสิทธิภาพ
Masoud

ด้วยความแตกต่างเล็กน้อยที่เครื่องมือเพิ่มประสิทธิภาพจะสร้างข้อความค้นหาที่เทียบเท่า
0x89
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.