Nest Loop vs. Hash ที่ไม่พึงประสงค์เข้าร่วมใน PostgreSQL 9.6


13

ฉันมีปัญหากับการวางแผนการสืบค้น PostgreSQL 9.6 ข้อความค้นหาของฉันมีลักษณะเช่นนี้:

SET role plain_user;

SELECT properties.*
FROM properties
JOIN entries_properties
  ON properties.id = entries_properties.property_id
JOIN structures
  ON structures.id = entries_properties.entry_id 
WHERE structures."STRUKTURBERICHT" != ''
  AND properties."COMPOSITION" LIKE 'Mo%'
  AND (
    properties."NAME" LIKE '%VASP-ase-preopt%'
    OR properties."CALCULATOR_ID" IN (7,22,25)
  )
AND properties."TYPE_ID" IN (6)

ฉันเปิดใช้งานการรักษาความปลอดภัยระดับแถวสำหรับตารางที่ใช้งานอยู่ด้านบน

  • ด้วยset enable_nestloop = Trueตัววางแผนคิวรีรัน Nested Loop ที่รวมเวลารันทั้งหมดประมาณ 37 วินาที: https://explain.depesz.com/s/59BR

  • ด้วยset enable_nestloop = Falseวิธีการเข้าร่วมแฮชและใช้เวลาแบบสอบถามประมาณ 0.3 วินาที: https://explain.depesz.com/s/PG8E

ฉันทำVACUUM ANALYZEก่อนเรียกใช้แบบสอบถาม แต่ไม่ได้ช่วยอะไร

ฉันรู้ว่ามันไม่ใช่วิธีปฏิบัติที่ดีset enable_nestloop = Falseและตัวเลือกอื่น ๆ ที่คล้ายกันสำหรับผู้วางแผน แต่ฉันจะ "โน้มน้าว" ผู้วางแผนที่จะใช้ตัวเชื่อมแฮชโดยไม่ปิดลูปซ้อนได้อย่างไร

การเขียนซ้ำแบบสอบถามเป็นตัวเลือก

ถ้าฉันเรียกใช้คิวรีเดียวกันภายใต้บทบาทที่ข้าม RLS แล้วจะมีการดำเนินการอย่างรวดเร็วมาก นโยบายความปลอดภัยระดับแถวมีลักษณะดังนี้:

CREATE POLICY properties_select
ON properties
FOR SELECT
USING (
  (
    properties.ouid = get_current_user_id()
    AND properties.ur
  )
  OR (
    properties.ogid in (select get_current_groups_id())
    AND properties.gr
  )
  OR properties.ar
);

ความคิดหรือข้อเสนอแนะใด ๆ ที่จะได้รับการชื่นชมอย่างมาก


สับสนนิดหน่อย: ทำไมถึงมีAND properties."TYPE_ID" IN (6);และไม่เป็นเช่นนั้น= 6;?
Vérace

2
@ Véraceในขณะที่ = ใช้กันอย่างแพร่หลายมากขึ้นพวกเขาทั้งสองได้รับการวางแผนในลักษณะเดียวกัน ข้อสันนิษฐานของฉันคือเขาเล่นด้วยค่ามากกว่าหนึ่งค่าหรือ ORM กำลังขี้เกียจน้อย
Evan Carroll

คำตอบ:


15

สิ่งที่เกิดขึ้นที่นี่คือ Nested Loop เป็นวิธีปิดหนึ่งด้าน Nested Loops ทำงานได้ดีมากเมื่อด้านใดด้านหนึ่งเล็กมากเช่นคืนหนึ่งแถว ในแบบสอบถามของคุณผู้วางแผนจะเข้ามาที่นี่และประเมินว่าการเข้าร่วมแฮชจะส่งคืนแถวเดียว Hash Join (property_id = id) นั้นกลับมาที่ 1,338 แถวแทน สิ่งนี้บังคับให้ 1,338 ลูปทำงานในอีกด้านหนึ่งของ Nested Loop ซึ่งมี 3,444 แถว นั่นเป็นจำนวนมากเมื่อคุณคาดหวังเพียงหนึ่ง (ซึ่งไม่มาก "ห่วง") Anywayy ..

การตรวจสอบเพิ่มเติมในขณะที่เราเลื่อนลงไปแสดงว่า Hash เข้าร่วมถูก borked จริง ๆ โดยการประเมินที่เกิดจาก

Filter: (((properties."COMPOSITION")::text ~~ 'Mo%'::text) AND (((properties."NAME")::text ~~ '%VASP-ase-preopt%'::text) OR (properties."CALCULATOR_ID" = ANY ('{7,22,25}'::integer[]))))

PostgreSQL คาดว่าจะส่งคืนหนึ่งแถว แต่มันก็ไม่ได้ และนั่นคือปัญหาของคุณจริงๆ ดังนั้นตัวเลือกบางอย่างที่นี่ที่ไม่เกี่ยวข้องกับการเอาค้อนเลื่อนและการปิดใช้งานnested_loop

  • คุณสามารถเพิ่มดัชนีหนึ่งหรือสองดัชนีpropertiesเพื่อช่วยให้ข้ามการสแกน seq ได้ทั้งหมดหรือประเมินผลตอบแทนดีกว่า

    CREATE INDEX ON properties USING ( "TYPE_ID", "CALCULATOR_ID" );
    -- the gist_trgm_ops may or may not be needed depending on selectivity of above.
    CREATE INDEX ON properties USING GIST (
      "COMPOSITION" gist_trgm_ops,
      "NAME"        gist_trgm_ops
    );
    ANALYZE properties;
  • อีกทางหนึ่งคุณสามารถย้ายคุณสมบัติของคุณสมบัติไปที่ CTE หรือเลือกย่อยOFFSET 0ซึ่งสร้างรั้ว

    WITH t AS (
      SELECT *
      FROM properties.
      WHERE "COMPOSITION" LIKE 'Mo%'
      AND (
        "NAME" LIKE '%VASP-ase-preopt%'
        OR "CALCULATOR_ID" IN (7,22,25)
      )
      AND "TYPE_ID" IN (6)
    )
    SELECT * FROM structures
    JOIN t ON (
      structures.id = entries_properties.entry_id
    )
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.