Hiệu suất INDEX không liên tục của PostGIS


8

Tôi có một bảng chứa khoảng 55 triệu điểm dữ liệu (điểm là hình học có SRID 4326) và đối với truy vấn của tôi, tôi cần tham gia bảng này vào bảng khu vực (hiện có ~ 1800 khu vực) chứa nhiều loại khác nhau từ đa giác lớn ( 2000 km vuông) đến khá nhỏ (nhỏ khoảng 100 km vuông).

Truy vấn ban đầu được người dùng chọn thu hẹp 55 triệu điểm ban đầu xuống còn khoảng ~ 300.000 điểm tùy thuộc vào phạm vi ngày, v.v. họ chọn. Sau đó, phép nối được thực hiện và phụ thuộc vào khu vực mà họ đã chọn để sử dụng khi truy vấn hoàn tất, điều này thường thu hẹp nó xuống còn ~ 150.000.

Vấn đề tôi gặp phải là đôi khi truy vấn chỉ dừng lại và thay vì mất ~ 25 giây như mong đợi, có thể mất tới ~ 18 phút. Tại thời điểm này thường phải thực hiện PHÂN TÍCH VACUUM và sau đó chạy một vài truy vấn trước khi nó bắt đầu hoạt động trở lại. Không có dữ liệu nào được thêm, cập nhật hoặc xóa khỏi bảng dữ liệu hoặc khu vực tại thời điểm này.

Tôi đã chơi xung quanh với tất cả mọi thứ tôi có thể nghĩ và điều này dường như vẫn tiếp tục xảy ra mà không có sự bất ổn quá. Cả cột data.point và cột area.polygon đều có GIST INDEXES trên chúng.

Tôi thấy việc xóa INDEX khỏi cột data.point dường như làm mọi thứ ổn định hơn một chút nhưng chậm hơn bình thường ~ 35 giây. Tuy nhiên, loại bỏ INDEX dường như là một lựa chọn rất tồi vì nó có nên giúp không cản trở?

Tôi đang sử dụng PostgreSQL 9.1.4 với PostGIS 1.5

Đây là truy vấn tôi đang chạy

    select * FROM data, area WHERE st_intersects (data.point, area.polygon) AND 
(readingdatetime BETWEEN '1948-01-01' AND '2012-11-19') AND datasetid IN(3) AND
 "polysetID" = 1 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)

GIẢI THÍCH

Nested Loop  (cost=312.28..336.59 rows=5 width=2246) (actual time=1445.973..11557.824 rows=12723 loops=1)
  Join Filter: _st_intersects(data.point, area.polygon)
  ->  Index Scan using "area_polysetID_index" on area  (cost=0.00..20.04 rows=1 width=1949) (actual time=0.017..0.229 rows=35 loops=1)
        Index Cond: ("polysetID" = 1)
        Filter: (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[]))
  ->  Bitmap Heap Scan on data  (cost=312.28..316.29 rows=1 width=297) (actual time=328.771..329.136 rows=641 loops=35)
        Recheck Cond: ((point && area.polygon) AND (datasetid = 3))"
        Filter: ((readingdatetime >= '1948-01-01 00:00:00'::timestamp without time zone) AND (readingdatetime <= '2012-11-19 00:00:00'::timestamp without time zone))
        ->  BitmapAnd  (cost=312.28..312.28 rows=1 width=0) (actual time=328.472..328.472 rows=0 loops=35)
              ->  Bitmap Index Scan on data_point_index  (cost=0.00..24.47 rows=276 width=0) (actual time=307.115..307.115 rows=1365770 loops=35)
                    Index Cond: (point && area.polygon)
              ->  Bitmap Index Scan on data_datasetid_index  (cost=0.00..284.37 rows=12856 width=0) (actual time=1.522..1.522 rows=19486 loops=35)
                    Index Cond: (datasetid = 3)
Total runtime: 11560.879 ms

Bảng tạo của tôi

CREATE TABLE data
(
  id bigserial NOT NULL,
  datasetid integer NOT NULL,
  readingdatetime timestamp without time zone NOT NULL,
  value double precision NOT NULL,
  description character varying(255),
  point geometry,
  CONSTRAINT "DATAPRIMARYKEY" PRIMARY KEY (id ),
  CONSTRAINT enforce_dims_point CHECK (st_ndims(point) = 2),
  CONSTRAINT enforce_geotype_point CHECK (geometrytype(point) = 'POINT'::text OR point IS NULL),
  CONSTRAINT enforce_srid_point CHECK (st_srid(point) = 4326)
);

CREATE INDEX data_datasetid_index ON data USING btree (datasetid);
ALTER TABLE data CLUSTER ON data_datasetid_index;

CREATE INDEX "data_datasetid_readingDatetime_index" ON data USING btree (datasetid , readingdatetime );
CREATE INDEX data_point_index ON data USING gist (point);

CREATE INDEX "data_readingDatetime_index" ON data USING btree (readingdatetime );

CREATE TABLE area
(
  id serial NOT NULL,
  polygon geometry,
  "polysetID" integer NOT NULL,
  CONSTRAINT area_primary_key PRIMARY KEY (id )
)

CREATE INDEX area_polygon_index ON area USING gist (polygon);
CREATE INDEX "area_polysetID_index" ON area USING btree ("polysetID");
ALTER TABLE area CLUSTER ON "area_polysetID_index";

Hy vọng rằng tất cả làm cho một mức độ ý nghĩa nếu cần biết bất cứ điều gì khác xin vui lòng yêu cầu.

Tóm tắt ngắn thực sự là INDEXES dường như hoạt động một số lần nhưng không phải là những người khác.

Bất cứ ai có thể đề nghị bất cứ điều gì tôi có thể cố gắng tìm ra những gì đang xảy ra?

Cảm ơn trước.

BIÊN TẬP:

Một vi dụ khac

select * FROM data, area WHERE st_intersects ( data.point, area.polygon) AND 
(readingdatetime BETWEEN '2009-01-01' AND '2012-01-19') AND datasetid IN(1,3) AND
 "polysetID" = 1 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) 

Chạy trên bản sao của bảng với chỉ số điểm

Nested Loop  (cost=0.00..1153.60 rows=35 width=2246) (actual time=86835.883..803363.979 rows=767 loops=1)
  Join Filter: _st_intersects(data.point, area.polygon)
  ->  Index Scan using "area_polysetID_index" on area  (cost=0.00..20.04 rows=1 width=1949) (actual time=0.021..16.287 rows=35 loops=1)
        Index Cond: ("polysetID" = 1)
        Filter: (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[]))
  ->  Index Scan using data_point_index on data  (cost=0.00..1133.30 rows=1 width=297) (actual time=17202.126..22952.706 rows=33 loops=35)
        Index Cond: (point && area.polygon)
        Filter: ((readingdatetime >= '2009-01-01 00:00:00'::timestamp without time zone) AND (readingdatetime <= '2012-01-19 00:00:00'::timestamp without time zone) AND (datasetid = ANY ('{1,3}'::integer[])))
Total runtime: 803364.120 ms

Chạy trên bản sao của bảng không có chỉ số điểm

Nested Loop  (cost=2576.91..284972.54 rows=34 width=2246) (actual time=181.478..235.608 rows=767 loops=1)
  Join Filter: ((data_new2.point && area.polygon) AND _st_intersects(data_new2.point, area.polygon))
  ->  Index Scan using "area_polysetID_index" on area  (cost=0.00..20.04 rows=1 width=1949) (actual time=0.149..0.196 rows=35 loops=1)
        Index Cond: ("polysetID" = 1)
        Filter: (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[]))
  ->  Bitmap Heap Scan on data_new2  (cost=2576.91..261072.36 rows=90972 width=297) (actual time=4.808..5.599 rows=2247 loops=35)
        Recheck Cond: ((datasetid = ANY ('{1,3}'::integer[])) AND (readingdatetime >= '2009-01-01 00:00:00'::timestamp without time zone) AND (readingdatetime <= '2012-01-19 00:00:00'::timestamp without time zone))
        ->  Bitmap Index Scan on "data_new2_datasetid_readingDatetime_index"  (cost=0.00..2554.16 rows=90972 width=0) (actual time=4.605..4.605 rows=2247 loops=35)
              Index Cond: ((datasetid = ANY ('{1,3}'::integer[])) AND (readingdatetime >= '2009-01-01 00:00:00'::timestamp without time zone) AND (readingdatetime <= '2012-01-19 00:00:00'::timestamp without time zone))
Total runtime: 235.723 ms

Như bạn có thể thấy truy vấn chậm hơn đáng kể khi chỉ số điểm đang được sử dụng.

EDIT 2 (Truy vấn được đề xuất của Pauls):

WITH polys AS (
  SELECT * FROM area
  WHERE "polysetID" = 1 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)
)
SELECT * 
FROM polys JOIN data ON ST_Intersects(data.point, polys.polygon)
WHERE datasetid IN(1,3) 
AND (readingdatetime BETWEEN '2009-01-01' AND '2012-01-19');

GIẢI THÍCH

Nested Loop  (cost=20.04..1155.43 rows=1 width=899) (actual time=16691.374..279065.402 rows=767 loops=1)
  Join Filter: _st_intersects(data.point, polys.polygon)
  CTE polys
    ->  Index Scan using "area_polysetID_index" on area  (cost=0.00..20.04 rows=1 width=1949) (actual time=0.016..0.182 rows=35 loops=1)
          Index Cond: ("polysetID" = 1)
          Filter: (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[]))
  ->  CTE Scan on polys  (cost=0.00..0.02 rows=1 width=602) (actual time=0.020..0.358 rows=35 loops=1)
  ->  Index Scan using data_point_index on data  (cost=0.00..1135.11 rows=1 width=297) (actual time=6369.327..7973.201 rows=33 loops=35)
        Index Cond: (point && polys.polygon)
        Filter: ((datasetid = ANY ('{1,3}'::integer[])) AND (readingdatetime >= '2009-01-01 00:00:00'::timestamp without time zone) AND (readingdatetime <= '2012-01-19 00:00:00'::timestamp without time zone))
Total runtime: 279065.540 ms

Bạn nói về "truy vấn" như thể nó chính xác cùng một SQL mỗi lần. Là nó?
Paul Ramsey

1
Ngoài những gì Paul đã nói ở trên, làm thế nào về việc đăng kết quả của một GIẢI THÍCH truy vấn? postgresql.org/docs/8.1/static/sql-explain.html
Kelso

@PaulRamsey Không có truy vấn nào giống nhau mỗi lần, như tôi đã nói phụ thuộc vào đầu vào mà người dùng chọn. Tuy nhiên tôi đã không làm hỏng câu hỏi của mình vì nó không tạo ra sự khác biệt về các bộ lọc đó, nhưng nó chỉ ảnh hưởng đến nó theo cách bạn mong đợi nếu có ít hàng hơn do các ràng buộc chặt chẽ nhanh hơn và ngược lại ngược lại Nhưng nó bị ảnh hưởng theo cách tương tự như đôi khi nó có thể bắt đầu chạy rất chậm cho đến khi thực hiện VACCUM ANALYZE như tôi giải thích ở trên, bất kể truy vấn là gì.
Mark Davidson

@Kelso Xin lỗi đã quên để thêm rằng tôi sẽ sớm thêm một GIẢI THÍCH.
Mark Davidson

Đã thêm một GIẢI THÍCH và một vài bit khác.
Mark Davidson

Câu trả lời:


4

Hiệu quả buộc người lập kế hoạch làm điều bạn muốn có thể giúp đỡ. Trong trường hợp này, thiết lập phụ bảng đa giác trước khi thực hiện phép nối không gian với bảng điểm. Bạn có thể đánh lừa người lập kế hoạch bằng cú pháp "VỚI":

WITH polys AS (
  SELECT * FROM area
  WHERE area.id in 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)
)
SELECT * 
FROM polys JOIN data ON ST_Intersects(data.point, polys.polygon)
WHERE datasetid IN(3) 
AND (readingdatetime BETWEEN '1948-01-01' AND '2012-11-19');

Rắc rối khi thử chơi các trò chơi này là bạn đang mã hóa vào câu lệnh của mình với giả định "danh sách đa giác của tôi sẽ luôn được lựa chọn nhiều hơn các phần truy vấn khác của tôi". Điều này có thể không đúng với tất cả các tham số hóa truy vấn của bạn hoặc cho tất cả các ứng dụng của một truy vấn cụ thể đối với tập dữ liệu được phân phối không đồng nhất.

Nhưng nó có thể làm việc.

CẬP NHẬT : Điều này thậm chí còn đi xa hơn trên con đường nguy hiểm giả sử bạn biết trước tính chọn lọc của các mệnh đề của mình, lần này chúng tôi cũng đưa lựa chọn thuộc tính trên bảng điểm ra và thực hiện riêng trước khi nối không gian:

WITH polys AS (
  SELECT * FROM area
  WHERE area.id in 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)
),
WITH points AS (
  SELECT * FROM data
  WHERE datasetid IN(3) 
  AND (readingdatetime BETWEEN '1948-01-01' AND '2012-11-19')
)
SELECT * 
FROM polys JOIN points ON ST_Intersects(points, polys.polygon);

Đã cập nhật câu hỏi của tôi với truy vấn được đề xuất của bạn và paul kết quả GIẢI THÍCH. Có vẻ như hoạt động tốt hơn nhưng vẫn không có nơi nào tốt như không có chỉ số. Tôi đang xem xét truy vấn và tự hỏi liệu nó có liên quan đến thực tế là nó đang cố gắng tìm ra nếu tất cả các điểm nằm trong đa giác trước khi thu hẹp thời gian đọc và bộ dữ liệu hay tôi đang hiểu sai về GIẢI THÍCH?
Mark Davidson

Bạn có thể làm rõ bảng đọc thời gian và tập dữ liệu đến từ đâu không?
Paul Ramsey

Không bao giờ, tôi thấy nó trong DDL. Vấn đề bây giờ khá rõ ràng, tôi nghĩ, và nó phụ thuộc vào sự chọn lọc tham gia trở lại quá chọn lọc. Tôi đã không nhận thấy rằng các mệnh đề khác của bạn đang đặt lại bảng điểm. Đẩy các bộ lọc không không gian lên thành mệnh đề CÓ.
Paul Ramsey

Điều này có vẻ rất hứa hẹn sau khi đẩy phần còn lại của các truy vấn không theo không gian lên thành mệnh đề VỚI. Tôi đã nghỉ làm việc trong 2 ngày qua nên không có cơ hội xác nhận 100% rằng nó đã giải quyết được vấn đề, nhưng tôi sẽ trao thưởng cho bạn tiền thưởng vì tất cả lời khuyên của bạn ở đây và cho người dùng sau danh sách đã rất hữu ích. Sẽ báo cáo lại khi tôi biết chắc chắn.
Mark Davidson

2

Bằng cách xem phần giải thích cho Chạy trên bản sao của bảng có chỉ mục điểm trên EDIT đầu tiên, có vẻ như bạn đang thiếu chỉ số này trên bảng mà không có chỉ mục điểm:

CREATE INDEX "data_readingDatetime_index" ON data USING btree (readingdatetime );

Bạn có thể xác nhận rằng chỉ số là có?

-- BIÊN TẬP --

Sau khi nghiên cứu thêm về câu hỏi của bạn (không phải là một câu hỏi dễ, btw) tôi có những gợi ý sau đây để thực hiện.

  1. bỏ chỉ mục "data_datasetid_readingDatetime_index" vì bạn đã lập chỉ mục hai cột riêng biệt. Điều này sẽ giúp bạn tiết kiệm không gian, cải thiện hiệu suất chèn và sẽ đơn giản hóa công việc lập kế hoạch truy vấn bằng cách lấy một biến ra khỏi phương trình
  2. cụm bảng lại chỉ mục "data_readingDatetime_index". Phân cụm hiệu quả hơn với các truy vấn dựa trên phạm vi. Bạn dường như không truy vấn dữ liệu với các điều kiện dựa trên phạm vi

    ALTER TABLE data CLUSTER ON data_readingDatetime_index;
  3. Thực hiện phân cụm thực tế. Lệnh ở mục trước không phân cụm bảng của bạn, nó chỉ thể hiện mong muốn của bạn rằng nếu bảng được phân cụm, bạn muốn nó được phân cụm trên chỉ mục đó. Cụm nó với:

     CLUSTER data;
  4. phân tích bảng sau khi phân cụm nó để thống kê (được sử dụng bởi người lập kế hoạch để quyết định chọn chiến lược nào) lưu ý bố cục mới trên đĩa:

     VACUUM ANALYZE data;

    bây giờ vì dữ liệu được tổ chức theo thời gian đọc, người lập kế hoạch sẽ ủng hộ chiến lược sử dụng chỉ mục data_readingDatetime_index và vì bất cứ khi nào nó được sử dụng, kế hoạch giải thích dường như là nhanh nhất nên có lẽ hiệu suất sẽ cải thiện và dao động ít hơn

Như tôi đã nói trong phần bình luận để Paul trả lời ở trên, đừng nghĩ rằng trình hoạch định sẽ không thay đổi chiến lược tùy thuộc vào các bộ lọc (ngay cả khi các bộ lọc luôn giống nhau và chỉ có giá trị của chúng thay đổi).

Có một ví dụ trong cuốn sách Hiệu suất cao PostregQuery 9.0 được đề xuất cao , trong đó việc thay đổi một điều kiện từ chọn ... từ bảng t trong đó v <5 sang v <6 đã chuyển kế hoạch từ quét chỉ mục sang quét toàn bộ bảng.


Nếu bạn nhìn vào lời giải thích thứ ba của anh ấy, bạn sẽ thấy "-> Quét chỉ mục Bitmap trên" data_new2_datasetid_readingDatetime_index "(chi phí = 0,00..2554.16 hàng = 90972 width = 0) (thời gian thực tế = 4.605..4.605 hàng = 2247 vòng lặp = 3547 ) "đó là nơi chỉ số trên cột đó thực sự phát huy tác dụng khi chỉ số không gian được đưa ra khỏi phương trình.
Paul Ramsey

chính xác quan điểm của tôi. đó là trong truy vấn nhanh, không phải trong truy vấn chậm (chậm là một trong kế hoạch giải thích trước kế hoạch thứ ba mà bạn đề cập). Có thể là trong việc sao chép bảng, chỉ mục bị mất?
unicoletti

Không, như đã lưu ý ở trên, trình hoạch định đang bỏ qua chỉ số đó theo hướng có lợi cho không gian, bởi vì không gian là (không chính xác) báo cáo độ chọn lọc rất cao cho trình hoạch định. Vì vậy, nó tồn tại, nó không được sử dụng.
Paul Ramsey

@unicoletti Cảm ơn bạn rất nhiều vì đầu vào của bạn chắc chắn sẽ cung cấp cho bạn các đề xuất và sẽ báo cáo lại kết quả sớm. Tôi thực sự đã có cuốn sách Hiệu suất cao PostgreSQL 9.0 hoàn toàn đồng ý với việc đọc sách biết rằng tôi cần phải đọc nó nhiều hơn để đảm bảo tăng hiệu suất mỗi lần tôi có thể.
Mark Davidson
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.