Cải thiện hiệu suất sắp xếp trong PostgreSQL?


7

Tôi đã có một cơ sở dữ liệu blog đơn giản trong postgres-8.4 có hai bảng articlescomments. Tôi có một truy vấn (được tạo bởi Django) muốn nhận bài viết mới nhất thuộc loại 'TIN TỨC' và cũng tìm thấy số lượng bình luận cho bài viết đó. Nó thực hiện điều đó với truy vấn sau:

SELECT "articles"."id", "articles"."datestamp", "articles"."title", "articles"."shorttitle", "articles"."description", "articles"."markdown", "articles"."body", "articles"."idxfti", "articles"."published", "articles"."type", COUNT("comments"."id") AS "comment__count"
FROM "articles"
LEFT OUTER JOIN "comments" ON ("articles"."id" = "comments"."article_id")
WHERE ("articles"."type"='NEWS')
GROUP BY "articles"."id", "articles"."datestamp", "articles"."title", "articles"."shorttitle", "articles"."description", "articles"."markdown", "articles"."body", "articles"."idxfti", "articles"."published", "articles"."type"
ORDER BY "articles"."datestamp" DESC
LIMIT 1;

Không có bảng nào trong số này đặc biệt lớn và truy vấn đó mất 46ms. Kế hoạch thực hiện là:

Limit  (cost=119.54..119.58 rows=1 width=1150) (actual time=46.479..46.481 rows=1 loops=1)
   ->  GroupAggregate  (cost=119.54..138.88 rows=455 width=1150) (actual time=46.475..46.475 rows=1 loops=1)
     ->  Sort  (cost=119.54..120.68 rows=455 width=1150) (actual time=46.426..46.428 rows=2 loops=1)
           Sort Key: articles.datestamp, articles.id, articles.title, articles.shorttitle, articles.description, articles.markdown, articles.body, articles.idxfti, articles.published, articles.type
           Sort Method:  quicksort  Memory: 876kB
           ->  Hash Left Join  (cost=11.34..99.45 rows=455 width=1150) (actual time=0.513..2.527 rows=566 loops=1)
                 Hash Cond: (articles.id = comments.article_id)
                 ->  Seq Scan on articles  (cost=0.00..78.84 rows=455 width=1146) (actual time=0.017..0.881 rows=455 loops=1)
                       Filter: ((type)::text = 'NEWS'::text)
                 ->  Hash  (cost=8.93..8.93 rows=193 width=8) (actual time=0.486..0.486 rows=193 loops=1)
                       ->  Seq Scan on comments  (cost=0.00..8.93 rows=193 width=8) (actual time=0.004..0.252 rows=193 loops=1)
 Total runtime: 46.574 ms

Bảng bài viết có chỉ mục sau được xác định (trong số những người khác):

idx_articles_datestamp" btree (datestamp DESC) CLUSTER

Trước khi tôi phân cụm nó, việc thực hiện truy vấn phù hợp hơn với các ước tính, khoảng 119ms.

Đối với con mắt chưa được huấn luyện của tôi, có vẻ như đó là thứ chiếm nhiều thời gian nhất ở đây. Dường như cũng đang cố gắng sắp xếp trên tất cả các trường NHÓM THEO, vấn đề là nó đang cố gắng sắp xếp trên ba trường tương đối lớn body, markdownidx_fti.

Câu hỏi của tôi là: Đây có phải là một khoảng thời gian không hợp lý cho truy vấn này, hoặc có điều gì đó rõ ràng tôi đã bỏ lỡ mà tôi có thể sử dụng để tăng tốc truy vấn này không? Tất cả các truy vấn khác được yêu cầu bởi trang blog này mất khoảng 1-5ms để thực hiện, do đó, truy vấn này nổi bật như mất nhiều thời gian. Tôi đánh giá cao việc có THAM GIA NGOÀI và NGHIÊM TRỌNG, điều này không thực sự hữu ích. Tuy nhiên, tôi không phải là chuyên gia, vì vậy nếu có ai có bất kỳ đề xuất nào, điều đó sẽ vô cùng hữu ích.

Câu trả lời:


9

Tại sao nó chậm?

Tôi sẽ khuyên bạn nên sử dụng truy vấn @ypercube được cung cấp kết hợp với các chỉ mục được đề cập. Nhưng tại sao các truy vấn bạn có rất chậm so sánh?

Bạn không cung cấp định nghĩa bảng của mình, nhưng tôi giả sử từ các tên cột và những gì bạn đã viết rằng bạn có một số cột ( texthoặc varchar) loại ký tự ( hoặc ) trong bảng articles:

title, shorttitle, description, markdown, body, idx_fti

Tôi cũng giả định rằng bạn đang chạy cơ sở dữ liệu của mình với một ngôn ngữ khácC . Sắp xếp các cột văn bản lớn theo ngôn ngữ là khá tốn kém. Điều liên quan là đối chiếu . Kiểm tra cài đặt (hiện tại) của bạn để biết LC_COLLATE:

SHOW LC_COLLATE;

Với Postgres 9.1 trở lên, bạn có thể chọn đối chiếu để đánh giá biểu thức của mình . Tuy nhiên, với PostgreQuery 8.4 , điều này được đặt ở thời gian tạo cụm và không thể thay đổi sau này.

Gần đây chúng tôi đã có một câu hỏi liên quan về SO, sau khi cân nhắc và thử nghiệm nhiều, chúng tôi thấy việc sắp xếp theo một địa điểm là sự chậm lại lớn:

Tôi hy vọng truy vấn của @ ypercube sẽ giải quyết triệt để vấn đề đó: Không GROUP BYcho các cột văn bản dài loại bỏ hoàn toàn loại sắp xếp đắt tiền. Vấn đề được giải quyết.


bạn có thể vui lòng giúp tôi trong một câu hỏi tương tự stackoverflow.com/questions/59818667/
trộm

8

Một cách khác để viết lại truy vấn, với một truy vấn con nội tuyến:

SELECT id,
       datestamp,
       title,
       shorttitle,
       description,
       markdown,
       body,
       idxfti,
       published,
       type,
       ( SELECT COUNT(*) 
         FROM comments 
         WHERE articles.id = comments.article_id
       ) AS comment__count
FROM articles 
WHERE type = 'NEWS'
ORDER BY datestamp DESC 
LIMIT 1

1
+1 Đây phải là giải pháp tốt nhất. Trong kết nối với một chỉ mục trên comments(article)và chỉ mục được đề cập ở trên articles(type, datestamp DESC). Truy vấn sử dụng LIMIT 1, do đó, chỉ các hàng có liên quan mới được tìm nạp articlevà tính.
Erwin Brandstetter

3

Nếu article.type nhỏ hơn khoảng 10% của bảng, bạn có thể được hưởng lợi từ một chỉ mục trên cột đó. Bạn gần như chắc chắn có thể hưởng lợi từ một chỉ mục trên bình luận.article_id, nếu bạn chưa có.

Ngoài ra, nếu bạn chưa điều chỉnh các yếu tố chi phí trong cấu hình của mình, bạn có thể thử hạ random_page_costxuống một nơi nào đó trong phạm vi 1.0 đến 2.0; nếu bộ dữ liệu hoạt động của bạn được lưu trữ đầy đủ, có lẽ bạn nên lấy nó seq_page_cost xuống 0,1. Bạn có thể nên tăng cpu_tuple_costđến một nơi nào đó trong khoảng 0,03 đến 0,05. effective_cache_sizephải là tổng shared_buffersvà bất cứ điều gì hệ điều hành của bạn hiển thị dưới dạng không gian bộ đệm.


Tôi đã có một chỉ mục trên comments.article_idvà thêm một chỉ mục articles.typedường như không làm được gì nhiều. Tôi sẽ thử điều chỉnh một số cài đặt máy chủ, cảm ơn vì điều đó.
gầm gừ

2

Bạn có thể muốn xóa nhóm bằng cách sử dụng chức năng cửa sổ để đếm. Điều đó loại bỏ nhu cầu nhóm theo / sắp xếp trên tất cả các cột:

SELECT articles.id,
       articles.datestamp,
       articles.title,
       articles.shorttitle,
       articles.description,
       articles.markdown,
       articles.body,
       articles.idxfti,
       articles.published,
       articles.type,
       COUNT(comments.id) over () AS comment__count
FROM articles 
  LEFT OUTER JOIN comments ON (articles.id = comments.article_id)
WHERE (articles.type = 'NEWS')
ORDER BY articles.datestamp DESC 
LIMIT 1

Một chỉ số trên (type ASC, datestamp DESC)cũng sẽ cải thiện hiệu suất, phải không?
ypercubeᵀᴹ

@ypercube: vâng, rất có thể giúp đỡ để tăng tốc độ điều kiện.
a_horse_with_no_name

Vâng, nếu truy vấn có thể được viết lại, có một số lựa chọn thay thế tốt, trong đó điều này có thể là tốt nhất. Thậm chí nhiều tùy chọn tồn tại trên phiên bản 9.1, trong đó nếu bạn GROUP BY(các) cột khóa chính, bạn không cần bao gồm bất kỳ cột nào khác trong bảng đó.
kgrittn

Tôi đã thử thêm một chỉ mục với create index idx_articles_type_datestamp on articles(type ASC, datestamp DESC)nhưng điều đó dường như không tạo ra nhiều khác biệt. Tôi sẽ thấy những gì tôi có thể làm với việc buộc django phải làm một COUNT OVER, nhưng tôi có đúng khi nghĩ đó là đặc thù của PG không?
gầm gừ

3
@growse: các chức năng cửa sổ là SQL tiêu chuẩn và được hỗ trợ bởi rất nhiều DBMS hiện đại (Oracle, PostgreQuery, DB2, SQL Server, Teradata, Firebird 3.0)
a_horse_with_no_name

-2

Có lẽ bạn có thể thực hiện một số thử nghiệm sửa đổi giá trị của work_mem . Ở đó bạn có thể tìm thấy các hướng dẫn để tìm ra bao nhiêu bộ nhớ được sử dụng trong các hoạt động sắp xếp.


Khi một loại không phù hợp với work_mem, nó sẽ tràn ra đĩa. Nếu xảy ra, điều này được hiển thị trong kế hoạch truy vấn. Mặt khác, việc sắp xếp ở đây chiếm 876 kB bộ nhớ. Rất khó có khả năng này lớn hơn work_mem của bất kỳ trường hợp PG tương đối gần đây.
dezso
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.