ฉันจะบังคับให้ Postgres ใช้ดัชนีเฉพาะได้อย่างไร


113

ฉันจะบังคับให้ Postgres ใช้ดัชนีได้อย่างไรในเมื่อมันจะยืนยันในการสแกนตามลำดับ



1
+1 ฉันชอบที่จะเห็นคุณลักษณะนี้ มันไม่ได้เป็นเพียงเรื่องของการปิดการใช้งานสแกน seq เป็นคำตอบอื่น ๆ กล่าวว่าเราต้องการความสามารถในการบังคับ PG ใช้ดัชนีที่เฉพาะเจาะจง เนื่องจากในสถิติคำจริงอาจผิดพลาดอย่างสิ้นเชิงและ ณ จุดนั้นคุณจำเป็นต้องใช้วิธีแก้ปัญหาที่ไม่น่าเชื่อถือ / บางส่วน ฉันยอมรับว่าในกรณีง่ายๆคุณควรตรวจสอบดัชนีและการตั้งค่าอื่น ๆ ก่อน แต่เพื่อความน่าเชื่อถือและการใช้งานขั้นสูงกับข้อมูลขนาดใหญ่เราต้องการสิ่งนี้
collimarco

MySQL และ Oracle มีทั้งคู่ ... ไม่แน่ใจว่าเหตุใดผู้วางแผนของ Postgres จึงไม่น่าเชื่อถือ
Kevin Parker

คำตอบ:


103

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

ในฐานะที่เป็นค้อนทื่อมากมีประโยชน์สำหรับการทดสอบคุณสามารถใช้พารามิเตอร์enable_seqscanand enable_indexscanดู:

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

เว้นแต่คุณจะมีเหตุผลที่ดีมากในการใช้ดัชนี Postgres อาจเป็นตัวเลือกที่ถูกต้อง ทำไม?

  • สำหรับตารางขนาดเล็กการสแกนตามลำดับจะเร็วกว่า
  • Postgres ไม่ใช้ดัชนีเมื่อประเภทข้อมูลไม่ตรงกันคุณอาจต้องใส่ Cast ที่เหมาะสม
  • การตั้งค่าการวางแผนของคุณอาจทำให้เกิดปัญหา

ดูโพสต์กลุ่มข่าวเก่านี้


4
เห็นด้วยการบังคับให้ postgres ทำในแบบของคุณมักจะหมายความว่าคุณทำผิด 9/10 เท่าของผู้วางแผนจะเอาชนะทุกสิ่งที่คุณคิดได้ อีก 1 ครั้งเป็นเพราะคุณทำผิด
Kent Fredric

ฉันคิดว่ามันเป็นความคิดที่ดีสำหรับการตรวจสอบคลาสตัวดำเนินการจริงๆของดัชนีของคุณ
metdos

2
ฉันเกลียดที่จะรื้อฟื้นคำถามเก่า ๆ แต่ฉันมักจะเห็นในเอกสารของ Postgres การอภิปรายและที่นี่ แต่มีแนวคิดทั่วไปสำหรับสิ่งที่มีคุณสมบัติสำหรับโต๊ะขนาดเล็กหรือไม่? มันเป็นอย่างเช่น 5,000 แถวหรือ 50000 เป็นต้น?
waffl

1
@waffl คุณได้พิจารณาเปรียบเทียบหรือไม่? สร้างตารางอย่างง่ายพร้อมดัชนีและฟังก์ชันประกอบสำหรับเติมขยะแบบสุ่มnแถว จากนั้นก็เริ่มมองหาที่แผนแบบสอบถามสำหรับค่าที่แตกต่างกันของn เมื่อคุณเห็นมันเริ่มใช้ดัชนีคุณควรมีคำตอบจากสนามเบสบอล คุณยังสามารถรับการสแกนตามลำดับได้หาก PostgreSQL กำหนด (ตามสถิติ) ว่าการสแกนดัชนีจะไม่กำจัดแถวจำนวนมากเช่นกัน ดังนั้นการเปรียบเทียบจึงเป็นความคิดที่ดีเสมอเมื่อคุณมีข้อกังวลเกี่ยวกับประสิทธิภาพที่แท้จริง ในฐานะที่เป็นมืออาชีพการคาดเดาเล็กน้อยฉันว่าสองพันมักจะ "เล็ก"
jpmc26

11
ด้วยประสบการณ์กว่า 30 ปีบนแพลตฟอร์มเช่น Oracle, Teradata และ MSSQL ฉันพบว่าเครื่องมือเพิ่มประสิทธิภาพของ PostgreSQL 10 ไม่ได้ฉลาดเป็นพิเศษ แม้จะมีสถิติที่ทันสมัย ​​แต่ก็ยังสร้างแผนการดำเนินการที่มีประสิทธิภาพน้อยกว่าการบังคับในทิศทางพิเศษ การให้คำแนะนำเชิงโครงสร้างเพื่อชดเชยปัญหาเหล่านี้จะช่วยให้ PostgreSQL เติบโตในกลุ่มตลาดได้มากขึ้น IMHO
Guido Leenders

75

อาจเป็นเหตุผลเดียวที่ถูกต้องในการใช้

set enable_seqscan=false

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


41
คำตอบสั้น ๆ นี้ให้คำแนะนำที่ดีสำหรับวัตถุประสงค์ในการทดสอบ
อยู่ที่

3
ไม่มีใครตอบคำถาม!
Ivailo Bardarov

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

เคล็ดลับที่ดีในการทดสอบ: เรียกใช้เรียกset enable_seqscan=falseใช้การสืบค้นของคุณจากนั้นเรียกใช้อย่างรวดเร็วset enable_seqscan=trueเพื่อคืนค่า postgresql ให้เป็นพฤติกรรมที่เหมาะสม (และเห็นได้ชัดว่าไม่ทำเช่นนี้ในการผลิตเฉพาะในการพัฒนาเท่านั้น!)
Brian Hellekin

3
@BrianHellekin ดีกว่าที่SET SESSION enable_seqscan=falseจะส่งผลต่อตัวเองเท่านั้น
Izkata

20

บางครั้ง PostgreSQL ไม่สามารถเลือกดัชนีที่ดีที่สุดสำหรับเงื่อนไขเฉพาะได้ ตัวอย่างเช่นสมมติว่ามีตารางธุรกรรมที่มีแถวหลายล้านแถวซึ่งมีหลายร้อยรายการในแต่ละวันและตารางมีดัชนีสี่ดัชนี ได้แก่ transaction_id, client_id, วันที่และคำอธิบาย คุณต้องการเรียกใช้แบบสอบถามต่อไปนี้:

SELECT client_id, SUM(amount)
FROM transactions
WHERE date >= 'yesterday'::timestamp AND date < 'today'::timestamp AND
      description = 'Refund'
GROUP BY client_id

PostgreSQL อาจเลือกใช้ index transaction_description_idx แทน transaction_date_idx ซึ่งอาจทำให้การสืบค้นใช้เวลาหลายนาทีแทนที่จะน้อยกว่าหนึ่งวินาที หากเป็นกรณีนี้คุณสามารถบังคับใช้ดัชนีในวันที่ได้โดยทำเงื่อนไขเช่นนี้:

SELECT client_id, SUM(amount)
FROM transactions
WHERE date >= 'yesterday'::timestamp AND date < 'today'::timestamp AND
      description||'' = 'Refund'
GROUP BY client_id

3
ความคิดดี. อย่างไรก็ตามเมื่อเราปิดใช้งานการใช้ดัชนีปัจจุบันด้วยวิธีนี้ - postgresql query optimizer ทางเลือกไปยังดัชนีที่เหมาะสมถัดไป ดังนั้นจึงไม่รับประกันว่าเครื่องมือเพิ่มประสิทธิภาพจะเลือกyour_wanted_indexมันเป็นไปได้ที่เอ็นจิ้น postgresql จะทำการสแกนลำดับ / คีย์หลักแทน สรุป - ไม่มีวิธีที่เชื่อถือได้ 100% ในการบังคับใช้ดัชนีบางอย่างสำหรับเซิร์ฟเวอร์ PostgreSql
Agnius Vasiliauskas

จะเกิดอะไรขึ้นถ้าไม่มีwhereเงื่อนไข แต่มีสองตารางหรือเข้าร่วมและ Postgres ไม่สามารถรับดัชนีได้
Luna Lovegood

@Surya ข้างต้นใช้กับทั้ง WHERE และกับ JOIN ... ON เงื่อนไข
Ziggy Crueltyfree Zeitgeister

18

คำตอบสั้น ๆ

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

การลดค่านี้ [... ] จะทำให้ระบบชอบสแกนดัชนี; การเพิ่มจะทำให้การสแกนดัชนีดูมีราคาแพงกว่า

คุณตรวจสอบได้ว่าค่าที่ต่ำกว่าจะทำให้ Postgres ใช้ดัชนีได้จริงหรือไม่ (แต่ใช้สำหรับการทดสอบเท่านั้น ):

EXPLAIN <query>;              # Uses sequential scan
SET random_page_cost = 1;
EXPLAIN <query>;              # May use index scan now

คุณสามารถคืนค่าเริ่มต้นด้วยSET random_page_cost = DEFAULT;อีกครั้ง

พื้นหลัง

การสแกนดัชนีต้องการการดึงข้อมูลเพจดิสก์ที่ไม่ใช่ลำดับ Postgres ใช้random_page_costในการประมาณค่าใช้จ่ายของการดึงข้อมูลที่ไม่ใช่ลำดับที่สัมพันธ์กับการดึงข้อมูลตามลำดับ ค่าดีฟอลต์คือ4.0สมมติว่ามีค่าใช้จ่ายเฉลี่ย 4 เมื่อเทียบกับการดึงข้อมูลตามลำดับ (โดยคำนึงถึงเอฟเฟกต์การแคช)

อย่างไรก็ตามปัญหาคือค่าเริ่มต้นนี้ไม่เหมาะสมในสถานการณ์จริงที่สำคัญต่อไปนี้:

1) โซลิดสเตทไดรฟ์

ตามที่เอกสารยอมรับ:

random_page_costการจัดเก็บข้อมูลที่มีค่าใช้จ่ายต่ำสุ่มอ่านเทียบกับลำดับเช่นไดรฟ์ของรัฐที่มั่นคงอาจจะเป็นแบบอย่างที่ดีกว่าที่มีมูลค่าต่ำกว่าสำหรับ

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

2) ข้อมูลแคช

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

ในทำนองเดียวกันหากข้อมูลของคุณน่าจะอยู่ในแคชอย่างสมบูรณ์การลด [... ] random_page_costก็เหมาะสม

ปัญหาคือแน่นอนว่าคุณไม่สามารถรู้ได้อย่างง่ายดายว่าข้อมูลที่เกี่ยวข้องถูกแคชไว้แล้วหรือไม่ อย่างไรก็ตามหากมีการสอบถามดัชนีเฉพาะบ่อยครั้งและหากระบบมี RAM เพียงพอข้อมูลก็น่าจะถูกแคชและrandom_page_costควรตั้งค่าเป็นค่าที่ต่ำกว่า คุณจะต้องทดลองกับค่าต่างๆและดูว่าอะไรเหมาะกับคุณ

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



2
ฉันต้องตั้งค่า random_page_cost = 0.1 เพื่อให้การสแกนดัชนีทำงานบนตารางขนาดใหญ่ (~ 600M แถว) ใน Pg 10.1 บน Ubuntu หากไม่มีการปรับแต่งการสแกน seq (แม้จะขนานกัน) ก็ใช้เวลา 12 นาที (โปรดทราบว่าทำการวิเคราะห์ตารางแล้ว!) ไดรฟ์คือ SSD หลังจากปรับแต่งเวลาของผู้บริหารกลายเป็น 1 วินาที
Anatoly Alekseev

คุณช่วยวันของฉัน ฉันแทบจะบ้าที่พยายามคิดว่าแบบสอบถามเดียวกันบนฐานข้อมูลเดียวกันใช้เวลา 30 วินาทีในเครื่องหนึ่งเครื่องและน้อยกว่า 1 ในอีกเครื่องหนึ่งแม้ว่าจะทำการวิเคราะห์ทั้งสองด้านแล้วก็ตาม ... ALTER SYSTEM SET random_page_cost = x 'ตั้งค่าเริ่มต้นใหม่ทั่วโลก
Julien

10

คำถามในตัวเองไม่ถูกต้องมาก การบังคับ (โดยทำ enable_seqscan = off เป็นต้น) เป็นความคิดที่แย่มาก อาจเป็นประโยชน์ในการตรวจสอบว่าจะเร็วขึ้นหรือไม่ แต่รหัสการผลิตไม่ควรใช้กลเม็ดดังกล่าว

แทนที่จะอธิบายการวิเคราะห์คำถามของคุณอ่านและค้นหาว่าเหตุใด PostgreSQL จึงเลือกแผนไม่ดี (ในความคิดของคุณ)

มีเครื่องมือบนเว็บที่ช่วยในการอ่านอธิบายผลการวิเคราะห์ - หนึ่งในนั้นคือExplanation.depesz.com - เขียนโดยฉัน

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


2

มีเคล็ดลับในการผลักดัน postgres ให้ชอบ seqscan เพิ่มOFFSET 0ในแบบสอบถามย่อย

สิ่งนี้มีประโยชน์สำหรับการเพิ่มประสิทธิภาพคำขอที่เชื่อมโยงตารางขนาดใหญ่ / ใหญ่เมื่อสิ่งที่คุณต้องการมีเพียง n องค์ประกอบแรก / สุดท้าย

สมมติว่าคุณกำลังมองหาองค์ประกอบ 20 รายการแรก / สุดท้ายที่เกี่ยวข้องกับหลายตารางที่มีรายการ 100k (หรือมากกว่า) ไม่มีการสร้างจุด / เชื่อมโยงแบบสอบถามทั้งหมดกับข้อมูลทั้งหมดเมื่อสิ่งที่คุณกำลังมองหาอยู่ใน 100 หรือ 1,000 แรก รายการ. ตัวอย่างเช่นในสถานการณ์นี้ดูเหมือนว่าจะทำการสแกนตามลำดับได้เร็วกว่า 10 เท่า

ดูฉันจะป้องกันไม่ให้ Postgres แทรกข้อความค้นหาย่อยได้อย่างไร


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