Quét chỉ mục chậm trong bảng lớn


11

Sử dụng PostgreSQL 9.2, tôi gặp rắc rối với các truy vấn chậm trên một bảng tương đối lớn (hơn 200 triệu hàng). Tôi không thử bất cứ điều gì điên rồ, chỉ thêm các giá trị lịch sử. Dưới đây là truy vấn và đầu ra kế hoạch truy vấn.

Bố trí bảng của tôi:

                                   Table "public.energy_energyentry"
  Column   |           Type           |                            Modifiers
-----------+--------------------------+-----------------------------------------------------------------
 id        | integer                  | not null default nextval('energy_energyentry_id_seq'::regclass)
 prop_id   | integer                  | not null
 timestamp | timestamp with time zone | not null
 value     | double precision         | not null
Indexes:
    "energy_energyentry_pkey" PRIMARY KEY, btree (id)
    "energy_energyentry_prop_id" btree (prop_id)
    "energy_energyentry_prop_id_timestamp_idx" btree (prop_id, "timestamp")
Foreign-key constraints:
    "energy_energyentry_prop_id_fkey" FOREIGN KEY (prop_id) REFERENCES gateway_peripheralproperty(id) DEFERRABLE INITIALLY DEFERRED

Dữ liệu nằm trong khoảng từ 2012-01-01 đến nay, với dữ liệu mới liên tục được thêm vào. Có khoảng 2,2k giá trị riêng biệt trong prop_idkhóa ngoại, được phân bổ đều.

Tôi nhận thấy rằng các ước tính hàng không xa, nhưng ước tính chi phí dường như lớn hơn theo hệ số 4x. Đây có lẽ không phải là một vấn đề, nhưng tôi có thể làm gì về nó không?

Tôi hy vọng rằng việc truy cập đĩa có thể là vấn đề, vì bảng luôn không có trong bộ nhớ.

EXPLAIN ANALYZE 
SELECT SUM("value") 
FROM "energy_energyentry" 
WHERE 
  "prop_id"=82411 
  AND "timestamp">'2014-06-11' 
  AND "timestamp"<'2014-11-11'
;
 Aggregate  (cost=214481.45..214481.46 rows=1 width=8) (actual time=51504.814..51504.814 rows=1 loops=1)
   ->  Index Scan using energy_energyentry_prop_id_timestamp_idx on  energy_energyentry (cost=0.00..214434.08 rows=18947 width=8) (actual time=136.030..51488.321 rows=13578 loops=1)
         Index Cond: ((prop_id = 82411) AND ("timestamp" > '2014-06-11 00:00:00+00'::timestamp with time zone) AND ("timestamp" < '2014-11-11 00:00:00+00'::timestamp with time zone))
 Total runtime: 51504.841 ms

Bất kỳ đề xuất làm thế nào để làm điều này nhanh hơn?
Tôi cũng ổn khi chỉ nghe nói tôi đã không làm điều gì kỳ lạ.


1
Vui lòng cho chúng tôi biết bảng của bạn trông như thế nào, nó có chỉ mục gì và sự lan truyền dữ liệu.
Colin 't Hart

Tôi đã thêm thông tin bổ sung mà bạn yêu cầu. Cho dù tôi có bỏ lỡ điều gì không.
Exelian

2
Strange: Phân tích giải thích của bạn cho thấy prop_time_idx, nhưng định nghĩa bảng hiển thị entry_prop_id_timestamp_idx. Đây có phải là cùng một chỉ số? Hãy sửa chữa.
Colin 't Hart

Nếu bạn giới thiệu 'các ước tính chi phí dường như lớn hơn gấp 4 lần ' với thực tế là các con số chi phí gấp khoảng 4 lần so với thời gian thực tế , thì xin lưu ý rằng hai điều này không liên quan gì đến nhau. Chi phí chỉ là một ước tính, giúp tối ưu hóa truy vấn để chọn kế hoạch tìm kiếm tốt nhất. Bên ngoài bối cảnh này, nó thường là một giá trị vô nghĩa.
dezso

1
Phạm vi ngày của bạn thể hiện bao nhiêu phần trăm của bảng (mà không tính đến các giá trị prop)? Nếu chỉ là một tỷ lệ nhỏ, có thể một chỉ số trên ("timestamp", prop)sẽ tốt hơn. Nhiều chỉ mục có cùng (các) cột hàng đầu ( proptrong trường hợp của bạn) cũng thường là dự phòng.
Colin 't Hart

Câu trả lời:


9

Bảng của bạn lớn và bất kỳ chỉ mục nào cũng bao trùm toàn bộ bảng. Giả sử rằng:

  • chỉ dữ liệu mới (với timestamp = now() ) được nhập
  • các hàng hiện tại không thay đổi cũng không bị xóa.
  • bạn có dữ liệu từ 2012-01-01 nhưng chủ yếu là các truy vấn trong năm hiện tại (?)

Tôi muốn đề xuất một chỉ mục một phần, nhiều cột (bao gồm!) :

CREATE INDEX ON energy_energyentry (prop_id, "timestamp", value)
WHERE "timestamp" >= '2014-01-01 0:0';  -- adapt to your needs

Chỉ bao gồm phạm vi thời gian được truy vấn thường xuyên. Hiệu quả suy giảm theo thời gian với các mục mới. Tái tạo chỉ số theo thời gian. (Bạn có thể cần điều chỉnh các truy vấn của mình.) Xem câu trả lời được liên kết bên dưới.

Giá trị cột cuối cùng chỉ được đưa vào để quét chỉ mục này. Cài đặt tự động xâm lấn có thể giúp bằng cách giữ cho bản đồ hiển thị được cập nhật, như @jjanes đã được đề cập .

Chỉ số một phần sẽ phù hợp với RAM dễ dàng hơn và ở đó lâu hơn.

Bạn có thể cần đưa điều WHEREkiện này vào các truy vấn để làm cho trình hoạch định hiểu chỉ mục có thể áp dụng cho truy vấn, như:

SELECT sum(value) AS sum_value
FROM   energy_energyentry
WHERE  prop_id = 82411 
AND   "timestamp" > '2014-06-11 0:0' 
AND   "timestamp" < '2014-11-11 0:0'
AND   "timestamp" >= '2014-01-01 0:0'; -- seems redundant, but may be needed

Vì truy vấn của bạn là tổng hợp rất nhiều hàng (rows=13578 ), nên việc này sẽ mất một chút thời gian, ngay cả khi quét chỉ mục. Nó không nên ở bất cứ đâu gần 50 giây. Ít hơn một giây trên bất kỳ phần cứng tốt nửa chừng.

Liên quan (nhưng bỏ qua CLUSTERFILLFACTOR, cả hai đều không liên quan nếu bạn có thể quét chỉ mục này khỏi mục này) :

Ngoài ra:
Vì bạn hiện có một chỉ mục trên (prop_id, "timestamp"), chỉ mục bổ sung trên (prop_id)có thể có giá cao hơn giá trị của nó:


Bây giờ Postgres hỗ trợ các chỉ mục BRIN, điều đó có hữu ích ở đây không? Tôi có kế hoạch lưu trữ khoảng 140 triệu hàng trên dữ liệu trên postgres, BRIN có phải là chỉ mục phù hợp để sử dụng cho bảng lớn không?
Arya

2

Nếu bạn thực hiện chỉ mục trên (prop_id, "dấu thời gian", "giá trị"), thì nó có thể sử dụng quét chỉ mục để tính giá trị mà không cần truy cập vào bảng. Điều này có thể tiết kiệm rất nhiều truy cập đĩa ngẫu nhiên.

Để có được lợi ích cao nhất, bạn cần phải tích cực trong việc hút bụi bàn. Cài đặt autovac mặc định không đủ mạnh cho các bảng chỉ chèn mà bạn muốn hỗ trợ quét chỉ mục một cách hiệu quả.


Thêm giá trị thực sự có thể thú vị, tôi sẽ xem liệu điều đó sẽ tăng tốc mọi thứ. Bạn có bất cứ đề nghị cho các thiết lập chân không hoặc tài liệu tôi có thể xem?
Exelian
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.