Nest Loop không mong muốn so với Hash Tham gia vào PostgreSQL 9.6


13

Tôi gặp rắc rối với kế hoạch truy vấn PostgreSQL 9.6. Truy vấn của tôi trông như thế này:

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)

Tôi đã bật Bảo mật cấp hàng cho các bảng được sử dụng ở trên.

  • với set enable_nestloop = True, trình lập kế hoạch truy vấn chạy kết nối Nested Loop với tổng thời gian chạy khoảng 37 giây: https://explain.depesz.com/s/59BR

  • với set enable_nestloop = False, phương thức Hash Join được sử dụng và thời gian truy vấn khoảng 0,3 giây: https://explain.depesz.com/s/PG8E

Tôi đã làm VACUUM ANALYZEtrước khi chạy các truy vấn, nhưng nó không giúp được gì.

Tôi biết rằng nó không phải là một thực hành tốt set enable_nestloop = False, và bất kỳ tùy chọn tương tự khác cho người lập kế hoạch. Nhưng làm thế nào tôi có thể "thuyết phục" người lập kế hoạch sử dụng phép nối băm mà không vô hiệu hóa các vòng lặp lồng nhau?

Viết lại truy vấn là một tùy chọn.

Nếu tôi chạy cùng một truy vấn theo vai trò bỏ qua RLS, thì nó được thực thi rất nhanh. Chính sách bảo mật cấp hàng trông như thế này:

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
);

Bất kỳ ý tưởng hoặc đề xuất sẽ được đánh giá cao.


Chỉ hơi bối rối: tại sao có AND properties."TYPE_ID" IN (6);và không = 6;?
Vérace

2
@ Vérace while = được sử dụng rộng rãi hơn, cả hai đều được lên kế hoạch theo cùng một cách. Giả định của tôi là anh ta chơi với nhiều hơn một giá trị, hoặc một ORM là một kẻ lười biếng.
Evan Carroll

Câu trả lời:


16

Điều đang xảy ra ở đây là Nested Loop đang tắt ở một phía. Nested Loops hoạt động thực sự tốt khi một bên rất nhỏ, chẳng hạn như trả về một hàng. Trong truy vấn của bạn, trình lập kế hoạch dò dẫm ở đây và ước tính rằng Hash Tham gia sẽ trả về chỉ một hàng. Thay vào đó, Hash Tham gia (property_id = id) trả về 1.338 hàng. Điều này buộc 1.338 vòng lặp chạy ở phía bên kia của Vòng lặp Nested đã có 3,444 hàng. Đó là rất nhiều khi bạn chỉ mong đợi một (thậm chí không phải là một "vòng lặp"). Dù sao đi nữa ..

Kiểm tra thêm khi chúng tôi di chuyển xuống cho thấy Hash Tham gia thực sự bị ảnh hưởng bởi các ước tính phát sinh từ điều này,

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

PostgreSQL hy vọng rằng sẽ trả về một hàng. Nhưng nó không. Và đó thực sự là vấn đề của bạn. Vì vậy, một số tùy chọn ở đây, không liên quan đến việc lấy búa tạ và vô hiệu hóanested_loop

  • Bạn có thể thêm một hoặc hai chỉ mục propertiesđể giúp nó có khả năng bỏ qua quét seq hoàn toàn hoặc ước tính lợi nhuận tốt hơn.

    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;
  • Ngoài ra, bạn có thể di chuyển các công cụ thuộc tính sang CTE hoặc chọn phụ OFFSET 0để tạo hàng rào.

    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
    )
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.