MySQL ไม่ได้ใช้ดัชนีเมื่อเข้าร่วมกับตารางอื่น


11

ฉันมีสองตารางตารางแรกประกอบด้วยบทความ / บล็อกโพสต์ทั้งหมดภายใน CMS บทความเหล่านี้บางส่วนอาจปรากฏในนิตยสารซึ่งในกรณีนี้พวกเขามีความสัมพันธ์กับต่างประเทศที่สำคัญกับตารางอื่นที่มีข้อมูลเฉพาะของนิตยสาร

นี่คือเวอร์ชันที่เรียบง่ายของไวยากรณ์การสร้างตารางสำหรับสองตารางเหล่านี้ที่มีแถวที่ไม่จำเป็นออกมา:

CREATE TABLE `base_article` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `date_published` datetime DEFAULT NULL,
  `title` varchar(255) NOT NULL,
  `description` text,
  `content` longtext,
  `is_published` int(11) NOT NULL DEFAULT '0',
  PRIMARY KEY (`id`),
  KEY `base_article_date_published` (`date_published`),
  KEY `base_article_is_published` (`is_published`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;

CREATE TABLE `mag_article` (
    `basearticle_ptr_id` int(11) NOT NULL,
    `issue_slug` varchar(8) DEFAULT NULL,
    `rubric` varchar(75) DEFAULT NULL,
    PRIMARY KEY (`basearticle_ptr_id`),
    KEY `mag_article_issue_slug` (`issue_slug`),
    CONSTRAINT `basearticle_ptr_id_refs_id` FOREIGN KEY (`basearticle_ptr_id`) REFERENCES `base_article` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;

CMS มีบทความทั้งหมดประมาณ 250,000 บทความและฉันได้เขียนสคริปต์ Pythonแบบง่ายที่สามารถใช้เพื่อเติมฐานข้อมูลทดสอบด้วยข้อมูลตัวอย่างหากพวกเขาต้องการทำซ้ำปัญหานี้ในเครื่อง

ถ้าฉันเลือกจากหนึ่งในตารางเหล่านี้ MySQL จะไม่มีปัญหาในการเลือกดัชนีที่เหมาะสมหรือดึงบทความได้อย่างรวดเร็ว อย่างไรก็ตามเมื่อรวมสองตารางเข้าด้วยกันในแบบสอบถามแบบง่ายเช่น:

SELECT * FROM `base_article` 
INNER JOIN `mag_article` ON (`mag_article`.`basearticle_ptr_id` = `base_article`.`id`)
WHERE is_published = 1
ORDER BY `base_article`.`date_published` DESC
LIMIT 30

MySQL ล้มเหลวในการเลือกแบบสอบถามที่เหมาะสมและลดประสิทธิภาพ นี่คือคำอธิบายที่เกี่ยวข้องเพิ่มเติม (เวลาดำเนินการซึ่งเกินหนึ่งวินาที):

+----+-------------+--------------+--------+-----------------------------------+---------+---------+----------------------------------------+-------+----------+---------------------------------+
| id | select_type |    table     |  type  |           possible_keys           |   key   | key_len |                  ref                   | rows  | filtered |              Extra              |
+----+-------------+--------------+--------+-----------------------------------+---------+---------+----------------------------------------+-------+----------+---------------------------------+
|  1 | SIMPLE      | mag_article  | ALL    | PRIMARY                           | NULL    | NULL    | NULL                                   | 23830 | 100.00   | Using temporary; Using filesort |
|  1 | SIMPLE      | base_article | eq_ref | PRIMARY,base_article_is_published | PRIMARY | 4       | my_test.mag_article.basearticle_ptr_id |     1 | 100.00   | Using where                     |
+----+-------------+--------------+--------+-----------------------------------+---------+---------+----------------------------------------+-------+----------+---------------------------------+
  • แก้ไขวันที่ 30: ฉันสามารถลบWHEREอนุประโยคออกจากแบบสอบถามนี้ แต่EXPLAINยังคงมีลักษณะเหมือนกันและแบบสอบถามยังคงช้า

ทางออกที่เป็นไปได้อย่างหนึ่งคือบังคับดัชนี เรียกใช้แบบสอบถามเดียวกันกับFORCE INDEX (base_articel_date_published)ผลลัพธ์ในแบบสอบถามที่ดำเนินการในประมาณ 1.6 มิลลิวินาที

+----+-------------+--------------+--------+---------------+-----------------------------+---------+-------------------------+------+-----------+-------------+
| id | select_type |    table     |  type  | possible_keys |             key             | key_len |           ref           | rows | filtered  |    Extra    |
+----+-------------+--------------+--------+---------------+-----------------------------+---------+-------------------------+------+-----------+-------------+
|  1 | SIMPLE      | base_article | index  | NULL          | base_article_date_published |       9 | NULL                    |   30 | 833396.69 | Using where |
|  1 | SIMPLE      | mag_article  | eq_ref | PRIMARY       | PRIMARY                     |       4 | my_test.base_article.id |    1 | 100.00    |             |
+----+-------------+--------------+--------+---------------+-----------------------------+---------+-------------------------+------+-----------+-------------+

ฉันไม่ต้องการบังคับดัชนีในแบบสอบถามนี้หากฉันสามารถหลีกเลี่ยงได้ด้วยเหตุผลหลายประการ ที่สำคัญที่สุดแบบสอบถามพื้นฐานนี้สามารถกรอง / แก้ไขได้หลายวิธี (เช่นการกรองโดยissue_slug) หลังจากนั้นbase_article_date_publishedอาจไม่เป็นดัชนีที่ดีที่สุดที่จะใช้อีกต่อไป

ใครบ้างที่สามารถแนะนำกลยุทธ์ในการปรับปรุงประสิทธิภาพสำหรับการค้นหานี้


ถ้าคอลัมน์ "is_published" เพียง แต่ช่วยให้สองหรือสามค่าจริงๆคุณอาจลดลงดัชนีคีย์base_article_is_published( is_published) .. ดูเหมือนผมมันเป็นชนิดบูลีน ..
เรย์มอนด์ Nijland

แก้ไขคำตอบ
Raymond Nijland

คำตอบ:


5

สิ่งนี้ควรลบความต้องการ "การใช้ชั่วคราวการใช้ไฟล์" เพราะข้อมูลอยู่ในประเภทที่ถูกต้องอยู่แล้ว

คุณจำเป็นต้องรู้เคล็ดลับที่ว่าทำไม MySQL ต้องการ "การใช้งานชั่วคราว; การใช้ filesort" เพื่อลบความต้องการนั้นออก

ดู sqlfriddle ที่สองสำหรับการอธิบายเกี่ยวกับการลบความต้องการ

SELECT
      *
    FROM base_article

    STRAIGHT_JOIN 
      mag_article
    ON
      (mag_article.basearticle_ptr_id = base_article.id)

    WHERE
      base_article.is_published = 1

    ORDER BY
      base_article.date_published DESC

ดูhttp://sqlfiddle.com/#!2/302710/2

ทำงานได้ค่อนข้างดีฉันต้องการสิ่งนี้เมื่อไม่นานมานี้สำหรับตารางประเทศ / เมืองดูการสาธิตที่นี่พร้อมข้อมูลตัวอย่างhttp://sqlfiddle.com/#!2/b34870/41

แก้ไขแล้วคุณอาจต้องการวิเคราะห์คำตอบนี้หาก base_article.is_published = 1 ส่งคืน 1 ระเบียนเสมอเช่นที่คุณอธิบายอธิบายตาราง INNER JOIN deliveryd อาจให้ประสิทธิภาพที่ดีขึ้นเช่นแบบสอบถามในคำตอบด้านล่าง

/programming/18738483/mysql-slow-query-using-filesort/18774937#18774937


คำตอบช่วยชีวิต! ฉันใช้JOINแต่ MySQL ไม่ได้เก็บดัชนี ขอบคุณ Raymond มาก
Maximus

4

ผู้ตอบแบบสอบถาม

SELECT * FROM
(SELECT * FROM base_article
WHERE is_published = 1
ORDER BY date_published LIMIT 30) A
INNER JOIN mag_article B
ON A.id = B.basearticle_ptr_id;

หรือ

SELECT B.*,C.* FROM
(SELECT id FROM base_article
WHERE is_published = 1
ORDER BY date_published LIMIT 30) A
LEFT JOIN base_article ON A.id = B.id
LEFT JOIN mag_article C ON B.id = C.basearticle_ptr_id;

แก้ไขดัชนีของคุณ

ALTER TABLE base_article DROP INDEX base_article_is_published;
ALTER TABLE base_article ADD INDEX ispub_datepub_index (is_published,date_published);

ให้มันลอง !!!


Refactor: ไม่ทำงานฉันกลัวเพราะLIMIT 30อยู่ในแบบสอบถามย่อย (ไม่ใช่แถวทั้งหมด 30 แถวที่จะอยู่ในmag_articlesตาราง) หากฉันย้ายLIMITไปยังข้อความค้นหาภายนอกประสิทธิภาพจะเหมือนกับในต้นฉบับของฉัน แก้ไขดัชนี: MySQL ไม่ได้ใช้ดัชนีนั้น การลบWHEREคำสั่งย่อยออกจากข้อความค้นหาเดิมของฉันดูเหมือนจะไม่สร้างความแตกต่าง
Joshmaker

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