Làm cách nào để buộc Postgres sử dụng một chỉ mục khi nếu không nó sẽ yêu cầu thực hiện quét tuần tự?
Làm cách nào để buộc Postgres sử dụng một chỉ mục khi nếu không nó sẽ yêu cầu thực hiện quét tuần tự?
Câu trả lời:
Giả sử bạn đang hỏi về tính năng "gợi ý chỉ mục" phổ biến được tìm thấy trong nhiều cơ sở dữ liệu, PostgreSQL không cung cấp tính năng như vậy. Đây là một quyết định có ý thức của nhóm PostgreSQL. Bạn có thể tìm thấy tổng quan tốt về lý do và những gì bạn có thể làm thay thế tại đây . Về cơ bản, lý do là đó là một cuộc tấn công hiệu suất có xu hướng gây ra nhiều vấn đề hơn sau này khi dữ liệu của bạn thay đổi, trong khi trình tối ưu hóa của PostgreSQL có thể đánh giá lại kế hoạch dựa trên số liệu thống kê. Nói cách khác, những gì có thể là một kế hoạch truy vấn tốt hiện nay có thể không phải là một kế hoạch truy vấn tốt cho mọi thời đại và các gợi ý chỉ mục buộc phải có một kế hoạch truy vấn cụ thể cho mọi thời điểm.
Là một chiếc búa rất cùn, hữu ích để thử nghiệm, bạn có thể sử dụng các thông số enable_seqscan
và enable_indexscan
. Xem:
Chúng không thích hợp cho việc sử dụng sản xuất liên tục . Nếu bạn gặp vấn đề với lựa chọn kế hoạch truy vấn, bạn nên xem tài liệu để theo dõi các vấn đề về hiệu suất truy vấn . Đừng chỉ thiết lập các thông số enable_
và bỏ đi.
Trừ khi bạn có một lý do chính đáng để sử dụng chỉ mục, Postgres có thể đưa ra lựa chọn chính xác. Tại sao?
Xem thêm bài đăng nhóm tin cũ này .
Có lẽ là lý do hợp lệ duy nhất để sử dụng
set enable_seqscan=false
là khi bạn đang viết các truy vấn và muốn nhanh chóng xem kế hoạch truy vấn thực sự sẽ như thế nào nếu có một lượng lớn dữ liệu trong (các) bảng. Hoặc tất nhiên nếu bạn cần nhanh chóng xác nhận rằng truy vấn của bạn không sử dụng chỉ mục chỉ đơn giản là vì tập dữ liệu quá nhỏ.
set enable_seqscan=false
, chạy truy vấn của bạn, và sau đó nhanh chóng chạy set enable_seqscan=true
trở về postgresql đến hành vi thích hợp của nó (và rõ ràng là không làm điều này trong sản xuất, chỉ trong sự phát triển!)
SET SESSION enable_seqscan=false
để chỉ ảnh hưởng đến bản thân
Đôi khi PostgreSQL không đưa ra được lựa chọn chỉ mục tốt nhất cho một điều kiện cụ thể. Ví dụ: giả sử có một bảng giao dịch với vài triệu hàng, trong đó có vài trăm hàng cho bất kỳ ngày nhất định nào và bảng có bốn chỉ mục: transaction_id, client_id, date và description. Bạn muốn chạy truy vấn sau:
SELECT client_id, SUM(amount)
FROM transactions
WHERE date >= 'yesterday'::timestamp AND date < 'today'::timestamp AND
description = 'Refund'
GROUP BY client_id
PostgreSQL có thể chọn sử dụng index transaction_description_idx thay vì transaction_date_idx, điều này có thể dẫn đến truy vấn mất vài phút thay vì ít hơn một giây. Nếu đúng như vậy, bạn có thể buộc sử dụng chỉ mục đúng ngày bằng cách làm mờ điều kiện như sau:
SELECT client_id, SUM(amount)
FROM transactions
WHERE date >= 'yesterday'::timestamp AND date < 'today'::timestamp AND
description||'' = 'Refund'
GROUP BY client_id
your_wanted_index
, có thể vì vậy mà công cụ postgresql sẽ chỉ thực hiện quét trình tự / khóa chính thay thế. Kết luận - không có phương pháp nào đáng tin cậy 100% để buộc sử dụng một số chỉ mục cho máy chủ PostgreSql.
where
điều kiện nhưng hai bảng hoặc được nối và Postgres không lấy chỉ mục.
Sự cố này thường xảy ra khi chi phí ước tính của một lần quét chỉ mục quá cao và không phản ánh đúng thực tế. Bạn có thể cần phải giảm random_page_cost
thông số cấu hình để khắc phục điều này. Từ tài liệu Postgres :
Giảm giá trị này [...] sẽ khiến hệ thống thích quét chỉ mục hơn; nâng cao nó sẽ làm cho việc quét chỉ mục trông tương đối đắt hơn.
Bạn có thể kiểm tra xem giá trị thấp hơn có thực sự khiến Postgres sử dụng chỉ mục hay không (nhưng chỉ sử dụng chỉ mục này để thử nghiệm ):
EXPLAIN <query>; # Uses sequential scan
SET random_page_cost = 1;
EXPLAIN <query>; # May use index scan now
Bạn có thể khôi phục lại giá trị mặc định với SET random_page_cost = DEFAULT;
một lần nữa.
Quét chỉ mục yêu cầu tìm nạp trang đĩa không tuần tự. Postgres sử dụng random_page_cost
để ước tính chi phí của các lần tìm nạp không tuần tự như vậy liên quan đến các lần tìm nạp tuần tự. Giá trị mặc định là 4.0
, do đó giả định hệ số chi phí trung bình là 4 so với tìm nạp tuần tự (có tính đến hiệu ứng bộ nhớ đệm).
Tuy nhiên, vấn đề là giá trị mặc định này không phù hợp trong các trường hợp thực tế quan trọng sau:
1) Ổ đĩa thể rắn
Như tài liệu thừa nhận:
Lưu trữ có chi phí đọc ngẫu nhiên thấp so với tuần tự, ví dụ như ổ đĩa thể rắn, có thể được mô hình hóa tốt hơn với giá trị thấp hơn cho
random_page_cost
.
Theo điểm cuối cùng của trang trình bày này từ một bài phát biểu tại PostgresConf 2018, random_page_cost
nên được đặt thành một cái gì đó giữa 1.0
và 2.0
cho các ổ đĩa thể rắn.
2) Dữ liệu được lưu vào bộ nhớ đệm
Nếu dữ liệu chỉ mục cần thiết đã được lưu trong bộ nhớ cache trong RAM, thì quá trình quét chỉ mục sẽ luôn nhanh hơn đáng kể so với quét tuần tự. Tài liệu cho biết:
Tương ứng, nếu dữ liệu của bạn có khả năng nằm hoàn toàn trong bộ nhớ cache, [...] giảm
random_page_cost
có thể phù hợp.
Vấn đề là bạn tất nhiên không thể dễ dàng biết được liệu dữ liệu liên quan đã được lưu vào bộ nhớ đệm hay chưa. Tuy nhiên, nếu một chỉ mục cụ thể thường xuyên được truy vấn và nếu hệ thống có đủ RAM, thì dữ liệu có thể được lưu vào bộ nhớ đệm và random_page_cost
nên được đặt thành giá trị thấp hơn. Bạn sẽ phải thử nghiệm với các giá trị khác nhau và xem điều gì phù hợp với bạn.
Bạn cũng có thể muốn sử dụng tiện ích mở rộng pg_prewarm để lưu vào bộ nhớ đệm dữ liệu rõ ràng.
Câu hỏi về chính nó là rất nhiều không hợp lệ. Buộc (bằng cách làm enable_seqscan = off chẳng hạn) là một ý tưởng rất tồi. Có thể hữu ích để kiểm tra xem nó có nhanh hơn không, nhưng mã sản xuất không bao giờ được sử dụng các thủ thuật như vậy.
Thay vào đó - hãy giải thích phân tích truy vấn của bạn, đọc nó và tìm hiểu lý do tại sao PostgreSQL chọn kế hoạch xấu (theo ý kiến của bạn).
Có những công cụ trên web giúp đọc giải thích đầu ra phân tích - một trong số chúng là giải thích.depesz.com - do tôi viết.
Một tùy chọn khác là tham gia kênh #postgresql trên mạng freenode irc và nói chuyện với những người ở đó để giúp bạn - vì việc tối ưu hóa truy vấn không phải là vấn đề "đặt câu hỏi, nhận câu trả lời là hạnh phúc". nó giống một cuộc trò chuyện hơn, với nhiều điều cần kiểm tra, nhiều điều cần học hỏi.
Có một mẹo để thúc đẩy các postgres thích một máy quét seqs thêm một OFFSET 0
trong truy vấn con
Điều này rất hữu ích để tối ưu hóa các yêu cầu liên kết các bảng lớn / khổng lồ khi tất cả những gì bạn cần chỉ là n phần tử đầu tiên / cuối cùng.
Giả sử bạn đang tìm kiếm 20 phần tử đầu tiên / cuối cùng liên quan đến nhiều bảng có 100k (hoặc nhiều hơn) mục nhập, không có điểm xây dựng / liên kết tất cả truy vấn trên tất cả dữ liệu khi những gì bạn sẽ tìm kiếm nằm trong 100 hoặc 1000 đầu tiên mục. Trong trường hợp này, chẳng hạn, thực hiện quét tuần tự nhanh hơn 10 lần.