Làm cách nào để buộc Postgres sử dụng một chỉ mục cụ thể?


111

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ự?



1
+1 Tôi rất thích xem tính năng này. Vấn đề không đơn giản là vô hiệu hóa quá trình quét seq, như các câu trả lời khác nói: chúng tôi cần khả năng buộc PG sử dụng một chỉ mục cụ thể . Điều này là do trong số liệu thống kê từ thực tế có thể hoàn toàn sai và tại thời điểm đó, bạn cần sử dụng các giải pháp thay thế một phần / không đáng tin cậy. Tôi đồng ý rằng trong những trường hợp đơn giản, trước tiên bạn nên kiểm tra các chỉ mục và các cài đặt khác, nhưng để có độ tin cậy và sử dụng nâng cao trên dữ liệu lớn, chúng tôi cần điều này.
collimarco

MySQL và Oracle đều có nó ... Không chắc tại sao trình lập kế hoạch của Postgres lại không đáng tin cậy như vậy.
Kevin Parker

Câu trả lời:


103

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_seqscanenable_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?

  • Đối với các bảng nhỏ, việc quét tuần tự sẽ nhanh hơn.
  • Postgres không sử dụng các chỉ mục khi kiểu dữ liệu không khớp đúng cách, bạn có thể cần phải bao gồm các phôi thích hợp.
  • Cài đặt lập kế hoạch của bạn có thể gây ra sự cố.

Xem thêm bài đăng nhóm tin cũ này .


4
Đồng ý, Buộc người đăng bài làm theo cách của bạn thường có nghĩa là bạn đã làm sai. 9/10 lần người lập kế hoạch sẽ đánh bại bất cứ thứ gì bạn có thể nghĩ ra. Lần khác là do bạn làm sai.
Kent Fredric

Tôi nghĩ rằng đó là một ý tưởng tốt để kiểm tra các lớp toán tử thực sự của việc giữ chỉ mục của bạn.
metdos

2
Tôi ghét phải làm lại một câu hỏi cũ nhưng tôi thường thấy trong tài liệu Postgres, các cuộc thảo luận và ở đây, nhưng có một khái niệm tổng quát cho những gì đủ điều kiện cho một bảng nhỏ không? Nó có phải là một cái gì đó giống như 5000 hàng, hoặc 50000, vv?
waffl

1
@waffl Bạn đã xem xét điểm chuẩn chưa? Tạo một bảng đơn giản với một chỉ mục và một hàm đi kèm để lấp đầy nó với n hàng rác ngẫu nhiên. Sau đó, bắt đầu xem xét kế hoạch truy vấn cho các giá trị khác nhau của n . Khi bạn nhìn thấy nó bắt đầu sử dụng chỉ mục, bạn sẽ có câu trả lời là quả bóng. Bạn cũng có thể nhận được quét tuần tự nếu PostgreSQL xác định (dựa trên số liệu thống kê) rằng quá trình quét chỉ mục sẽ không loại bỏ rất nhiều hàng. Vì vậy, điểm chuẩn luôn là một ý tưởng hay khi bạn thực sự lo lắng về hiệu suất. Như một phỏng đoán đơn giản, mang tính giai thoại, tôi muốn nói rằng một vài nghìn thường là "nhỏ".
jpmc26

9
Với hơn 30 năm kinh nghiệm trên các nền tảng như Oracle, Teradata và MSSQL, tôi thấy trình tối ưu hóa của PostgreSQL 10 không đặc biệt thông minh. Ngay cả với số liệu thống kê cập nhật, nó tạo ra các kế hoạch thực thi kém hiệu quả hơn so với việc bị ép buộc theo một hướng đặc biệt. Cung cấp các gợi ý về cấu trúc để bù đắp những vấn đề này sẽ cung cấp giải pháp cho phép PostgreSQL phát triển ở nhiều phân khúc thị trường hơn. IMHO.
Guido Leenders

75

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ỏ.


41
câu trả lời ngắn này thực sự mang lại một gợi ý tốt cho mục đích thử nghiệm
dwery

3
Không ai trả lời câu hỏi!
Ivailo Bardarov

@IvailoBardarov Lý do tất cả các đề xuất khác là ở đây là vì PostgreSQL không có tính năng này; đây là một quyết định có ý thức của các nhà phát triển dựa trên cách nó thường được sử dụng và các vấn đề lâu dài mà nó gây ra.
jpmc26

Một thủ thuật rất hay để kiểm tra: chạy set enable_seqscan=false, chạy truy vấn của bạn, và sau đó nhanh chóng chạy set enable_seqscan=truetrở 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!)
Brian Hellekin

2
@BrianHellekin tốt hơn, SET SESSION enable_seqscan=falseđể chỉ ảnh hưởng đến bản thân
Izkata

19

Đô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

3
Ý kiến ​​hay. Tuy nhiên, khi chúng tôi vô hiệu hóa việc sử dụng chỉ mục hiện tại bằng phương pháp này - trình tối ưu hóa truy vấn postgresql dự phòng cho chỉ mục phù hợp tiếp theo. Do đó, không có gì đảm bảo rằng trình tối ưu hóa sẽ chọn 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.
Agnius Vasiliauskas

Điều gì xảy ra nếu không có wheređiều kiện nhưng hai bảng hoặc được nối và Postgres không lấy chỉ mục.
Luna Lovegood

@Surya này áp dụng ở trên để cả hai WHERE và để THAM GIA ... Về điều kiện
Ziggy Crueltyfree Zeitgeister

18

Câu trả lời ngắn

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_costthô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.

Lý lịch

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_costnên được đặt thành một cái gì đó giữa 1.02.0cho 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_costcó 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_costnê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.



2
Tôi thậm chí đã phải đặt random_page_cost = 0,1 để quét chỉ mục hoạt động trên bảng lớn (~ 600 triệu hàng) trong Pg 10.1 trên Ubuntu. Nếu không có tinh chỉnh, quá trình quét seq (mặc dù diễn ra song song) mất 12 phút (Lưu ý rằng bảng Phân tích đã được thực hiện!). Ổ cứng là SSD. Sau khi chỉnh sửa, thời gian thực thi trở thành 1 giây.
Anatoly Alekseev

Bạn đã cứu ngày của tôi. Tôi đã phát điên khi cố gắng tìm ra cách mà cùng một truy vấn chính xác trên cùng một cơ sở dữ liệu đã mất 30 giây trên một máy và ít hơn 1 trên một máy khác, ngay cả sau khi chạy phân tích ở cả hai đầu ... Người mà nó có thể quan tâm: lệnh ' ALTER SYSTEM SET random_page_cost = x 'đặt giá trị mặc định mới trên toàn cầu.
Julien

10

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.


2

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 0trong 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.

xem Làm cách nào để ngăn Postgres nội dòng truy vấn con?


Bí quyết đẹp. Mặc dù tối ưu hóa tốt nên tất nhiên tối ưu hóa đi các offset 0 :-)
Guido Leenders
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.