6

Tôi có một bảng trong postgresql chứa một mảng được cập nhật liên tục.Tối ưu hóa truy vấn đếm cho PostgreSQL

Trong ứng dụng của tôi, tôi cần lấy số hàng mà thông số cụ thể không có trong cột mảng đó. truy vấn của tôi trông như thế này:

select count(id) 
from table 
where not (ARRAY['parameter value'] <@ table.array_column) 

Nhưng khi tăng số lượng hàng và số lượng các vụ hành quyết của truy vấn đó (vài lần mỗi giây, có thể hàng trăm hoặc hàng ngàn) hiệu suất decreses rất nhiều, có vẻ như với tôi rằng đếm trong postgresql có thể có một thứ tự tuyến tính thực hiện (tôi không hoàn toàn chắc chắn về điều này).

Về cơ bản câu hỏi của tôi là:

Có một mô hình hiện tại tôi không nhận thức được áp dụng cho tình huống này? cách tiếp cận tốt nhất cho điều này là gì?

Bất kỳ đề xuất nào bạn có thể cho tôi sẽ thực sự được đánh giá cao.

+0

Không chắc chắn, nhưng tôi nghĩ rằng chỉ mục GIN trên table.array_column sẽ giúp tăng tốc độ này. Bạn sẽ cần phải chạy EXPLAIN để tìm hiểu. Xem ở đây: http://dba.stackexchange.com/a/27505/1822 –

+1

Sẽ rất khó để thực hiện điều này hiệu quả trong các postgres khi bảng trở nên lớn. chỉ mục gin sẽ chỉ giúp khi thử nghiệm "chứa trong" trái với "không chứa trong" trong vị từ của bạn. Nếu nó không quan trọng là đếm được chính xác 100%, bạn có thể thử bộ nhớ đệm nó ở lớp ứng dụng với một số TTL. Nếu tỷ lệ ghi của bạn trên bảng không quá cao, bạn có thể sử dụng các trình kích hoạt hợp lý để cập nhật một bảng khác có chứa số lượng hiện tại. – dbenhur

+0

Tốt nhất để hiển thị phiên bản của bạn và 'giải thích phân tích'; xem http://stackoverflow.com/tags/postgresql-performance/info –

Trả lời

2

Có mẫu hiện có nào mà tôi không biết về mẫu áp dụng cho trường hợp này không? cách tiếp cận tốt nhất cho điều này là gì?

Đặt cược tốt nhất của bạn trong trường hợp này có thể là bình thường hóa giản đồ của bạn. Tách mảng ra thành một bảng. Thêm chỉ mục b-tree trên bảng thuộc tính hoặc đặt khóa chính để có thể tìm kiếm hiệu quả theo property_id.

CREATE TABLE demo(id integer primary key); 
INSERT INTO demo (id) SELECT id FROM arrtable; 
CREATE TABLE properties (
    demo_id integer not null references demo(id), 
    property integer not null, 
    primary key (demo_id, property) 
); 
CREATE INDEX properties_property_idx ON properties(property); 

Sau đó bạn có thể truy vấn các thuộc tính:

SELECT count(id) 
FROM demo 
WHERE NOT EXISTS (
    SELECT 1 FROM properties WHERE demo.id = properties.demo_id AND property = 1 
) 

Tôi nghĩ đây chỉ là nhanh hơn rất nhiều so với các truy vấn ban đầu, nhưng nó thực sự nhiều việc cùng với các dữ liệu mẫu tương tự; nó chạy trong phạm vi 2s đến 3s giống như truy vấn ban đầu của bạn. Vấn đề tương tự khi tìm kiếm những gì là không phải là có chậm hơn nhiều so với việc tìm kiếm những gì ở đó; nếu chúng tôi đang tìm kiếm các hàng có chứa thuộc tính, chúng tôi có thể tránh các seqscan của demo và chỉ cần quét trực tiếp properties để đối sánh ID.

Một lần nữa, quét seq trên bảng chứa mảng cũng thực hiện công việc.

+0

Cảm ơn bạn đã giải thích chi tiết, vâng trong tình huống hiện tại của tôi là tốt hơn để làm số đếm tuần tự hoặc nghĩ cách khác để lưu trữ thông tin để tìm kiếm nhanh hơn, một lần nữa cảm ơn rất nhiều điều này đã thực sự hữu ích – jeruki

2

Tôi nghĩ với mô hình dữ liệu hiện tại của bạn Bạn đã hết may mắn. Hãy thử nghĩ về một thuật toán mà cơ sở dữ liệu phải thực thi cho truy vấn của bạn. Không có cách nào nó có thể làm việc mà không quét tuần tự dữ liệu.

Bạn có thể sắp xếp cột sao cho cột lưu trữ nghịch đảo của dữ liệu (để truy vấn sẽ là select count(id) from table where ARRAY[‘parameter value’] <@ table.array_column)? Truy vấn này sẽ sử dụng chỉ mục gin/gist.

5

PostgreSQL thực sự hỗ trợ chỉ mục GIN trên cột mảng. Thật không may, dường như không sử dụng được cho NOT ARRAY[...] <@ indexed_col và chỉ số GIN không phù hợp với các bảng được cập nhật thường xuyên.

Demo:

CREATE TABLE arrtable (id integer primary key, array_column integer[]); 

INSERT INTO arrtable(1, ARRAY[1,2,3,4]); 

CREATE INDEX arrtable_arraycolumn_gin_arr_idx 
ON arrtable USING GIN(array_column); 

-- Use the following *only* for testing whether Pg can use an index 
-- Do not use it in production. 
SET enable_seqscan = off; 

explain (buffers, analyze) select count(id) 
from arrtable 
where not (ARRAY[1] <@ arrtable.array_column); 

Thật không may, điều này cho thấy rằng khi viết chúng tôi không thể sử dụng các chỉ số. Nếu bạn không phủ nhận điều kiện, nó có thể được sử dụng, vì vậy bạn có thể tìm kiếm và đếm các hàng do chứa phần tử tìm kiếm (bằng cách xóa NOT).

Bạn có thể sử dụng chỉ mục để đếm các mục nhập do chứa giá trị đích, sau đó trừ kết quả đó khỏi tổng số tất cả các mục nhập. Kể từ khi count ing tất cả các hàng trong một bảng là khá chậm trong PostgreSQL (9.1 trở lên) và yêu cầu quét tuần tự này sẽ thực sự chậm hơn so với truy vấn hiện tại của bạn. Có thể là trên 9,2 một quét chỉ số chỉ có thể được sử dụng để đếm các hàng nếu bạn có một chỉ số b-tree trên id, trong trường hợp này thực sự có thể OK:

SELECT (
    SELECT count(id) FROM arrtable 
) - (
    SELECT count(id) FROM arrtable 
    WHERE (ARRAY[1] <@ arrtable.array_column) 
); 

Nó đảm bảo để thực hiện tồi tệ hơn phiên bản gốc của bạn cho phiên bản 9.1 trở xuống, bởi vì ngoài seqscan, bản gốc của bạn cũng yêu cầu nó cũng cần cần quét chỉ mục GIN. Bây giờ tôi đã thử nghiệm này trên 9.2 và nó xuất hiện để sử dụng một chỉ số cho số lượng, do đó, nó có giá trị khám phá cho 9.2. Với một số dữ liệu giả ít tầm thường:

drop index arrtable_arraycolumn_gin_arr_idx ; 
truncate table arrtable; 
insert into arrtable (id, array_column) 
select s, ARRAY[1,2,s,s*2,s*3,s/2,s/4] FROM generate_series(1,1000000) s; 
CREATE INDEX arrtable_arraycolumn_gin_arr_idx 
ON arrtable USING GIN(array_column); 

Lưu ý rằng một chỉ số GIN như thế này sẽ làm chậm cập nhật xuống một LOT, và khá chậm chạp trong việc tạo ra ở nơi đầu tiên. Nó không thích hợp cho các bảng được cập nhật nhiều nhất - như bảng của bạn.

Tệ hơn, truy vấn sử dụng chỉ mục này mất gấp hai lần miễn là truy vấn ban đầu của bạn và ở mức tốt nhất một nửa là dài trên cùng một tập dữ liệu. Đó là điều tồi tệ nhất đối với các trường hợp chỉ số không phải là rất có chọn lọc như ARRAY[1] - 4s so với 2 giây đối với truy vấn ban đầu. Trường hợp chỉ số có tính chọn lọc cao (ví dụ: không có nhiều kết quả phù hợp, chẳng hạn như ARRAY[199]), chỉ số này chạy trong khoảng 1,2 giây so với 3 giây ban đầu. Chỉ mục này chỉ đơn giản là không có giá trị cho truy vấn này.

Bài học ở đây? Đôi khi, câu trả lời đúng chỉ là thực hiện quét tuần tự.

Vì điều đó sẽ không làm cho tỷ lệ truy cập của bạn, hãy duy trì chế độ xem được kích hoạt bằng @debenhur gợi ý hoặc cố gắng đảo ngược mảng thành danh sách các thông số mà mục nhập không phải có thể sử dụng chỉ mục GiST như @maniek đề xuất.

Các vấn đề liên quan