Tôi có 4 bảng, hãy đặt tên chúng là:
- bảng A, 15M hàng
- bảng B, hàng 40K,
- bảng C, hàng 30K,
- bảng D, 25M hàng
(kk - có nghĩa là hàng triệu)
và tôi đã có một truy vấn cũ, được xây dựng như thế này:
select C.<some_fields>,B.<some_fields>,D.<some_fields> from C
inner join A on C.x = A.x
inner join D on D.z = 123 and D.a_id = A.a_id
inner join B on C.x = B.x and B.z = 123
where A.type = 'Xxx'
Truy vấn này cực kỳ chậm, mất tới 3 phút để thực hiện kết quả (đối với các trường hợp cụ thể, nó trả về 35k hàng).
Nhưng khi tôi thay đổi nó thành cấu trúc sau:
with t as (
select C.<some_fields>,D.<some_fields> from C
inner join A on C.x = A.x
inner join D on D.z = 123 and D.a_id = A.a_id
where A.type = 'Xxx'
)
select t.*, B.<some_fields>,
inner join B on t.x = B.x and B.z = 123
Nó bắt đầu hoạt động nhanh hơn 30 lần (tức là phải mất tới 6 giây để lấy lại kết quả tương tự).
Giả sử, các chỉ mục được xây dựng đúng. Và ý tưởng của tôi để thực hiện thủ thuật như vậy đã xuất hiện khi tôi nhận thấy rằng khối này, mà tôi đã đưa vào with ( ... )
hoạt động rất nhanh (và nó trả về lượng dữ liệu rất giống như toàn bộ truy vấn).
Vì vậy, câu hỏi của tôi là: những gì có thể là lý do? Tại sao Postgres không thể xây dựng kế hoạch phù hợp hoặc thực hiện cùng một thủ thuật trong nội bộ?
CẬP NHẬT :
Kế hoạch thực hiện cho truy vấn cũ :
Nested Loop (cost=1.83..1672.82 rows=1 width=54) (actual time=8.178..91515.625 rows=37373 loops=1)
-> Nested Loop (cost=1.42..1671.47 rows=1 width=62) (actual time=8.108..90883.567 rows=37373 loops=1)
Join Filter: (a.x = b.x)
Rows Removed by Join Filter: 9132436
-> Index Scan using b_pkey on B b (cost=0.41..8.43 rows=1 width=71) (actual time=0.022..0.782 rows=241 loops=1)
Index Cond: (z = 123)
-> Nested Loop (cost=1.00..1660.48 rows=146 width=149) (actual time=0.027..363.227 rows=38049 loops=241)
-> Index Only Scan using idx_1 on D d (cost=0.56..424.59 rows=146 width=8) (actual time=0.017..50.869 rows=64176 loops=241)
Index Cond: (z = 123)
Heap Fetches: 15564503
-> Index Scan using a_pkey on A a (cost=0.44..8.46 rows=1 width=149) (actual time=0.003..0.004 rows=1 loops=15466416)
Index Cond: (a_id = d.a_id)
-> Index Scan using c_pkey on C c (cost=0.41..1.08 rows=1 width=8) (actual time=0.005..0.007 rows=1 loops=37373)
Index Cond: (x = a.x)
Filter: ((type)::text = 'Xxx')
Planning time: 3.468 ms
Execution time: 91541.019 ms
Kế hoạch thực hiện cho truy vấn mới :
Hash Join (cost=1828.09..1830.28 rows=1 width=94) (actual time=0.654..1130.542 rows=37376 loops=1)
Hash Cond: (t.x = b.x)
CTE t
-> Nested Loop (cost=1.42..1819.64 rows=81 width=158) (actual time=0.060..761.058 rows=38052 loops=1)
-> Nested Loop (cost=1.00..1660.48 rows=146 width=149) (actual time=0.039..461.235 rows=38052 loops=1)
-> Index Only Scan using idx_1 on D d (cost=0.56..424.59 rows=146 width=8) (actual time=0.024..73.972 rows=64179 loops=1)
Index Cond: (z = 123)
Heap Fetches: 64586
-> Index Scan using a_pkey on A a (cost=0.44..8.46 rows=1 width=149) (actual time=0.004..0.004 rows=1 loops=64179)
Index Cond: (a_id = d.a_id)
-> Index Scan using c_pkey on C c (cost=0.41..1.07 rows=1 width=17) (actual time=0.004..0.005 rows=1 loops=38052)
Index Cond: (x = a.x)
Filter: ((type)::text = 'Xxx')
-> CTE Scan on t (cost=0.00..1.62 rows=81 width=104) (actual time=0.063..854.405 rows=38052 loops=1)
-> Hash (cost=8.43..8.43 rows=1 width=71) (actual time=0.353..0.353 rows=241 loops=1)
Buckets: 1024 Batches: 1 Memory Usage: 34kB
-> Index Scan using b_pkey on B b (cost=0.41..8.43 rows=1 width=71) (actual time=0.012..0.262 rows=241 loops=1)
Index Cond: (z = 123)
Planning time: 1.221 ms
Execution time: 1147.267 ms
CẬP NHẬT-2 :
Gần đây các nhà bình luận thân yêu đã nhận thấy rằng vấn đề này là do ước tính xấu về số hàng và họ đề nghị tôi làm vacuum analyze
. Nhưng tôi đang chạy máy chủ này ở Amazon-RDS
nơi autovacuum
tính năng được bật. Ngoài ra, tôi đã cố chạy tập lệnh để hiển thị các bảng đủ điều kiện cho chân không, được đề xuất trong Tài liệu RDS của Amazon và nó hiển thị cho tôi 0 bảng đủ điều kiện cho chân không.
CẬP NHẬT-3 : Việc thực hiện ANALYZE
, được đề xuất trong các bài bình luận, đã không thay đổi các ước tính hàng xấu trong các kế hoạch, nhưng tăng tốc độ của biến thể truy vấn "cũ". Tôi vẫn chưa có sự hiểu biết đầy đủ về câu hỏi cốt lõi của mình: tại sao loại truy vấn thứ hai có tốc độ cao hơn đáng kể (thậm chí w / o ANALYZE)?
WITH
truy vấn) phổ biến trong PostgreSQL là hàng rào tối ưu hóa , đó là lý do tại sao nó không được làm phẳng về dạng ban đầu. Có vẻ như bạn có vấn đề ước tính theo thứ tự tham gia, điều này có thể có nghĩa là vấn đề thống kê hoặc một cái gì đó giống như các trường có tương quan cao dẫn đến ước tính xấu. Bạn chưa cung cấp kế hoạch truy vấn, vì vậy thật khó để nói thêm.