PostgreSQL Quét tuần tự thay vì Quét chỉ mục Tại sao?


11

Xin chào Tất cả tôi đã gặp sự cố với truy vấn cơ sở dữ liệu PostgreQuery của mình và tự hỏi liệu có ai có thể giúp đỡ không. Trong một số trường hợp, truy vấn của tôi dường như bỏ qua chỉ mục mà tôi đã tạo được sử dụng để nối hai bảng datadata_area. Khi điều này xảy ra, nó sử dụng quét tuần tự và dẫn đến một truy vấn chậm hơn nhiều.

Quét tuần tự (~ 5 phút)

Unique  (cost=15368261.82..15369053.96 rows=200 width=1942) (actual time=301266.832..301346.936 rows=153812 loops=1)
   CTE data
     ->  Bitmap Heap Scan on data  (cost=6086.77..610089.54 rows=321976 width=297) (actual time=26.286..197.625 rows=335130 loops=1)
           Recheck Cond: (datasetid = 1)
           Filter: ((readingdatetime >= '1920-01-01 00:00:00'::timestamp without time zone) AND (readingdatetime <= '2013-03-11 00:00:00'::timestamp without time zone) AND (depth >= 0::double precision) AND (depth <= 99999::double precision))
           ->  Bitmap Index Scan on data_datasetid_index  (cost=0.00..6006.27 rows=324789 width=0) (actual time=25.462..25.462 rows=335130 loops=1)
                 Index Cond: (datasetid = 1)
   ->  Sort  (cost=15368261.82..15368657.89 rows=158427 width=1942) (actual time=301266.829..301287.110 rows=155194 loops=1)
         Sort Key: data.id
         Sort Method: quicksort  Memory: 81999kB
         ->  Hash Left Join  (cost=15174943.29..15354578.91 rows=158427 width=1942) (actual time=300068.588..301052.832 rows=155194 loops=1)
               Hash Cond: (data_area.area_id = area.id)
               ->  Hash Join  (cost=15174792.93..15351854.12 rows=158427 width=684) (actual time=300066.288..300971.644 rows=155194 loops=1)
                     Hash Cond: (data.id = data_area.data_id)
                     ->  CTE Scan on data  (cost=0.00..6439.52 rows=321976 width=676) (actual time=26.290..313.842 rows=335130 loops=1)
                     ->  Hash  (cost=14857017.62..14857017.62 rows=25422025 width=8) (actual time=300028.260..300028.260 rows=26709939 loops=1)
                           Buckets: 4194304  Batches: 1  Memory Usage: 1043357kB
                           ->  Seq Scan on data_area  (cost=0.00..14857017.62 rows=25422025 width=8) (actual time=182921.056..291687.996 rows=26709939 loops=1)
                                 Filter: (area_id = ANY ('{28,29,30,31,32,33,25,26,27,18,19,20,21,12,13,14,15,16,17,34,35,1,2,3,4,5,6,22,23,24,7,8,9,10,11}'::integer[]))
               ->  Hash  (cost=108.49..108.49 rows=3349 width=1258) (actual time=2.256..2.256 rows=3349 loops=1)
                     Buckets: 1024  Batches: 1  Memory Usage: 584kB
                     ->  Seq Scan on area  (cost=0.00..108.49 rows=3349 width=1258) (actual time=0.007..0.666 rows=3349 loops=1)
 Total runtime: 301493.379 ms

Quét chỉ mục (~ 3 giây) ( trên giải thích.depesz.com )

Unique  (cost=17352256.47..17353067.50 rows=200 width=1942) (actual time=3603.303..3681.619 rows=153812 loops=1)
   CTE data
     ->  Bitmap Heap Scan on data  (cost=6284.60..619979.56 rows=332340 width=297) (actual time=26.201..262.314 rows=335130 loops=1)
           Recheck Cond: (datasetid = 1)
           Filter: ((readingdatetime >= '1920-01-01 00:00:00'::timestamp without time zone) AND (readingdatetime <= '2013-03-11 00:00:00'::timestamp without time zone) AND (depth >= 0::double precision) AND (depth <= 99999::double precision))
           ->  Bitmap Index Scan on data_datasetid_index  (cost=0.00..6201.51 rows=335354 width=0) (actual time=25.381..25.381 rows=335130 loops=1)
                 Index Cond: (datasetid = 1)
   ->  Sort  (cost=17352256.47..17352661.98 rows=162206 width=1942) (actual time=3603.302..3623.113 rows=155194 loops=1)
         Sort Key: data.id
         Sort Method: quicksort  Memory: 81999kB
         ->  Hash Left Join  (cost=1296.08..17338219.59 rows=162206 width=1942) (actual time=29.980..3375.921 rows=155194 loops=1)
               Hash Cond: (data_area.area_id = area.id)
               ->  Nested Loop  (cost=0.00..17334287.66 rows=162206 width=684) (actual time=26.903..3268.674 rows=155194 loops=1)
                     ->  CTE Scan on data  (cost=0.00..6646.80 rows=332340 width=676) (actual time=26.205..421.858 rows=335130 loops=1)
                     ->  Index Scan using data_area_pkey on data_area  (cost=0.00..52.13 rows=1 width=8) (actual time=0.006..0.008 rows=0 loops=335130)
                           Index Cond: (data_id = data.id)
                           Filter: (area_id = ANY ('{28,29,30,31,32,33,25,26,27,18,19,20,21,12,13,14,15,16,17,34,35,1,2,3,4,5,6,22,23,24,7,8,9,10,11}'::integer[]))
               ->  Hash  (cost=1254.22..1254.22 rows=3349 width=1258) (actual time=3.057..3.057 rows=3349 loops=1)
                     Buckets: 1024  Batches: 1  Memory Usage: 584kB
                     ->  Index Scan using area_primary_key on area  (cost=0.00..1254.22 rows=3349 width=1258) (actual time=0.012..1.429 rows=3349 loops=1)
 Total runtime: 3706.630 ms

Cấu trúc bảng

Đây là cấu trúc bảng cho data_areabảng. Tôi có thể cung cấp các bảng khác nếu cần.

CREATE TABLE data_area
(
  data_id integer NOT NULL,
  area_id integer NOT NULL,
  CONSTRAINT data_area_pkey PRIMARY KEY (data_id , area_id ),
  CONSTRAINT data_area_area_id_fk FOREIGN KEY (area_id)
      REFERENCES area (id) MATCH SIMPLE
      ON UPDATE NO ACTION ON DELETE NO ACTION,
  CONSTRAINT data_area_data_id_fk FOREIGN KEY (data_id)
      REFERENCES data (id) MATCH SIMPLE
      ON UPDATE CASCADE ON DELETE CASCADE
);

TRUY VẤN

WITH data AS (
    SELECT * 
    FROM data 
    WHERE 
        datasetid IN (1) 
        AND (readingdatetime BETWEEN '1920-01-01' AND '2013-03-11') 
        AND depth BETWEEN 0 AND 99999
)
SELECT * 
FROM ( 
    SELECT DISTINCT ON (data.id) data.id, * 
    FROM 
        data, 
        data_area 
        LEFT JOIN area ON area_id = area.id 
    WHERE 
        data_id = data.id 
        AND area_id IN (28,29,30,31,32,33,25,26,27,18,19,20,21,12,13,14,15,16,17,34,35,1,2,3,4,5,6,22,23,24,7,8,9,10,11) 
) as s;

Trả về 153812hàng. Đã set enable_seqscan= false;vô hiệu hóa quét tuần tự và nhận được kết quả chỉ mục.

Tôi đã thử thực hiện ANALYSEtrên cơ sở dữ liệu và tăng số liệu thống kê được thu thập trên các cột được sử dụng trong truy vấn, nhưng dường như không có gì giúp được.

Bất cứ ai có thể lan truyền và ánh sáng về điều này hoặc đề nghị bất cứ điều gì khác tôi nên thử?


Nó sẽ giúp tôi nếu bạn bao gồm các truy vấn đã tạo ra từng kế hoạch thực hiện đó.
Mike Sherrill 'Nhớ lại mèo'

Chênh lệch 2 bậc độ lớn trong số lượng hàng ước tính và số hàng thực tế? Tôi đang đọc đúng không?
Mike Sherrill 'Nhớ lại mèo'

@Catcall Đã thêm truy vấn (loại cơ bản để có thể tìm ra những gì đang xảy ra). Khi bạn tham khảo các hàng ước tính là 200 và sau đó nó thực sự trả về 153812?
Mark Davidson

2
Có, 200 so với 150k có vẻ kỳ quặc trong nháy mắt. Có một lý do thuyết phục để trộn một liên kết trái với một sản phẩm của Cartesian ( FROM data, data_area) không? Thoạt nhìn, sử dụng DISTINCT ON mà không có mệnh đề ORDER BY dường như là một ý tưởng tồi.
Mike Sherrill 'Nhớ lại mèo'

Câu trả lời:


7

Lưu ý dòng này:

->  Index Scan using data_area_pkey on data_area  (cost=0.00..52.13 rows=1 width=8) 
    (actual time=0.006..0.008 rows=0 loops=335130)

Nếu bạn tính tổng chi phí, xem xét các vòng lặp, đó là 52.3 * 335130 = 17527299. Đây là lớn hơn 14857017.62 cho sự seq_scanthay thế. Đó là lý do tại sao nó không sử dụng chỉ mục.

Vì vậy, trình tối ưu hóa đang đánh giá quá cao chi phí quét chỉ mục. Tôi đoán rằng dữ liệu của bạn được sắp xếp theo chỉ mục (do chỉ mục được nhóm hoặc do cách tải) và / hoặc bạn có nhiều bộ nhớ đệm và / hoặc một đĩa nhanh đẹp. Do đó có rất ít I / O ngẫu nhiên đang diễn ra.

Bạn cũng nên kiểm tra correlationtrong pg_stats, được sử dụng bởi tôi ưu hoa để đánh giá phân nhóm khi tính toán chi phí chỉ số, và cuối cùng hãy thử thay đổi random_page_costcpu_index_tuple_cost, để phù hợp với hệ thống của bạn.


Trừ khi tôi thiếu một cái gì đó, tôi nghĩ @jop có nghĩa là 52.13không 52.3, điều đó sẽ dẫn đến kết quả là 17470326.9 (vẫn lớn hơn seq_scan)
BotNet

2

CTE của bạn thực sự không làm gì khác sau đó 'thuê ngoài' một vài WHEREđiều kiện, hầu hết chúng trông tương đương WHERE TRUE. Vì các CTE thường đứng sau một hàng rào tối ưu hóa (có nghĩa là nó được tối ưu hóa riêng), chúng có thể giúp ích rất nhiều cho các truy vấn nhất định. Trong trường hợp này, tuy nhiên, tôi sẽ mong đợi hiệu ứng ngược lại chính xác.

Những gì tôi sẽ cố gắng là viết lại truy vấn sao cho đơn giản nhất có thể:

SELECT d.id, * 
FROM 
    data d 
    JOIN data_area da ON da.data_id = d.id
    LEFT JOIN area a ON da.area_id = a.id 
WHERE 
    d.datasetid IN (1) 
    AND da.area_id IN (28,29,30,31,32,33,25,26,27,18,19,20,21,12,13,14,15,16,17,34,35,1,2,3,4,5,6,22,23,24,7,8,9,10,11) 
    AND (readingdatetime BETWEEN '1920-01-01' AND '2013-03-11') -- this and the next condition don't do anything, I think
    AND depth BETWEEN 0 AND 99999
;

và sau đó kiểm tra xem chỉ số có được sử dụng hay không. Vẫn có khả năng là bạn không cần tất cả các cột đầu ra (ít nhất hai cột của bảng nối là không cần thiết).

Vui lòng báo cáo lại và cho chúng tôi biết bạn sử dụng phiên bản PostgreSQL nào.


Cảm ơn lời đề nghị của bạn, lời xin lỗi của tôi về phản hồi chậm trễ của tôi đối với bài đăng của bạn, tôi đã làm việc trên các dự án khác. Đề xuất của bạn thực sự có nghĩa là truy vấn dường như sử dụng chỉ mục một cách đáng tin cậy cho tất cả các truy vấn nhưng tôi vẫn không nhận được hiệu suất mà tôi mong đợi với nó. Tôi đã thực hiện phân tích về một truy vấn có nhiều dữ liệu giải thích hơn.depesz.com/s/1yu mất 4 phút với 95% thời gian dành cho việc quét INDEX.
Mark Davidson

Quên đề cập đến việc tôi đang sử dụng phiên bản 9.1.4
Mark Davidson

Về cơ bản quét chỉ mục khá nhanh, vấn đề là nó được lặp lại vài triệu lần. Bạn nhận được gì nếu bạn SET enable_nestloop=offtrước khi chạy truy vấn?
dezso

-1

Đối với những người theo dõi, tôi đã có một vấn đề tương tự như

select * from table where bigint_column between x and y and mod(bigint_column, 10000) == z

Vấn đề là bigint_column của tôi "giữa x và y" có một chỉ mục, nhưng về cơ bản truy vấn của tôi là "tất cả các hàng" trong bảng đó, vì vậy nó không sử dụng chỉ mục [vì dù sao nó cũng phải quét toàn bộ bảng] nhưng đã thực hiện quét tuần tự seq_scan. Một sửa chữa cho tôi là tạo một chỉ mục mới cho phía "mod" của phương trình, để nó có thể sử dụng nó trên một biểu thức .


downvoters cứ để lại bình luận tại sao :)
rogerdpack
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.