2013-04-15 34 views
5

Tôi có một bảng trong cơ sở dữ liệu PostgreSQL được gọi là feeds_up. Có vẻ như:Bản ghi gần đây nhất, trước ngày, theo danh mục: tối ưu hóa

| feed_url | isup | hasproblems | observed timestamp with tz | id (pk)| 
|----------|------|-------------|-------------------------------|--------| 
| http://b.| t | f   | 2013-02-27 16:34:46.327401+11 | 15235 | 
| http://f.| f | t   | 2013-02-27 16:31:25.415126+11 | 15236 | 

Nó có thứ gì đó giống như 300 nghìn hàng, tăng khoảng 20 hàng mỗi năm phút. Tôi có truy vấn chạy rất thường xuyên (mỗi lần tải trang)

select distinct on (feed_url) feed_url, isUp, hasProblems 
    from feeds_up 
    where observed <= '2013-02-27T05:38:00.000Z' 
    order by feed_url, observed desc; 

Tôi đặt thời gian mẫu ở đó, thời gian đó được tham số. Phân tích giải thích là trên explain.depesz.com. Mất khoảng 8s. Khùng!

Chỉ có khoảng 20 giá trị duy nhất cho feed_url, vì vậy điều này có vẻ thực sự không hiệu quả. Tôi nghĩ rằng tôi sẽ là ngu ngốc và thử một vòng lặp FOR trong một chức năng.

CREATE OR REPLACE FUNCTION feedStatusAtDate(theTime timestamp with time zone) RETURNS SETOF feeds_up AS 
$BODY$ 
DECLARE 
    url feeds_list%rowtype; 
BEGIN 
FOR url IN SELECT * FROM feeds_list 
LOOP 
    RETURN QUERY SELECT * FROM feeds_up 
    WHERE observed <= theTime 
    AND feed_url = url.feed_url 
    ORDER BY observed DESC LIMIT 1; 
END LOOP; 
END; 
$BODY$ language plpgsql; 

select * from feedStatusAtDate('2013-02-27T05:38:00.000Z'); 

Chỉ mất 307ms!

Sử dụng vòng lặp FOR trong SQL làm tôi hiểu sai, làm cách nào để tạo một truy vấn tốt đẹp — như truy vấn đầu tiên — hiệu quả? Điều đó có thể không? Hay đây là loại thứ mà vòng lặp FOR thực sự là tốt nhất?

ETA

Postgres phiên bản: PostgreSQL 9.1.5 trên i686-pc-linux-gnu, biên soạn bởi gcc (SUSE Linux) 4.3.4 [gcc-4_3-chi nhánh sửa đổi 152.973], 32-bit

Chỉ số trên feeds_up:

CREATE INDEX feeds_up_url 
    ON feeds_up 
    USING btree 
    (feed_url COLLATE pg_catalog."default"); 

CREATE INDEX feeds_up_url_observed 
    ON feeds_up 
    USING btree 
    (feed_url COLLATE pg_catalog."default", observed DESC); 

CREATE INDEX feeds_up_observed 
    ON public.feeds_up 
    USING btree 
    (observed DESC); 
+0

Chỉ cần FYI @Cathy đã thử nâng cấp 'work_mem' lên 20MB với kết quả sau: http://explain.depesz.com/s/UJw (từ nhận xét về câu trả lời tôi đã xóa). Các loại không còn tràn vào đĩa nhưng truy vấn không phải là nhanh hơn đáng kể. Tạo một chỉ mục 'CREATE INDEX feeds_up_feed_url_observed ON feed_up (feed_url, DESC quan sát);' cũng không tốt; chỉ mục không được sử dụng. –

+0

Phiên bản PostgreSQL nào, bằng cách này? 'SELECT version()'. –

+0

@CraigRinger 9.1.5, tôi sẽ thực hiện chỉnh sửa. – Cathy

Trả lời

1

Giả sử rằng "id" là nối tiếp và luôn luôn liên tục, bạn có thể đơn giản hóa bằng cách tìm MAX (id) cho mỗi FEED_URL trong một subquery và sau đó kéo trong phần còn lại của dữ liệu như sau:

SELECT fu.feed_url, fu.isup, fu.hasproblems, fu.observed 
FROM feeds_up fu 
JOIN 
(
    SELECT feed_url, max(id) AS id FROM feeds_up 
    WHERE observed <= '2013-03-27T05:38:00.000Z' 
    GROUP BY feed_url 
) AS q USING (id); 
ORDER BY fu.feed_url, fu.observed desc; 

Tôi đã làm một bài kiểm tra nhanh chóng và điều này hoạt động rất hiệu quả chỉ sử dụng một chỉ số về "quan sát".

UPDATE:

Để sử dụng "quan sát" thay vì "id" (vì hồ sơ có thể không chèn theo thứ tự), bạn có thể sửa đổi trên truy vấn như sau:

SELECT DISTINCT ON (fu.feed_url) fu.feed_url, fu.isup, fu.hasproblems, fu.observed 
FROM feeds_up fu 
JOIN 
(
    SELECT feed_url, max(observed) as observed FROM feeds_up 
    WHERE observed <= '2013-03-27T05:38:00.000Z' 
    GROUP BY feed_url 
) AS q USING (feed_url, observed) 
ORDER BY fu.feed_url, fu.observed desc; 

Trên hệ thống của tôi này chạy trong gần như cùng một thời điểm với một chỉ số trên "quan sát". YMMV

+0

Đó chỉ là những gì tôi đang tìm kiếm! Ít hơn 100ms với bộ nhớ cache nóng. – Cathy

0

Nếu bạn đang nói về tối ưu hóa bạn nên mô tả những gì indexs bạn có.

Tôi nghĩ rằng một trong đó là hoàn toàn bắt buộc một chỉ mục trong "quan sát"

chỉ số khác sẽ là "FEED_URL, quan sát"

Cuối cùng một trong "FEED_URL", có thể có ích nhưng tôi không phải như vậy chắc chắn nếu điều này sẽ làm ấm hơn tốt. Tất nhiên, nhược điểm của tất cả những điều này sẽ là hiệu năng khi chèn, nhưng vì điều đó tôi sẽ cần biết vấn đề tốt hơn một chút.

Bạn đã xem một partition bởi "feed_url" (vì bạn nói bạn chỉ có một số ít giới hạn)? Nếu không "quan sát" theo ngày (tháng)?

+0

Tôi đã chỉnh sửa để thêm các chỉ mục tôi có. Như @CraigRinger đã nói ở trên, tôi đã thử tạo một chỉ mục kết hợp, nhưng kế hoạch truy vấn không sử dụng nó (Vâng, tôi 'phân tích chân không'). – Cathy

+0

Tôi không nghĩ rằng một phân vùng bởi feed_url sẽ giúp đỡ, vì tôi luôn luôn lấy một trong mỗi (trừ khi tôi phân chia sự hiểu lầm?). Tôi sẵn sàng thử phân vùng bằng "quan sát", (Tôi không thể tìm thấy bất kỳ hướng dẫn nào cho một bảng hiện có?) Mặc dù điều đó có vẻ hơi khắc nghiệt đối với một bảng có khoảng 200 nghìn hàng mỗi tháng. – Cathy

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