เมื่อใดควรใช้ STRAIGHT_JOIN กับ MySQL


88

ฉันมีคำถามที่ค่อนข้างซับซ้อนที่ฉันกำลังทำงานอยู่และใช้เวลา 8 วินาทีในการเรียกใช้ EXPLAIN แสดงลำดับตารางแปลก ๆ และดัชนีของฉันไม่ได้ถูกใช้ทั้งหมดแม้จะมีคำใบ้ FORCE INDEX ก็ตาม ฉันเจอคีย์เวิร์ดเข้าร่วม STRAIGHT_JOIN และเริ่มแทนที่คีย์เวิร์ด INNER JOIN บางคำด้วยคีย์เวิร์ด ฉันสังเกตเห็นการปรับปรุงความเร็วอย่างมาก ในที่สุดฉันก็แทนที่คำหลัก INNER JOIN ทั้งหมดของฉันด้วย STRAIGHT_JOIN สำหรับคำค้นหานี้และตอนนี้มันทำงานใน. 01 วินาที

คำถามของฉันคือคุณใช้ STRAIGHT_JOIN เมื่อใดและคุณใช้ INNER JOIN เมื่อใด มีเหตุผลใดบ้างที่จะไม่ใช้ STRAIGHT_JOIN หากคุณเขียนข้อความค้นหาที่ดี

คำตอบ:


73

ฉันไม่แนะนำให้ใช้ STRAIGHT_JOIN โดยไม่มีเหตุผลที่ดี ประสบการณ์ของฉันเองคือเครื่องมือเพิ่มประสิทธิภาพการสืบค้น MySQL เลือกแผนการสืบค้นที่ไม่ดีบ่อยกว่าที่ฉันต้องการ แต่ไม่บ่อยพอที่คุณควรจะข้ามไปโดยทั่วไปซึ่งเป็นสิ่งที่คุณจะต้องทำหากคุณใช้ STRAIGHT_JOIN อยู่เสมอ

คำแนะนำของฉันคือปล่อยให้การค้นหาทั้งหมดเป็นการเข้าร่วมปกติ หากคุณพบว่าหนึ่งแบบสอบถามกำลังใช้แผนการสืบค้นย่อยที่เหมาะสมที่สุดฉันขอแนะนำก่อนอื่นให้ลองเขียนใหม่หรือจัดโครงสร้างคิวรีใหม่สักเล็กน้อยเพื่อดูว่าเครื่องมือเพิ่มประสิทธิภาพจะเลือกแผนการสืบค้นข้อมูลที่ดีกว่าได้หรือไม่ นอกจากนี้อย่างน้อยสำหรับ innodb ตรวจสอบให้แน่ใจว่าไม่ใช่แค่ว่าสถิติดัชนีของคุณล้าสมัย ( ตารางวิเคราะห์ ) ซึ่งอาจทำให้เครื่องมือเพิ่มประสิทธิภาพเลือกแผนการสืบค้นข้อมูลที่ไม่ดี โดยทั่วไปคำแนะนำของเครื่องมือเพิ่มประสิทธิภาพควรเป็นทางเลือกสุดท้ายของคุณ

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


60
คำตอบนี้ไม่ได้อธิบายว่าควรใช้เมื่อ straight_joinใด
Pacerier

23

จากการอ้างอิง MySQL JOIN :

"STRAIGHT_JOIN คล้ายกับ JOIN ยกเว้นว่าตารางด้านซ้ายจะอ่านก่อนตารางด้านขวาเสมอซึ่งสามารถใช้ได้กับกรณี (ไม่กี่) กรณีที่เครื่องมือเพิ่มประสิทธิภาพการเข้าร่วมทำให้ตารางเรียงลำดับไม่ถูกต้อง"


28
ขอบคุณ แต่ฉันอ่านคู่มือ MySQL แล้ว หวังว่าจะได้รับคำอธิบายเพิ่มเติม
Greg

20

นี่คือสถานการณ์ที่เกิดขึ้นเมื่อไม่นานมานี้ในที่ทำงาน

พิจารณาสามตาราง A, B, C

A มี 3,000 แถว; B มี 300,000,000 แถว; และ C มี 2,000 แถว

มีการกำหนดคีย์ต่างประเทศ: B (a_id), B (c_id)

สมมติว่าคุณมีข้อความค้นหาที่มีลักษณะดังนี้:

select a.id, c.id
from a
join b on b.a_id = a.id
join c on c.id = b.c_id

จากประสบการณ์ของฉัน MySQL อาจเลือกไปที่ C -> B -> A ในกรณีนี้ C มีขนาดเล็กกว่า A และ B นั้นมีค่ามหาศาลและพวกมันทั้งหมดเป็น Equijoins

ปัญหาคือ MySQL ไม่จำเป็นต้องคำนึงถึงขนาดของจุดตัดระหว่าง (C.id และ B.c_id) เทียบกับ (A.id และ B.a_id) ถ้าการรวมระหว่าง B และ C ส่งคืนแถวเท่า ๆ กับ B แสดงว่าเป็นตัวเลือกที่แย่มาก ถ้าเริ่มต้นด้วย A จะกรอง B ลงให้มากที่สุดเท่าที่ A แถวนั้นจะเป็นทางเลือกที่ดีกว่า straight_joinสามารถใช้บังคับคำสั่งนี้ได้ดังนี้:

select a.id, c.id
from a
straight_join b on b.a_id = a.id
join c on c.id = b.c_id

ตอนนี้ต้องสามารถเข้าร่วมก่อนab

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

สถิติขึ้นอยู่กับว่า หากการกระจายข้อมูลเปลี่ยนแปลงการคำนวณอาจเปลี่ยนแปลง นอกจากนี้ยังขึ้นอยู่กับรายละเอียดการใช้งานของกลไกการเข้าร่วม

กรณีที่เลวร้ายที่สุดที่ฉันเคยเห็นสำหรับ MySQL straight_joinการบอกใบ้ดัชนีทั้งหมดยกเว้นที่จำเป็นหรือเชิงรุกคือข้อความค้นหาที่แบ่งหน้าข้อมูลจำนวนมากตามลำดับการจัดเรียงที่เข้มงวดพร้อมการกรองแสง MySQL ต้องการใช้ดัชนีสำหรับตัวกรองใด ๆ และเข้าร่วมในประเภทต่างๆ สิ่งนี้สมเหตุสมผลเพราะคนส่วนใหญ่ไม่ได้พยายามจัดเรียงฐานข้อมูลทั้งหมด แต่มีแถวย่อยที่ จำกัด ที่ตอบสนองต่อแบบสอบถามและการจัดเรียงชุดย่อยที่ จำกัด นั้นเร็วกว่าการกรองทั้งตารางไม่ว่าจะเรียงลำดับหรือ ไม่. ในกรณีนี้การรวมแบบตรงทันทีหลังตารางที่มีคอลัมน์ที่จัดทำดัชนีที่ฉันต้องการจัดเรียงตามสิ่งที่ตายตัว


คุณจะใช้การเข้าร่วมโดยตรงเพื่อแก้ไขปัญหาอย่างไร
Hannele

@ Hannele straight_joinประเมินตารางซ้ายก่อนขวา ดังนั้นหากคุณต้องการที่จะไปจากA -> B -> Cในตัวอย่างของฉันเป็นครั้งแรกคำหลักจะถูกแทนที่ด้วยjoin straight_join
Barry Kelly

อ่าเรียบร้อย จะมีประโยชน์หากรวมสิ่งนั้นไว้เป็นตัวอย่างในคำตอบของคุณ :)
Hannele

18

MySQL ไม่จำเป็นต้องเลือกลำดับการเข้าร่วมในแบบสอบถามที่ซับซ้อน โดยการระบุแบบสอบถามที่ซับซ้อนเป็น direct_join แบบสอบถามจะเรียกใช้การรวมตามลำดับที่ระบุ การวางตารางให้เป็นตัวส่วนร่วมน้อยที่สุดก่อนและระบุ straight_join คุณจะสามารถปรับปรุงประสิทธิภาพการสืบค้นได้


11

STRAIGHT_JOINเมื่อใช้ประโยคนี้คุณสามารถควบคุมJOINลำดับ: ตารางใดที่ถูกสแกนในวงนอกและตารางใดอยู่ในวงใน


วงนอกและวงในคืออะไร?
Istiaque Ahmed

@Istiaque Ahmed ตารางรวมกันด้วยลูปที่ซ้อนกัน (ใช้แถวแรกจากตาราง A และตารางการโยนลูป B จากนั้นใช้แถวที่สอง ... และอื่น ๆ ที่นี่ตาราง A อยู่ที่วงนอก)
นักบัญชีم

6

ฉันจะบอกคุณว่าทำไมฉันต้องใช้ STRAIGHT_JOIN:

  • ฉันมีปัญหาด้านประสิทธิภาพกับข้อความค้นหา
  • การลดความซับซ้อนของการสืบค้นแบบสอบถามมีประสิทธิภาพมากขึ้นอย่างน่าเหลือเชื่อ
  • พยายามคิดว่าส่วนใดที่ทำให้เกิดปัญหาฉันก็ทำไม่ได้ (การรวมทางซ้าย 2 ตัวเข้าด้วยกันช้าและแต่ละอันก็เร็วอย่างอิสระ)
  • จากนั้นฉันดำเนินการ EXPLAIN ด้วยแบบสอบถามทั้งช้าและเร็ว (เพิ่มหนึ่งในการรวมด้านซ้าย)
  • น่าแปลกใจที่ MySQL เปลี่ยนคำสั่ง JOIN ทั้งหมดระหว่างคำค้นหา 2 รายการ

ดังนั้นฉันจึงบังคับให้หนึ่งในการรวมเป็น straight_join เพื่อบังคับให้การเข้าร่วมก่อนหน้านี้ถูกอ่านก่อน สิ่งนี้ทำให้ MySQL เปลี่ยนคำสั่งดำเนินการและทำงานได้อย่างมีเสน่ห์!


2

จากประสบการณ์สั้น ๆ ของฉันหนึ่งในสถานการณ์ที่STRAIGHT_JOINทำให้การสืบค้นของฉันลดลงจาก 30 วินาทีเหลือ 100 มิลลิวินาทีคือตารางแรกในแผนการดำเนินการไม่ใช่ตารางที่มีลำดับตามคอลัมน์

-- table sales (45000000) rows
-- table stores (3) rows
SELECT whatever
FROM 
    sales 
    INNER JOIN stores ON sales.storeId = stores.id
ORDER BY sales.date, sales.id 
LIMIT 50;
-- there is an index on (date, id)

หากเครื่องมือเพิ่มประสิทธิภาพเลือกที่จะตีstores ก่อนจะทำให้เกิดUsing index; Using temporary; Using filesortเพราะ

ถ้า ORDER BY หรือ GROUP BY มีคอลัมน์จากตารางอื่นที่ไม่ใช่ตารางแรกในคิวการเข้าร่วมตารางชั่วคราวจะถูกสร้างขึ้น

แหล่งที่มา

ที่นี่เครื่องมือเพิ่มประสิทธิภาพต้องการความช่วยเหลือเล็กน้อยโดยบอกให้เขาตีsalesก่อนโดยใช้

sales STRAIGHT_JOIN stores

1
(ฉันประดับคำตอบของคุณ)
Rick James

2

ถ้าปลายแบบสอบถามของคุณด้วยORDER BY... LIMIT...มันอาจจะเป็นที่ดีที่สุดที่จะกำหนดใหม่แบบสอบถามเพื่อหลอกลวงให้เพิ่มประสิทธิภาพการเข้าทำLIMIT ก่อนJOIN

(คำตอบนี้ใช้ไม่ได้เฉพาะกับคำถามเดิมเกี่ยวกับSTRAIGHT_JOINและไม่ใช้กับทุกกรณีSTRAIGHT_JOIN)

เริ่มต้นด้วยตัวอย่างโดย @Accountant مสิ่งนี้ควรทำงานได้เร็วขึ้นในสถานการณ์ส่วนใหญ่ (และหลีกเลี่ยงการต้องการคำใบ้)

SELECT  whatever
    FROM  ( SELECT id FROM sales
                ORDER BY  date, id
                LIMIT  50
          ) AS x
    JOIN  sales   ON sales.id = x.id
    JOIN  stores  ON sales.storeId = stores.id
    ORDER BY  sales.date, sales.id;

หมายเหตุ:

  • ขั้นแรกให้ดึงข้อมูล 50 รหัส INDEX(date, id)นี้จะเป็นโดยเฉพาะอย่างยิ่งได้อย่างรวดเร็วด้วย
  • จากนั้นการรวมกลับเพื่อsalesให้คุณได้รับ "whatevers" เพียง 50 คนโดยไม่ต้องลากพวกเขาไปรอบ ๆ ในตารางชั่วคราว
  • เนื่องจากการORDER BYสืบค้นย่อยเป็นไปตามคำนิยามไม่เรียงลำดับจึงต้องทำซ้ำในแบบสอบถามภายนอก (เครื่องมือเพิ่มประสิทธิภาพอาจหาวิธีหลีกเลี่ยงการทำแบบอื่นจริงๆ)
  • ใช่มันยุ่งกว่า แต่มักจะเร็วกว่า

ฉันไม่เห็นด้วยกับการใช้เพลงฮิตเพราะ "แม้ว่าวันนี้จะเร็วกว่า แต่พรุ่งนี้ก็อาจจะเร็วกว่านี้ไม่ได้"


0

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

  • ตามลำดับที่ถูกต้อง

ใส่คำอธิบายภาพที่นี่

  • การเพิ่มรหัสทีละ 1 จะทำให้คำสั่งซื้อยุ่งเหยิง สังเกตช่อง "พิเศษ"

ใส่คำอธิบายภาพที่นี่

  • การใช้ straight_join ช่วยแก้ปัญหาได้

ใส่คำอธิบายภาพที่นี่

คำสั่งที่ไม่ถูกต้องทำงานเป็นเวลาประมาณ 65 วินาทีในขณะที่ใช้ straight_join ทำงานเป็นมิลลิวินาที


-5
--use 120s, 18 million data
    explain SELECT DISTINCT d.taid
    FROM tvassist_recommend_list_everyday_diverse d, tvassist_taid_all t
    WHERE d.taid = t.taid
      AND t.client_version >= '21004007'
      AND t.utdid IS NOT NULL
      AND d.recommend_day = '20170403'
    LIMIT 0, 10000

--use 3.6s repalce by straight join
 explain SELECT DISTINCT d.taid
    FROM tvassist_recommend_list_everyday_diverse d
    STRAIGHT_JOIN 
      tvassist_taid_all t on d.taid = t.taid 
    WHERE 
     t.client_version >= '21004007'
       AND d.recommend_day = '20170403'

      AND t.utdid IS NOT NULL  
    LIMIT 0, 10000

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