Hiệu quả tối ưu hóa kỳ lạ bằng cách sử dụng xây dựng VỚI


7

Tôi có 4 bảng, hãy đặt tên chúng là:

  1. bảng A, 15M hàng
  2. bảng B, hàng 40K,
  3. bảng C, hàng 30K,
  4. 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-RDSnơi autovacuumtí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)?


5
Các biểu thức bảng ( WITHtruy 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.
Craig Ringer

2
Đã chỉnh sửa để hiển thị các kế hoạch trên giải thích.depesz.com để đọc dễ dàng hơn. Lưu ý các ước tính hàng xấu? đó là nguyên nhân của vấn đề.
Craig Ringer

Câu trả lời:


1

Từ phần giải thích, tôi có thể thấy rõ PostgreSQL tạo ra các ước tính xấu và chọn kế hoạch xấu cho truy vấn đầu tiên. Bộ chọn đẳng thức trên cột z trong bảng D có ước tính hoàn toàn sai (giảm 500 lần).

Lần thứ hai tốt hơn vì VỚI là một hàng rào kế hoạch, như Craig chỉ ra.

Hãy tập trung vào truy vấn đầu tiên.

Planner tạo ra các ước tính xấu vì số liệu thống kê còn thiếu / cũ / không đầy đủ.

Hầu như tất cả các số liệu thống kê có thể nhìn thấy trong pg_stats. Bạn nên kiểm tra quan điểm này và tốt nhất là dán các hàng có liên quan ở đây.

SELECT * FROM pg_stats WHERE tablename='d' and attname='z';

Nếu một cột có một số phân phối thống kê hài hước (không đồng nhất, không gaussian, sai lệch, giả ngẫu nhiên với các mẫu ẩn, v.v.), thì mô-đun thống kê đằng sau ANALYZE có thể không thể bắt được các quy tắc cần thiết cho trình hoạch định.

Trong nhiều trường hợp, sử dụng mẫu lớn hơn giúp tạo ra các ước tính trung thực. Có một tham số cấu hình để tăng kích thước mẫu cho ANALYZE , có thể được sử dụng như thế này:

SET default_statistics_target TO 200;
ANALYZE A;
ANALYZE B;
ANALYZE C;
SET default_statistics_target TO 1000;
ANALYZE D;

Thử nghiệm với các giá trị và thử lại các truy vấn CHỌN của bạn. Nếu nó giúp, bạn có thể tăng kích thước mẫu vĩnh viễn, sử dụng ALTER TABLE D ALTER COLUMN Z SET STATISTICS 1000.

Tái bút như mọi khi, bạn nên đảm bảo rằng cấu hình bộ nhớ / tài nguyên là chính xác. Tất cả các thiết lập trong phần "Cấu hình tài nguyên" phải được điều chỉnh theo tài nguyên máy chủ thực tế (kích thước cơ sở dữ liệu & bộ nhớ, loại mảng đột kích, ổ đĩa ssd).


Vấn đề là đây là một Cơ sở dữ liệu sản xuất, vì vậy không có khả năng thực hiện các thử nghiệm trên nó.
Andremoniy

Đó không phải là vấn đề, vì lệnh SET sẽ chỉ ảnh hưởng đến phiên hiện tại. Bạn có thể kiểm tra cài đặt mới một cách an toàn khi chạy ALTER.
filiprem
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.