Chỉ mục không được sử dụng, nhưng ảnh hưởng đến truy vấn


8

Tôi đã có một bảng PostgreSQL 9.3 với một số số và một số dữ liệu bổ sung:

CREATE TABLE mytable (
    myid BIGINT,
    somedata BYTEA
)

Bảng này hiện có khoảng 10 triệu bản ghi và chiếm 1GB dung lượng đĩa. myidkhông liên tiếp.

Tôi muốn tính toán có bao nhiêu hàng trong mỗi khối 100000 số liên tiếp:

SELECT myid/100000 AS block, count(*) AS total FROM mytable GROUP BY myid/100000;

Điều này trả về khoảng 3500 hàng.

Tôi nhận thấy rằng sự tồn tại của một chỉ mục nhất định tăng tốc đáng kể truy vấn này mặc dù kế hoạch truy vấn hoàn toàn không đề cập đến nó. Kế hoạch truy vấn không có chỉ mục:

db=> EXPLAIN (ANALYZE TRUE, VERBOSE TRUE) SELECT myid/100000 AS block, count(*) AS total FROM mytable GROUP BY myid/100000;
                                                               QUERY PLAN                                                               
----------------------------------------------------------------------------------------------------------------------------------------
 GroupAggregate  (cost=1636639.92..1709958.65 rows=496942 width=8) (actual time=6783.763..8888.841 rows=3460 loops=1)
   Output: ((myid / 100000)), count(*)
   ->  Sort  (cost=1636639.92..1659008.91 rows=8947594 width=8) (actual time=6783.752..8005.831 rows=8947557 loops=1)
         Output: ((myid / 100000))
         Sort Key: ((mytable.myid / 100000))
         Sort Method: external merge  Disk: 157440kB
         ->  Seq Scan on public.mytable  (cost=0.00..236506.92 rows=8947594 width=8) (actual time=0.020..1674.838 rows=8947557 loops=1)
               Output: (myid / 100000)
 Total runtime: 8914.780 ms
(9 rows)

Chỉ số:

db=> CREATE INDEX myindex ON mytable ((myid/100000));
db=> VACUUM ANALYZE;

Gói truy vấn mới:

db=> EXPLAIN (ANALYZE TRUE, VERBOSE TRUE) SELECT myid/100000 AS block, count(*) AS total FROM mytable GROUP BY myid/100000;
                                                            QUERY PLAN                                                            
----------------------------------------------------------------------------------------------------------------------------------
 HashAggregate  (cost=281242.99..281285.97 rows=3439 width=8) (actual time=3190.189..3190.800 rows=3460 loops=1)
   Output: ((myid / 100000)), count(*)
   ->  Seq Scan on public.mytable  (cost=0.00..236505.56 rows=8947485 width=8) (actual time=0.026..1659.571 rows=8947557 loops=1)
         Output: (myid / 100000)
 Total runtime: 3190.975 ms
(5 rows)

Vì vậy, các kế hoạch truy vấn và thời gian chạy khác nhau đáng kể (gần ba lần) nhưng không đề cập đến chỉ số. Hành vi này có thể tái tạo hoàn hảo trên máy dev của tôi: Tôi đã trải qua nhiều chu kỳ bỏ chỉ mục, kiểm tra truy vấn nhiều lần, tạo lại chỉ mục, một lần nữa kiểm tra truy vấn nhiều lần. Chuyện gì đang xảy ra ở đây vậy?


Tôi không phải là chuyên gia phân tích các kế hoạch truy vấn của Postgres nhưng tôi đoán chỉ mục được sử dụng cho HashAggregatephương thức (và không cần sắp xếp), do đó bạn sẽ có hiệu suất tốt hơn. Tại sao chỉ số không được đề cập trong kế hoạch, tôi không phải là một đầu mối.
ypercubeᵀᴹ

Đầu ra của gói có thay đổi không nếu bạn bật chế độ dài dòng bằng cách sử dụng : explain (analyze true, verbose true) ...?
a_horse_with_no_name

Sẽ thật tuyệt nếu bạn có thể biến cái này thành một hộp thử nghiệm khép kín. Nó chắc có vẻ kỳ quặc.
Craig Ringer

@a_horse_with_no_name: Vâng, nó thay đổi, tôi đã thay thế các kế hoạch truy vấn bằng các kế hoạch dài dòng trong câu hỏi. Nhưng kế hoạch truy vấn đó vẫn không đề cập đến chỉ số nào cả.
liori

Nếu có nhiều số liệu thống kê có sẵn (đặc biệt là cardinality và có thể là giá trị tối thiểu / tối đa) trên cột id với chỉ số hơn là không có, điều đó có thể thay đổi nhóm của trình tối ưu hóa bằng cách chọn phương thức, ngay cả khi nó hoàn toàn không sử dụng chỉ mục . (Tôi hoàn toàn không biết trình tối ưu hóa & thống kê của postgres, vì vậy không biết đó có phải là trường hợp hay không.)
Mat

Câu trả lời:


3

VACUUM ANALYZElàm cho sự khác biệt trong ví dụ của bạn Ngoài ra, như @jjanes đã cung cấp , số liệu thống kê bổ sung cho chỉ mục chức năng. Mỗi tài liệu:

pg_statisticcũng lưu trữ dữ liệu thống kê về các giá trị của biểu thức chỉ mục. Chúng được mô tả như thể chúng là các cột dữ liệu thực tế; đặc biệt, starelidtham khảo các chỉ số. Tuy nhiên, không có mục nào được tạo cho một cột chỉ mục không biểu thức thông thường, vì nó sẽ là dự phòng với mục nhập cho cột bảng bên dưới.

Tuy nhiên, việc tạo chỉ mục không tự nó khiến Postgres thu thập số liệu thống kê. Thử:

CREATE INDEX myindex ON mytable ((myid/100000));
SELECT * FROM pg_statistic WHERE starelid = 'myindex'::regclass;

Trả về không có gì cho đến khi bạn chạy lần đầu tiên ANALYZE(hoặc VACUUM ANALYZE, hoặc trình tự động tự động khởi động).

ANALYZE mytable;
SELECT * FROM pg_statistic WHERE starelid = 'myindex'::regclass;

Bây giờ bạn sẽ thấy số liệu thống kê được thêm vào.

Vì toàn bộ bảng phải được đọc bằng mọi cách, Postgres sẽ sử dụng quét tuần tự trừ khi dự kiến ​​tính toán của myid/100000nó đủ đắt để chuyển đổi, điều này không phải là.

Cơ hội duy nhất khác của bạn sẽ là quét chỉ mục nếu chỉ mục nhỏ hơn nhiều so với bảng - và điều kiện tiên quyết cho quét chỉ mục được đáp ứng. Chi tiết trong Wiki Postgrestrong hướng dẫn .

Miễn là chỉ số chức năng đó không được sử dụng, lợi ích tài sản thế chấp từ số liệu thống kê được thêm vào là vừa phải. Nếu bảng chỉ đọc, chi phí sẽ thấp - nhưng một lần nữa, có lẽ chúng ta sẽ thấy quét chỉ mục ngay lập tức.

Có lẽ bạn cũng có thể đạt được các kế hoạch truy vấn tốt hơn bằng cách đặt mục tiêu thống kê cao hơn cho mytable.myid. Điều đó sẽ chỉ phát sinh một chi phí nhỏ. Hơn:


Cảm ơn bạn đã giải thích, nó rất hữu ích trong việc hiểu vấn đề. Trong trường hợp của tôi, rất có thể tôi sẽ cần một myid/100000 BETWEEN somevalue AND othervalueđiều kiện bổ sung , vì vậy chỉ mục sẽ được sử dụng trong kế hoạch truy vấn dù sao tôi cũng chỉ hỏi câu hỏi này vì tôi không hiểu tại sao chỉ mục này hữu ích trong trường hợp toàn bảng.
liori

@liori: bạn có thể bao gồm điều đó với WHERE myid BETWEEN somevalue*100000 AND othervalue*100000(xem xét các hiệu ứng làm tròn tùy thuộc vào loại của bạn) và bạn có thể đã có một chỉ mục đơn giản myid, vì vậy bạn có thể làm mà không cần thêm một chỉ mục chuyên biệt. Có thể hiệu quả hơn.
Erwin Brandstetter

6

Khi bạn tạo một chỉ mục biểu thức, nó khiến PostgreSQL thu thập số liệu thống kê về biểu thức đó. Với các số liệu thống kê trên tay, giờ đây nó có một ước tính chính xác cho số lượng hàng tổng hợp mà truy vấn sẽ trả về, điều này dẫn đến việc đưa ra lựa chọn kế hoạch tốt hơn.

Cụ thể trong trường hợp này, nếu không có những thống kê bổ sung đó, nó nghĩ rằng bảng băm sẽ quá lớn để phù hợp với work_mem, vì vậy nó đã không chọn phương thức đó.


Tôi nghĩ rằng người lập kế hoạch không tính đến giá trị của work_memtài khoản. Nếu bạn nâng nó lên để sắp xếp phù hợp với bộ nhớ, nếu vẫn sử dụng cùng một kế hoạch. Hãy để tôi lưu ý ở đây rằng sự khác biệt thời gian (hầu hết trong số đó) đến từ sắp xếp đĩa bên ngoài.
dezso

1
@dezso Điều gì xảy ra nếu bạn thử nghiệm nhân đôi hoặc nhân ba giá trị của work_mem cần thiết để phù hợp với sắp xếp trong bộ nhớ? Sắp xếp và băm có các ước tính trên không khác nhau và bản thân các ước tính không chính xác lắm. Ngoài ra, bạn đang sử dụng phiên bản nhỏ nào của 9.3?
jjanes
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.