2010-02-25 24 views
19

Tôi đã dành hơn một giờ hôm nay, tôi đã bối rối trước một kế hoạch truy vấn mà tôi không thể hiểu được. Truy vấn là UDPATE và nó sẽ không chạy TẤT CẢ. Hoàn toàn bế tắc: pg_locks cho thấy nó cũng không chờ đợi bất cứ điều gì. Bây giờ, tôi không coi mình là người đọc kế hoạch truy vấn tốt nhất hay tệ nhất, nhưng tôi thấy điều này đặc biệt khó khăn. Tôi tự hỏi làm thế nào để đọc những? Có phương pháp luận nào để tuân theo để xác định lỗi không?Làm thế nào tôi có thể "suy nghĩ tốt hơn" khi đọc một kế hoạch truy vấn PostgreSQL? (Ví dụ đính kèm)

Tôi dự định đặt một câu hỏi khác về cách giải quyết vấn đề này, nhưng hiện tại tôi đang nói cụ thể theo số cách đọc các loại kế hoạch này. Vui lòng không trỏ đến bất kỳ hướng dẫn chung nào trừ khi nó giải quyết cụ thể vấn đề này, được đánh dấu bên dưới kế hoạch truy vấn.

          QUERY PLAN           
-------------------------------------------------------------------------------------------- 
Nested Loop Anti Join (cost=47680.88..169413.12 rows=1 width=77) 
    Join Filter: ((co.fkey_style = v.chrome_styleid) AND (co.name = o.name)) 
    -> Nested Loop (cost=5301.58..31738.10 rows=1 width=81) 
     -> Hash Join (cost=5301.58..29722.32 rows=229 width=40) 
       Hash Cond: ((io.lot_id = iv.lot_id) AND ((io.vin)::text = (iv.vin)::text)) 
       -> Seq Scan on options io (cost=0.00..20223.32 rows=23004 width=36) 
        Filter: (name IS NULL) 
       -> Hash (cost=4547.33..4547.33 rows=36150 width=24) 
        -> Seq Scan on vehicles iv (cost=0.00..4547.33 rows=36150 width=24) 
          Filter: (date_sold IS NULL) 
     -> Index Scan using options_pkey on options co (cost=0.00..8.79 rows=1 width=49) 
       Index Cond: ((co.fkey_style = iv.chrome_styleid) AND (co.code = io.code)) 
    -> Hash Join (cost=42379.30..137424.09 rows=16729 width=26) 
     Hash Cond: ((v.lot_id = o.lot_id) AND ((v.vin)::text = (o.vin)::text)) 
     -> Seq Scan on vehicles v (cost=0.00..4547.33 rows=65233 width=24) 
     -> Hash (cost=20223.32..20223.32 rows=931332 width=44) 
       -> Seq Scan on options o (cost=0.00..20223.32 rows=931332 width=44) 
(17 rows) 

Vấn đề với kế hoạch truy vấn này - Tôi tin rằng tôi hiểu được - có lẽ là tốt nhất nói bởi RhodiumToad (ông chắc chắn là tốt hơn lúc này, vì vậy tôi sẽ đặt cược vào lời giải thích của ông là tốt hơn) của irc://irc.freenode.net/#postgresql:

oh, kế hoạch đó là khả năng disasterous vấn đề với kế hoạch đó là nó đang chạy một hashjoin cực kỳ đắt đối với mỗi hàng vấn đề là các hàng = 1 ước tính từ khác tham gia và các nhà quy hoạch cho rằng đó là ok để đặt một ly truy vấn đắt tiền trong đường dẫn bên trong của một tổ hợp nơi đường dẫn bên ngoài được ước tính chỉ trả về một hàng. vì, rõ ràng, theo ước tính của người lập kế hoạch phần đắt tiền sẽ chỉ được chạy một lần nhưng điều này có xu hướng rõ ràng thực sự gây rối trong thực tế vấn đề là người lập kế hoạch tin rằng ước tính riêng của mình lý tưởng, người lập kế hoạch cần phải biết sự khác biệt giữa "ước trở 1 hàng" và "không thể trả về nhiều hơn 1 hàng" nhưng nó không phải là ở tất cả rõ ràng làm thế nào để kết hợp đó vào mã hiện

Ông tiếp tục nói:

nó có thể ảnh hưởng đến bất kỳ tham gia nào, nhưng thường tham gia chống lại subqueries là khả năng

nhất Bây giờ khi tôi đọc kế hoạch này, điều đầu tiên tôi nhận thấy là Nested Loop Anti Join, điều này đã có một chi phí 169,413 (Tôi sẽ dính vào cận trên). Anti-Join này phân tích thành kết quả của một số Nested Loop với chi phí 31,738 và kết quả là Hash Join với chi phí là 137,424. Bây giờ, số 137,424, là nhiều lớn hơn 31,738 vì vậy tôi biết sự cố là Tham gia băm. Sau đó, tôi tiếp tục đến EXPLAIN ANALYZE phân đoạn Hash Tham gia bên ngoài truy vấn. Nó được thực hiện trong 7 giây. Tôi đã đảm bảo có các chỉ mục trên (lot_id, vin) và (co.code và v.code) - đã có. Tôi đã vô hiệu hóa seq_scanhashjoin riêng lẻ và nhận thấy tốc độ tăng ít hơn 2 giây. Không đủ gần để giải thích tại sao nó không tiến triển sau một giờ.

Nhưng, sau tất cả điều này tôi hoàn toàn sai! Có, đó là phần chậm hơn của truy vấn, nhưng vì bit rows="1" (tôi cho rằng nó nằm trên Nested Loop Anti Join). Có hướng dẫn nào giúp tôi xác định các loại vấn đề này không. Ở đây nó là một lỗi (thiếu khả năng) trong kế hoạch sai ước tính số lượng hàng?Làm thế nào tôi phải đọc vào điều này để đi đến cùng một kết luận RhodiumToad đã làm?

Chỉ đơn giản là rows="1" đó là nghĩa vụ phải kích hoạt tôi tìm ra điều này?

Tôi đã chạy VACUUM FULL ANALYZE trên tất cả các bảng có liên quan và đây là Postgresql 8.4.

Trả lời

24

Thấy thông qua các vấn đề như thế này đòi hỏi một số kinh nghiệm về nơi mà mọi thứ có thể đi sai. Nhưng để tìm các vấn đề trong kế hoạch truy vấn, hãy thử xác thực kế hoạch được sản xuất từ ​​trong ra ngoài, kiểm tra xem số lượng các ước tính hàng là các ước tính lành mạnh và chi phí phù hợp với thời gian đã bỏ ra. Btw. hai ước tính chi phí không thấp hơn và giới hạn trên, đầu tiên là chi phí ước tính để tạo ra hàng đầu tiên của đầu ra, số thứ hai là tổng chi phí ước tính, xem explain documentation để biết chi tiết, cũng có sẵn một số planner documentation. Nó cũng giúp biết các phương thức truy cập khác nhau hoạt động như thế nào. Như một điểm khởi đầu Wikipedia có thông tin về nested loop, hashmerge joins.

Trong ví dụ của bạn, bạn muốn bắt đầu với:

  -> Seq Scan on options io (cost=0.00..20223.32 rows=23004 width=36) 
       Filter: (name IS NULL) 

Run EXPLAIN ANALYZE SELECT * FROM options WHERE name IS NULL; và xem nếu các hàng trả lại phù hợp với dự toán. Một yếu tố của 2 off thường không phải là một vấn đề, bạn đang cố gắng để phát hiện thứ tự của sự khác biệt độ lớn.

Sau đó, xem EXPLAIN ANALYZE SELECT * FROM vehicles WHERE date_sold IS NULL; trả về số lượng hàng dự kiến.

Sau đó đi lên một bậc để băm tham gia:

 -> Hash Join (cost=5301.58..29722.32 rows=229 width=40) 
      Hash Cond: ((io.lot_id = iv.lot_id) AND ((io.vin)::text = (iv.vin)::text)) 

Xem nếu EXPLAIN ANALYZE SELECT * FROM vehicles AS iv INNER JOIN options io ON (io.lot_id = iv.lot_id) AND ((io.vin)::text = (iv.vin)::text) WHERE iv.date_sold IS NULL AND io.name IS NULL; kết quả trong 229 dòng.

Tăng thêm một cấp độ INNER JOIN options co ON (co.fkey_style = iv.chrome_styleid) AND (co.code = io.code) và dự kiến ​​chỉ trả về một hàng. Đây có lẽ là nơi vấn đề là bởi vì nếu số thực tế của hàng đi từ 1 đến 100, tổng ước tính chi phí đi qua vòng lặp bên trong của vòng lặp lồng nhau bị tắt theo hệ số 100.

Sai lầm cơ bản kế hoạch đang tạo ra có lẽ là nó hy vọng rằng hai biến vị ngữ để tham gia vào co là độc lập với nhau và nhân các chọn lọc của chúng. Trong khi trên thực tế chúng có thể tương quan nhiều với độ chọn lọc gần với MIN (s1, s2) chứ không phải s1 * s2.

+0

Đây là một câu trả lời tuyệt vời, nhưng bạn đang nói về điều gì khi bạn nói 'tham gia vào co'? Tôi tin rằng 'RhodiumToads' giải thích vấn đề bởi vì nó có vẻ chính xác? Bạn đang giải thích điều tương tự hay điều gì đó khác? –

+0

Điều tương tự. Tham gia vào 'co' là' Chỉ mục Quét bằng cách sử dụng tùy chọn_pkey trên các tùy chọn co' nút bên trong của vòng lặp lồng nhau tham gia. Nó có hai điều kiện mà người lập kế hoạch có thể nghĩ không hợp lý sẽ dẫn đến một hàng đầu ra.Nếu bạn cố gắng chạy truy vấn đó và xem có bao nhiêu hàng nó thực sự trả về, bạn có thể xác minh xem đây có phải là trường hợp không. Ước tính xấu cho các vị từ tương quan là một vấn đề đã biết. Có một số cuộc thảo luận về điều này trên danh sách hiệu suất: http://archives.postgresql.org/pgsql-performance/2009-06/msg00055.php –

2

Bạn có ANALYZE các bảng không? Và pg_stats phải nói gì về những bảng này? Kế hoạch truy vấn dựa trên số liệu thống kê, những điều này phải ổn. Và bạn sử dụng phiên bản nào? 8,4?

Chi phí có thể được tính bằng cách sử dụng số liệu thống kê, số lượng relpages, số lượng hàng và cài đặt trong postgresql.conf cho hằng số chi phí kế hoạch.

work_mem cũng tham gia, nó có thể là quá thấp và buộc các nhà quy hoạch để làm một seqscan, giết hiệu suất ...

+0

Tôi đã chạy 'VACUUM FULL ANALYZE' trên tất cả các bảng có liên quan, và đây là Postgresql 8.4. –

Các vấn đề liên quan