จะเร็วกว่าอะไรแบบสอบถามขนาดใหญ่หรือแบบสอบถามขนาดเล็กจำนวนมาก


68

ฉันทำงานให้กับ บริษัท ที่แตกต่างกันและฉันสังเกตเห็นว่าบางคนชอบที่จะมีมุมมองที่จะเข้าร่วมโต๊ะกับ "ญาติ" ทั้งหมดของมัน แต่ในบางครั้งเราจำเป็นต้องใช้เพียง 1 คอลัมน์เท่านั้น

ดังนั้นจะเร็วกว่าที่จะทำการเลือกแบบง่าย ๆ แล้ว "เข้าร่วม" ในรหัสระบบหรือไม่

ระบบอาจเป็น php, java, asp, ภาษาใด ๆ ที่เชื่อมต่อกับฐานข้อมูล

ดังนั้นคำถามคือสิ่งที่เร็วไปจากฝั่งเซิร์ฟเวอร์ (php, java, asp, ruby, python ... ) ไปยังฐานข้อมูลรันหนึ่งแบบสอบถามที่ได้รับทุกสิ่งที่เราต้องการหรือไปจากฝั่งเซิร์ฟเวอร์ไปยังฐานข้อมูลและเรียกใช้ แบบสอบถามที่ได้รับคอลัมน์จากตารางเดียวเท่านั้นหรือไม่


2
คุณกำลังใช้งาน 'SQL' ประเภทใด MySQL, Microsoft SQL Server, Oracle, Postgresql ฯลฯ ใช่ไหม โปรดอัปเดตแท็กของคุณ
RLF

1
Mysql และ Postgresql
sudo.ie

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

3
@a_horse_with_no_name นั่นเป็นลักษณะทั่วไปที่กว้างมากโดยเฉพาะในบริบทของคำถามนี้ เครื่องมือเพิ่มประสิทธิภาพ MySQL นั้นง่ายมากโดยการออกแบบและอาจทำให้เกิดปัญหากับการรวมและการสืบค้นย่อย - โดยเฉพาะอย่างยิ่งในรุ่นเก่าของ MySQL - ที่มีการสร้างแผนได้เร็วขึ้นใน PostgreSQL ในขณะที่ MySQL สามารถทำได้เร็วสำหรับโหลด OLTP บริสุทธิ์ อย่างไรก็ตามในบริบทของคำถามเคียวรีขนาดใหญ่จะเร็วกว่านั้นสมมุติว่า - ในสถานการณ์ที่เลวร้ายที่สุดที่เป็นไปได้ - เลือกภายในลูปการเขียนโปรแกรม (ไม่ว่า RDBMS จะใช้)
jynus

2
@jynus: ดีคำถามคือกว้างมาก (บวก: ผมบอกว่า "ในประสบการณ์ของฉัน" - คนอื่น ๆ อาจมีประสบการณ์ที่แตกต่างกัน) แบบสอบถามภายใน LOOP ไม่ใช่ความคิดที่ดีและมักเป็นผลมาจากการออกแบบที่ไม่ดีหรือขาดความเข้าใจในการทำงานกับฐานข้อมูลเชิงสัมพันธ์
a_horse_with_no_name

คำตอบ:


69

สิ่งที่จะตอบคำถามของคุณคือหัวเรื่องเข้าร่วมการลดทอน

ตามหน้า 209 ของหนังสือ

MySQL ประสิทธิภาพสูง

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

SELECT * FROM tag
JOIN tag_post ON tag_post.tag_id = tag.id
JOIN post ON tag_post.post_id = post.id
WHERE tag.tag = 'mysql';

คุณอาจเรียกใช้แบบสอบถามเหล่านี้:

SELECT * FROM tag WHERE tag = 'mysql';
SELECT * FROM tag_post WHERE tag_id=1234;
SELECT * FROM post WHERE post.id IN (123,456,567,9098,8904);

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

  • การแคชอาจมีประสิทธิภาพมากกว่า แอปพลิเคชันจำนวนมากแคช "วัตถุ" ที่แมปกับตาราง ในตัวอย่างนี้ถ้าวัตถุที่มีแท็กmysqlแคชแล้วแอปพลิเคชันจะข้ามแบบสอบถามแรก หากคุณพบโพสต์ที่มี ID 123, 567 หรือ 908 ในแคชคุณสามารถลบออกจากIN()รายการ แคชแบบสอบถามอาจได้รับประโยชน์จากกลยุทธ์นี้ หากหนึ่งในตารางมีการเปลี่ยนแปลงบ่อยครั้งการแยกย่อยการเข้าร่วมสามารถลดจำนวนการทำให้แคชใช้ไม่ได้
  • การดำเนินการแบบสอบถามแต่ละรายการบางครั้งสามารถลดความขัดแย้งในการล็อก
  • การรวมในแอพพลิเคชั่นทำให้การขยายฐานข้อมูลง่ายขึ้นโดยการวางตารางบนเซิร์ฟเวอร์ที่แตกต่างกัน
  • แบบสอบถามเองมีประสิทธิภาพมากขึ้น ในตัวอย่างนี้การใช้IN()รายการแทนการเข้าร่วมช่วยให้ MySQL เรียงลำดับแถว ID และดึงแถวได้อย่างเหมาะสมที่สุดเกินกว่าที่จะทำได้ด้วยการเข้าร่วม
  • คุณสามารถลดการเข้าถึงแถวซ้ำซ้อนได้ การเข้าร่วมในแอปพลิเคชั่นหมายถึงการดึงข้อมูลแต่ละแถวเพียงครั้งเดียวในขณะที่การเข้าร่วมในแบบสอบถามนั้นเป็นการลบล้างที่อาจเข้าถึงข้อมูลเดียวกันซ้ำ ๆ ด้วยเหตุผลเดียวกันการปรับโครงสร้างดังกล่าวอาจลดทราฟฟิกเครือข่ายและการใช้หน่วยความจำทั้งหมด
  • ในระดับหนึ่งคุณสามารถดูเทคนิคนี้เป็นการใช้การเข้าร่วมแฮชด้วยตนเองแทนอัลกอริทึมลูปซ้อนที่ MySQL ใช้เพื่อดำเนินการเข้าร่วม การเข้าร่วมแฮชอาจมีประสิทธิภาพมากกว่า

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

การสังเกต

ฉันชอบกระสุนแรกเพราะ InnoDB เป็นมือหนักเล็กน้อยเมื่อ crosschecks แคชแบบสอบถาม

สำหรับสัญลักษณ์ล่าสุดที่ฉันเขียนโพสต์กลับเมื่อ 11 มีนาคม 2013 ( มีความแตกต่างการดำเนินการระหว่างเงื่อนไขการเข้าร่วมและเงื่อนไข WHERE? ) ที่อธิบายอัลกอริทึมวนซ้อนกัน หลังจากอ่านแล้วคุณจะเห็นว่าการรวมตัวแบบแยกส่วนนั้นดีแค่ไหน

สำหรับประเด็นอื่น ๆ ทั้งหมดจากหนังสือนักพัฒนามองหาประสิทธิภาพเป็นบรรทัดล่าง บางคนใช้วิธีการภายนอก (นอกแอปพลิเคชั่น) สำหรับการปรับปรุงประสิทธิภาพเช่นการใช้ดิสก์ที่รวดเร็วรับ CPU / Cores มากขึ้นการปรับเอนจิ้นการจัดเก็บและการปรับแต่งไฟล์การกำหนดค่า คนอื่น ๆ จะหัวเข็มขัดลงและเขียนรหัสที่ดีกว่า บางคนอาจใช้วิธีการเข้ารหัสข้อมูลทางธุรกิจทั้งหมดใน Stored Procedure แต่ยังไม่สามารถใช้การรวมแบบแยกส่วนได้ (ดูการโต้แย้งหรือการวางตรรกะของแอปพลิเคชันในชั้นฐานข้อมูลคืออะไรพร้อมกับโพสต์อื่น ๆ ) มันขึ้นอยู่กับวัฒนธรรมและความอดทนของร้านค้าแต่ละแห่ง

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

สำหรับนักพัฒนาที่เต็มใจ ...

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


3
สำหรับลิงค์นั้นเกี่ยวกับการเปลี่ยนเป็น 3 ข้อความค้นหา ... ฉันรู้จักและเคารพ Baron, Vadim และ Peter แต่ฉันไม่เห็นด้วยกับคำแนะนำที่ทำให้เข้าใจผิดนี้ ข้อโต้แย้งส่วนใหญ่ที่สนับสนุนการแบ่งแยกเป็นของหายากที่ไม่ควรพูดถึง ติดกับแบบสอบถามเดียวกับ JOIN แล้วลองปรับปรุงให้ดีขึ้น
Rick James

2
@RickJames ฉันเห็นด้วยกับความคิดเห็นของคุณ ในช่วงหลายปีที่ผ่านมาฉันได้เห็นการทำงานเพื่อการสลายตัวสำหรับบางคนและล้มเหลว แม้ว่าจะมีชุดทักษะ SQL ที่เหมาะสม แต่ก็สามารถทำงานกับคุณได้หากการแยกย่อยการเข้าร่วมไม่ถูกต้อง ที่นายจ้างปัจจุบันของฉันหลายฝ่ายชอบที่จะปรับขนาดขึ้นและออกโดยเฉพาะอย่างยิ่งเมื่อมีการเกี่ยวข้องกับรหัสดั้งเดิมและมีกระเป๋าลึก ๆ สำหรับผู้ที่มีรสชาติคาเวียร์ แต่งบประมาณสลัดไข่การเข้าร่วมการสลายตัวอาจคุ้มค่ากับความเสี่ยง แต่ต้องทำถูกต้อง
RolandoMySQLDBA

ฉันชอบที่จะดูว่ามันทำงานอย่างไรในสภาพแวดล้อมของ Oracle ถ้าฉันมีสิทธิ์และเวลา
Rick Henderson

อีกวิธีหนึ่งที่เร็วกว่าก็คือหากคุณกำลังสั่งซื้อมันจะเป็นการคำนวณโดยรวมที่น้อยลงในการสั่งซื้อรายการที่เล็กกว่าการสั่งซื้อรายการขนาดใหญ่หนึ่งรายการ
Evan Siroky

24

ในPostgres (และอาจเป็น RDBMS ในระดับที่ใกล้เคียงกัน, MySQL ในระดับที่น้อยกว่า), การสืบค้นที่น้อยลงนั้นเกือบจะเร็วกว่ามากเสมอ

ค่าใช้จ่ายในการแยกวิเคราะห์และการวางแผนหลายแบบสอบถามมีอยู่แล้วมากกว่ากำไรใด ๆ ที่เป็นไปได้ในกรณีส่วนใหญ่

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

คุณถ่ายโอนข้อมูลเพิ่มเติมระหว่างเซิร์ฟเวอร์ DB และไคลเอนต์ สิ่งนี้อาจเล็กน้อยสำหรับมือที่เต็มไปด้วยคุณค่าหรือสร้างความแตกต่างอย่างมาก

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

เวลาแฝงของเครือข่ายเพียงอย่างเดียวอาจใช้เวลานานกว่าส่วนที่เหลือทั้งหมดตามคำสั่งของขนาดทั้งนี้ขึ้นอยู่กับการตั้งค่าของคุณ

คำถามที่เกี่ยวข้องกับ SO:

อาจมีจุดเปลี่ยนสำหรับคิวรีที่ยาวและใหญ่มากเนื่องจากทรานแซคชันจะรวบรวมล็อกแถว DB ในระหว่างทาง คำสั่งที่ใหญ่มากอาจจะล็อคจำนวนมากสำหรับการขยายระยะเวลาซึ่งอาจทำให้เกิดแรงเสียดทานที่มีคำสั่งพร้อมกัน


แค่อยากรู้อยากเห็นอะไรที่คุณคิดว่าใหญ่มาก ๆ ?
Sablefoste

@Sablefoste: ขึ้นอยู่กับรูปแบบการเข้าถึงของคุณเป็นอย่างมาก จุดสำคัญคือที่การทำธุรกรรมที่เกิดขึ้นพร้อมกันเริ่มเข้าคิวรอการปลดล็อกหรือถ้าคุณสะสมการล็อคเพียงพอที่จะกินทรัพยากรของคุณ หรือถ้าคำค้นหาของคุณใช้เวลานานพอที่จะเข้าไปยุ่งเกี่ยวกับ autovacuum ...
Erwin Brandstetter

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

1
@JustAMartin: ฟังดูเหมือนเป็นประเภทของแบบสอบถามที่เกือบจะเร็วกว่าแน่นอนเมื่อจัดการโดยเครื่องมือวางแผนแบบสอบถามของ RDBMS - สมมติว่ามีแบบสอบถามที่ถูกต้อง เกี่ยวกับreturns lots of redundant data for "parent" table: ทำไมคุณต้องส่งคืนข้อมูลที่ซ้ำซ้อน ส่งคืนข้อมูลที่คุณต้องการเท่านั้น
Erwin Brandstetter

1
ด้วย outer join RDBMS ส่งคืนข้อมูลจากตารางพาเรนต์ที่ทำซ้ำสำหรับเด็กที่เข้าร่วมทุกคนซึ่งหมายถึงค่าใช้จ่ายเครือข่ายและหน่วยความจำบางส่วนจากนั้นแยกวิเคราะห์เพิ่มเติมในเครื่องมือ ORM เพื่อทิ้งค่าพาเรนต์ที่ซ้ำกัน ดังนั้นด้วยการค้นหาเพียงครั้งเดียวเราจึงประหยัดงานที่มีประสิทธิภาพของการวางแผนแบบสอบถาม RDBMS, การร้องขอเครือข่ายน้อยลง (หรือไปป์ภายใน) แต่สูญเสียข้อมูลเพิ่มเติมที่ไม่จำเป็นและการย้ายข้อมูลไปรอบ ๆ ในไลบรารี ORM ฉันเดาว่ามันเป็นเช่นเคย - วัดก่อนที่จะปรับให้เหมาะสม
JustAMartin
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.