ฉันจะบังคับให้ Postgres ใช้ดัชนีได้อย่างไรในเมื่อมันจะยืนยันในการสแกนตามลำดับ
ฉันจะบังคับให้ Postgres ใช้ดัชนีได้อย่างไรในเมื่อมันจะยืนยันในการสแกนตามลำดับ
คำตอบ:
สมมติว่าคุณกำลังถามเกี่ยวกับคุณสมบัติ "การบอกใบ้ดัชนี" ทั่วไปที่พบในฐานข้อมูลจำนวนมาก PostgreSQL ไม่มีคุณลักษณะดังกล่าว นี่เป็นการตัดสินใจอย่างมีสติของทีม PostgreSQL ภาพรวมที่ดีของเหตุผลและสิ่งที่คุณสามารถทำแทนสามารถพบได้ที่นี่ สาเหตุโดยพื้นฐานแล้วเป็นการแฮ็กประสิทธิภาพที่มีแนวโน้มที่จะทำให้เกิดปัญหามากขึ้นในภายหลังเมื่อข้อมูลของคุณเปลี่ยนแปลงในขณะที่เครื่องมือเพิ่มประสิทธิภาพของ PostgreSQL สามารถประเมินแผนใหม่ตามสถิติ กล่าวอีกนัยหนึ่งแผนการสืบค้นข้อมูลที่ดีในปัจจุบันอาจจะไม่ใช่แผนการสืบค้นที่ดีตลอดเวลาและคำแนะนำดัชนีจะบังคับใช้แผนการสืบค้นเฉพาะตลอดเวลา
ในฐานะที่เป็นค้อนทื่อมากมีประโยชน์สำหรับการทดสอบคุณสามารถใช้พารามิเตอร์enable_seqscan
and enable_indexscan
ดู:
เหล่านี้จะไม่เหมาะสำหรับใช้ในการผลิตอย่างต่อเนื่อง หากคุณมีปัญหากับการเลือกแผนแบบสอบถามคุณจะเห็นเอกสารสำหรับการติดตามการลงปัญหาประสิทธิภาพการค้นหา อย่าเพิ่งตั้งค่าenable_
พารามิเตอร์และเดินจากไป
เว้นแต่คุณจะมีเหตุผลที่ดีมากในการใช้ดัชนี Postgres อาจเป็นตัวเลือกที่ถูกต้อง ทำไม?
ดูโพสต์กลุ่มข่าวเก่านี้
อาจเป็นเหตุผลเดียวที่ถูกต้องในการใช้
set enable_seqscan=false
คือเมื่อคุณเขียนแบบสอบถามและต้องการดูอย่างรวดเร็วว่าแผนแบบสอบถามจะเป็นอย่างไรเนื่องจากมีข้อมูลจำนวนมากในตาราง หรือแน่นอนถ้าคุณต้องการยืนยันอย่างรวดเร็วว่าแบบสอบถามของคุณไม่ได้ใช้ดัชนีเพียงเพราะชุดข้อมูลมีขนาดเล็กเกินไป
set enable_seqscan=false
ใช้การสืบค้นของคุณจากนั้นเรียกใช้อย่างรวดเร็วset enable_seqscan=true
เพื่อคืนค่า postgresql ให้เป็นพฤติกรรมที่เหมาะสม (และเห็นได้ชัดว่าไม่ทำเช่นนี้ในการผลิตเฉพาะในการพัฒนาเท่านั้น!)
SET SESSION enable_seqscan=false
จะส่งผลต่อตัวเองเท่านั้น
บางครั้ง 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
your_wanted_index
มันเป็นไปได้ที่เอ็นจิ้น postgresql จะทำการสแกนลำดับ / คีย์หลักแทน สรุป - ไม่มีวิธีที่เชื่อถือได้ 100% ในการบังคับใช้ดัชนีบางอย่างสำหรับเซิร์ฟเวอร์ PostgreSql
where
เงื่อนไข แต่มีสองตารางหรือเข้าร่วมและ Postgres ไม่สามารถรับดัชนีได้
ปัญหานี้มักเกิดขึ้นเมื่อค่าใช้จ่ายโดยประมาณของการสแกนดัชนีสูงเกินไปและไม่ได้สะท้อนความเป็นจริงอย่างถูกต้อง คุณอาจต้องลด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สำหรับการแคชข้อมูลที่ชัดเจน
คำถามในตัวเองไม่ถูกต้องมาก การบังคับ (โดยทำ enable_seqscan = off เป็นต้น) เป็นความคิดที่แย่มาก อาจเป็นประโยชน์ในการตรวจสอบว่าจะเร็วขึ้นหรือไม่ แต่รหัสการผลิตไม่ควรใช้กลเม็ดดังกล่าว
แทนที่จะอธิบายการวิเคราะห์คำถามของคุณอ่านและค้นหาว่าเหตุใด PostgreSQL จึงเลือกแผนไม่ดี (ในความคิดของคุณ)
มีเครื่องมือบนเว็บที่ช่วยในการอ่านอธิบายผลการวิเคราะห์ - หนึ่งในนั้นคือExplanation.depesz.com - เขียนโดยฉัน
อีกทางเลือกหนึ่งคือเข้าร่วมช่อง #postgresql บนเครือข่ายfreenode irc และพูดคุยกับคนที่นั่นเพื่อช่วยเหลือคุณเนื่องจากการเพิ่มประสิทธิภาพการสืบค้นไม่ใช่เรื่องของการ "ถามคำถามรับคำตอบก็มีความสุข" มันเหมือนกับการสนทนามีหลายสิ่งให้ตรวจสอบหลายสิ่งที่ต้องเรียนรู้
มีเคล็ดลับในการผลักดัน postgres ให้ชอบ seqscan เพิ่มOFFSET 0
ในแบบสอบถามย่อย
สิ่งนี้มีประโยชน์สำหรับการเพิ่มประสิทธิภาพคำขอที่เชื่อมโยงตารางขนาดใหญ่ / ใหญ่เมื่อสิ่งที่คุณต้องการมีเพียง n องค์ประกอบแรก / สุดท้าย
สมมติว่าคุณกำลังมองหาองค์ประกอบ 20 รายการแรก / สุดท้ายที่เกี่ยวข้องกับหลายตารางที่มีรายการ 100k (หรือมากกว่า) ไม่มีการสร้างจุด / เชื่อมโยงแบบสอบถามทั้งหมดกับข้อมูลทั้งหมดเมื่อสิ่งที่คุณกำลังมองหาอยู่ใน 100 หรือ 1,000 แรก รายการ. ตัวอย่างเช่นในสถานการณ์นี้ดูเหมือนว่าจะทำการสแกนตามลำดับได้เร็วกว่า 10 เท่า
ดูฉันจะป้องกันไม่ให้ Postgres แทรกข้อความค้นหาย่อยได้อย่างไร