วิธีใช้ดัชนีเพื่อเพิ่มความเร็วในการเรียงลำดับใน postgres


10

ฉันใช้ postgres 9.4

The messagesมีสคีมาดังต่อไปนี้: ข้อความเป็นของ feed_id และโพสต์ _at และข้อความสามารถมีข้อความหลัก (ในกรณีที่ตอบกลับ)

                    Table "public.messages"
            Column            |            Type             | Modifiers
------------------------------+-----------------------------+-----------
 message_id                   | character varying(255)      | not null
 feed_id                      | integer                     |
 parent_id                    | character varying(255)      |
 posted_at                    | timestamp without time zone |
 share_count                  | integer                     |
Indexes:
    "messages_pkey" PRIMARY KEY, btree (message_id)
    "index_messages_on_feed_id_posted_at" btree (feed_id, posted_at DESC NULLS LAST)

ฉันต้องการส่งคืนข้อความทั้งหมดที่สั่งซื้อโดยshare_countแต่สำหรับแต่ละข้อความparent_idฉันต้องการส่งคืนข้อความเดียวเท่านั้น กล่าวคือหากข้อความหลายข้อความมีข้อความเหมือนกันระบบจะส่งกลับparent_idเฉพาะข้อความล่าสุด ( posted_at) เท่านั้น parent_idสามารถเป็นโมฆะข้อความที่มี null parent_idควรทั้งหมดกลับมา

แบบสอบถามที่ฉันใช้คือ:

WITH filtered_messages AS (SELECT * 
                           FROM messages
                           WHERE feed_id IN (7) 
                           AND (posted_at >= '2015-01-01 04:00:00.000000') 
                           AND (posted_at < '2015-04-28 04:00:00.000000'))
    SELECT *
    FROM (SELECT DISTINCT ON(COALESCE(parent_id, message_id)) parent_id,
                          message_id, 
                          posted_at, 
                          share_count
          FROM filtered_messages
          ORDER BY COALESCE(parent_id, message_id), posted_at DESC NULLS LAST
         ) messages
    ORDER BY share_count DESC NULLS LAST, posted_at DESC NULLS LAST;

นี่คือhttp://sqlfiddle.com/#!15/588e5/1/0ใน SQL Fiddle ฉันได้กำหนดสคีมาแบบสอบถามที่แน่นอนและผลลัพธ์ที่คาดหวัง

แต่ประสิทธิภาพของแบบสอบถามช้าเมื่อตารางข้อความมีขนาดใหญ่ ฉันพยายามเพิ่มดัชนีการเรียงลำดับหลายรายการ แต่ดูเหมือนจะไม่ใช้ดัชนี นี่คือคำอธิบาย: http://explain.depesz.com/s/Sv2

ฉันจะสร้างดัชนีที่ถูกต้องได้อย่างไร


ได้อย่างรวดเร็วก่อนที่ORDER BYในแบบสอบถามย่อยจะไร้ประโยชน์โดยสิ้นเชิง นอกจากนี้แผนเชื่อมโยงไม่สามารถเป็นผลมาจากแบบสอบถามที่โพสต์ - ไม่มีการกล่าวถึงmetadataเช่น
dezso

คำอธิบายของคุณไม่ครอบคลุมถึงบทบาทของfeed_idและposted_atคุณไม่ได้พูดถึงmetadataเลยซึ่งดูเหมือนจะเป็นประเภท JSON? โปรดซ่อมแซมคำถามของคุณเพื่อให้สอดคล้องกัน คุณเลือก> 500k rows ใน CTE ... มีกี่แถวในตาราง? เปอร์เซ็นต์ของแถวที่คุณเลือกใน CTE เปอร์เซ็นต์ของแถวมีparent_id IS NULLอะไรบ้าง พิจารณาข้อมูลในแท็ก[postgresql-performance]สำหรับคำถามเกี่ยวกับประสิทธิภาพ
Erwin Brandstetter

ยังมีความสำคัญ: วิธีการหลายแถวสำหรับแต่ละparent_id? (นาที / เฉลี่ย / สูงสุด)
Erwin Brandstetter

ขอโทษผมก็พยายามที่จะทำให้คำถามที่ชัดเจนมากขึ้นโดยการลดบางส่วนของคอลัมน์ SHARE_COUNT เป็นจริงใน metadatahstore ขณะนี้ตารางข้อความมีข้อมูล 10 ล้าน แต่เพิ่มขึ้นอย่างรวดเร็ว ฉันคิดว่าจะแยกเป็นตารางพาร์ติชันสำหรับแต่ละ feed_id เนื่องจากฉันดึงข้อมูลตามรหัสฟีดเท่านั้น เปอร์เซ็นต์ของ parent_id null และไม่เป็นโมฆะคือประมาณ 60% / 40% การดึงข้อมูลทั่วไปอยู่ที่ประมาณ 1-2% ของตาราง (ประมาณข้อความ 100K) ประสิทธิภาพสำหรับ 100K อยู่ที่ประมาณ 1s แต่เมื่อถึง 500K + จะใช้ดัชนีบิตแมปและใช้เวลาปกติ 10 วินาที
Zhaohan Weng

คำตอบ:


9

สอบถาม

แบบสอบถามนี้ควรจะเร็วขึ้นอย่างมากในทุกกรณี:

SELECT parent_id, message_id, posted_at, share_count
FROM   messages
WHERE  feed_id = 7
AND    posted_at >= '2015-01-01 4:0:0'
AND    posted_at <  '2015-04-28 4:0:0'
AND    parent_id IS NULL  -- match index condition
UNION ALL
(
SELECT DISTINCT ON(parent_id)
       parent_id, message_id, posted_at, share_count
FROM   messages
WHERE  feed_id = 7
AND    posted_at >= '2015-01-01 4:0:0'
AND    posted_at <  '2015-04-28 4:0:0'
AND    parent_id IS NOT NULL  -- match index condition
ORDER  BY parent_id, posted_at DESC NULLS LAST
)
ORDER  BY share_count DESC NULLS LAST, posted_at DESC NULLS LAST;
  • CTE ไม่ได้ทำอะไรที่นี่ที่แบบสอบถามย่อยธรรมดาไม่สามารถส่งมอบได้ และ CTE แนะนำสิ่งกีดขวางการปรับให้เหมาะสมเนื่องจากมันถูกดำเนินการแยกจากกันและผลลัพธ์ของมันจะปรากฏขึ้น

  • คุณมีหนึ่งแบบสอบถามย่อยอีกระดับหนึ่งเกินกว่าที่คุณต้องการจริงๆ

  • นิพจน์(COALESCE(parent_id, message_id)เข้ากันไม่ได้กับดัชนีธรรมดาคุณจะต้องมีดัชนีในนิพจน์นั้น แต่นั่นอาจไม่เป็นประโยชน์อย่างใดอย่างหนึ่งขึ้นอยู่กับการกระจายข้อมูล ตามลิงค์ของฉันด้านล่างสำหรับข้อมูลรายละเอียด

  • การแบ่งกรณีอย่างง่ายของparent_id IS NULLการแยกSELECTอาจจะหรืออาจส่งมอบที่ไม่เหมาะสม โดยเฉพาะอย่างยิ่งไม่หากเป็นกรณีที่หายากอยู่แล้วซึ่งในกรณีนี้แบบสอบถามแบบรวมที่มีดัชนี(COALESCE(parent_id, message_id)อาจทำงานได้ดีขึ้น ข้อควรพิจารณาอื่น ๆ มีผล ...

ดัชนี

โดยเฉพาะอย่างยิ่งเมื่อได้รับการสนับสนุนด้วยดัชนีเหล่านี้:

CREATE INDEX messages_idx_null ON messages (
  feed_id
, posted_at DESC NULLS LAST
, share_count DESC NULLS LAST
, parent_id, message_id
)
WHERE parent_id IS NULL;

CREATE INDEX messages_idx_notnull ON messages (
  feed_id
, posted_at DESC NULLS LAST
, share_count DESC NULLS LAST
, parent_id, message_id
)
WHERE parent_id IS NOT NULL;

ดัชนีบางส่วนทั้งสองครอบคลุมทั้งตารางเข้าด้วยกันและมีขนาดใกล้เคียงกันรวมเป็นดัชนีเดียวทั้งหมด

คอลัมน์ที่สองที่ผ่านมาparent_id, message_idเพียงทำให้รู้สึกว่าคุณจะได้รับดัชนีเพียงสแกนออกมาจากมัน อื่นลบออกจากดัชนีทั้งสอง

ซอ Fiddle

DISTINCT ONอาจมีหรือไม่มีเทคนิคการสืบค้นที่ดีที่สุดสำหรับวัตถุประสงค์ทั้งนี้ขึ้นอยู่กับรายละเอียดที่ขาดหายไป เปิดคำอธิบายโดยละเอียดใหม่ที่นี่:

และอาจเป็นทางเลือกที่เร็วกว่าที่นี่:

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