Tại sao Array_agg () chậm hơn hàm tạo ARRAY () không tổng hợp?


13

Tôi vừa xem lại một số mã cũ được viết cho trước PostgreSQL 8.4 và tôi đã thấy một cái gì đó thực sự tiện lợi. Tôi nhớ có một chức năng tùy chỉnh làm một số điều này trở lại trong ngày, nhưng tôi quên mất những gì array_agg()trông giống như trước . Để xem xét, tổng hợp hiện đại được viết như thế này.

SELECT array_agg(x ORDER BY x DESC) FROM foobar;

Tuy nhiên, ngày xửa ngày xưa, nó được viết như thế này,

SELECT ARRAY(SELECT x FROM foobar ORDER BY x DESC);

Vì vậy, tôi đã thử nó với một số dữ liệu thử nghiệm ..

CREATE TEMP TABLE foobar AS
SELECT * FROM generate_series(1,1e7)
  AS t(x);

Kết quả thật đáng ngạc nhiên .. Cách #OldSchoolCool đã nhanh hơn rất nhiều: tăng tốc 25%. Hơn nữa, đơn giản hóa nó mà không cần ĐẶT HÀNG, cho thấy sự chậm chạp tương tự.

# EXPLAIN ANALYZE SELECT ARRAY(SELECT x FROM foobar);
                                                         QUERY PLAN                                                          
-----------------------------------------------------------------------------------------------------------------------------
 Result  (cost=104425.28..104425.29 rows=1 width=0) (actual time=1665.948..1665.949 rows=1 loops=1)
   InitPlan 1 (returns $0)
     ->  Seq Scan on foobar  (cost=0.00..104425.28 rows=6017728 width=32) (actual time=0.032..716.793 rows=10000000 loops=1)
 Planning time: 0.068 ms
 Execution time: 1671.482 ms
(5 rows)

test=# EXPLAIN ANALYZE SELECT array_agg(x) FROM foobar;
                                                        QUERY PLAN                                                         
---------------------------------------------------------------------------------------------------------------------------
 Aggregate  (cost=119469.60..119469.61 rows=1 width=32) (actual time=2155.154..2155.154 rows=1 loops=1)
   ->  Seq Scan on foobar  (cost=0.00..104425.28 rows=6017728 width=32) (actual time=0.031..717.831 rows=10000000 loops=1)
 Planning time: 0.054 ms
 Execution time: 2174.753 ms
(4 rows)

Vì vậy, những gì đang xảy ra ở đây. Tại sao Array_agg , một hàm nội bộ chậm hơn nhiều so với SQL voodoo của trình hoạch định?

Sử dụng " PostgreSQL 9.5.5 trên x86_64-pc-linux-gnu, được biên dịch bởi gcc (Ubuntu 6.2.0-5ubfox12) 6.2.0 20161005, 64-bit"

Câu trả lời:


17

Không có gì "trường học cũ" hay "lỗi thời" về một nhà xây dựng ARRAY (Đó là cái gì ARRAY(SELECT x FROM foobar)). Nó hiện đại hơn bao giờ hết. Sử dụng nó để tổng hợp mảng đơn giản.

Hướng dẫn sử dụng:

Cũng có thể xây dựng một mảng từ kết quả của truy vấn con. Trong biểu mẫu này, hàm tạo mảng được viết với từ khóa ARRAYtheo sau là truy vấn con được ngoặc đơn (không được đặt trong ngoặc).

Các tổng hợparray_agg() linh hoạt hơn nhiều ở chỗ nó có thể được tích hợp trong SELECTdanh sách với nhiều cột hơn, có thể nhiều tập hợp hơn trong cùng SELECTvà các nhóm tùy ý có thể được tạo thành GROUP BY. Trong khi một hàm tạo ARRAY chỉ có thể trả về một mảng từ một SELECTcột trả về.

Tôi đã không nghiên cứu mã nguồn, nhưng có vẻ như rõ ràng là một công cụ linh hoạt hơn nhiều cũng đắt hơn.


array_aggphải theo dõi thứ tự của các đầu vào của nó trong đó hàm ARRAYtạo dường như đang làm một cái gì đó gần tương đương với một UNIONbiểu thức bên trong. Nếu tôi phải mạo hiểm đoán, array_aggcó thể sẽ cần nhiều bộ nhớ hơn. Tôi không thể kiểm tra toàn diện điều này nhưng trên PostgreSQL 9.6 chạy trên Ubuntu 16.04, ARRAY()truy vấn có ORDER BYsử dụng hợp nhất bên ngoài và chậm hơn array_aggtruy vấn. Như bạn đã nói, việc không đọc mã câu trả lời của bạn là lời giải thích tốt nhất mà chúng tôi có.
Jeffrey

@Jeffrey: Bạn tìm thấy một trường hợp thử nghiệm nơi array_agg()nhanh hơn so với các nhà xây dựng mảng? Đối với một trường hợp đơn giản? Rất khó xảy ra, nhưng nếu vậy có lẽ là do Postgres dựa trên quyết định của mình cho một kế hoạch truy vấn về số liệu thống kê không chính xác về cài đặt chi phí. Tôi chưa bao giờ thấy array_agg()tốt hơn một nhà xây dựng mảng và tôi đã thử nghiệm nhiều lần.
Erwin Brandstetter

1
@Jeffrey: Không có hiệu ứng bộ nhớ đệm sai lệch? Bạn đã chạy từng truy vấn nhiều lần? Tôi sẽ cần phải xem định nghĩa bảng, số lượng và truy vấn chính xác để nói thêm.
Erwin Brandstetter

1
Đây không phải là một câu trả lời thực sự. Nhiều công cụ đa năng có thể thực hiện cũng như các công cụ cụ thể hơn. Nếu linh hoạt thực sự là những gì làm cho nó chậm hơn, thì tính linh hoạt của nó là gì?
Gavin Wahl

1
@Jeffrey: Có vẻ như Postgres chọn một thuật toán sắp xếp khác nhau cho mỗi biến thể (dựa trên ước tính chi phí và thống kê bảng). Và cuối cùng, việc chọn một phương thức kém hơn cho hàm tạo ARRAY, chỉ ra rằng một hoặc nhiều yếu tố trong tính toán chi phí ước tính là quá xa vời. Đây là trên một bảng tạm thời? Bạn đã làm VACUUM ANALYZEnó trước khi bạn chạy các truy vấn? Hãy xem xét: dba.stackexchange.com/a/18694/3684
Erwin Brandstetter

5

Tôi tin rằng câu trả lời được chấp nhận bởi Erwin có thể được thêm vào như sau.

Thông thường, chúng tôi đang làm việc với các bảng thông thường với các chỉ mục, thay vì các bảng tạm thời (không có chỉ mục) như trong câu hỏi ban đầu. Thật hữu ích khi lưu ý rằng các tập hợp, chẳng hạn như ARRAY_AGG, không thể tận dụng các chỉ số hiện có khi việc sắp xếp được thực hiện trong quá trình tổng hợp .

Ví dụ: giả sử truy vấn sau:

SELECT ARRAY(SELECT c FROM t ORDER BY id)

Nếu chúng ta có một chỉ mục trên t(id, ...), chỉ mục có thể được sử dụng, theo hướng quét liên tục ttheo sau là sắp xếp theo t.id. Ngoài ra, nếu cột đầu ra được bọc trong mảng (ở đây c) là một phần của chỉ mục (chẳng hạn như chỉ mục trên t(id, c)hoặc chỉ mục bao gồm trênt(id) include(c) ), thì điều này thậm chí có thể là quét chỉ mục.

Bây giờ, hãy viết lại truy vấn đó như sau:

SELECT ARRAY_AGG(c ORDER BY id) FROM t

Bây giờ, tập hợp sẽ không sử dụng chỉ mục và nó phải sắp xếp các hàng trong bộ nhớ (hoặc thậm chí tệ hơn cho các tập dữ liệu lớn, trên đĩa). Đây sẽ luôn là một lần quét liên ttiếp theo sau là tổng hợp + sắp xếp .

Theo tôi biết, điều này không được ghi lại trong tài liệu chính thức, nhưng có thể được lấy từ nguồn. Đây phải là trường hợp cho tất cả các phiên bản hiện tại, v11 bao gồm.


2
Điểm tốt. Nhưng trong tất cả các công bằng, các truy vấn có array_agg()hoặc các hàm tổng hợp tương tự vẫn có thể tận dụng các chỉ mục với một truy vấn con như : SELECT ARRAY_AGG(c) FROM (SELECT c FROM t ORDER BY id) sub. ORDER BYMệnh đề tổng hợp là những gì ngăn cản việc sử dụng chỉ mục trong ví dụ của bạn. Một hàm tạo mảng nhanh hơn array_agg()khi có thể sử dụng cùng một chỉ mục (hoặc không). Nó không linh hoạt như vậy. Xem: dba.stackexchange.com/a/213724/3684
Erwin Brandstetter

1
Phải, đó là một sự khác biệt quan trọng để thực hiện. Tôi hơi thay đổi câu trả lời của mình để làm rõ rằng nhận xét này chỉ giữ khi hàm tổng hợp phải sắp xếp. Bạn thực sự vẫn có thể kiếm được lợi nhuận từ chỉ mục trong trường hợp đơn giản, bởi vì PostgreQuery dường như đưa ra một số đảm bảo rằng việc tổng hợp sẽ diễn ra theo đúng thứ tự như được xác định trong truy vấn con, như được giải thích trong liên kết. Điều đó khá tuyệt. Mặc dù tôi tự hỏi liệu điều này có còn trong trường hợp các bảng được phân vùng và / hoặc các bảng FDW và / hoặc các nhân viên song song - và nếu PostgreQuery có thể giữ lời hứa này trong các bản phát hành trong tương lai.
pbillen

Đối với hồ sơ, tôi không có ý định nghi ngờ về câu trả lời được chấp nhận. Tôi chỉ nghĩ rằng đó là một bổ sung tốt cho lý do về sự tồn tại và việc sử dụng các chỉ số kết hợp với tổng hợp.
pbillen

1
một bổ sung tốt.
Erwin Brandstetter
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.