PostgreSQL
 sql >> Cơ Sở Dữ Liệu >  >> RDS >> PostgreSQL

Postgres không sử dụng chỉ mục khi quét chỉ mục là tùy chọn tốt hơn nhiều

Quét chỉ mục (Chỉ) -> Quét chỉ mục bitmap -> Quét tuần tự

Đối với một số hàng, nó trả tiền để chạy quét chỉ mục. Nếu tất cả các trang dữ liệu đều có thể nhìn thấy đủ các trang dữ liệu (=đủ chân không và tải ghi đồng thời không quá nhiều) và chỉ mục có thể cung cấp tất cả các giá trị cột cần thiết, thì chỉ mục quét nhanh hơn được sử dụng. Với nhiều hàng hơn dự kiến ​​sẽ được trả lại (tỷ lệ phần trăm của bảng cao hơn và tùy thuộc vào phân phối dữ liệu, tần số giá trị và độ rộng của hàng), nhiều khả năng sẽ tìm thấy nhiều hàng trên một trang dữ liệu hơn. Sau đó, nó trả tiền để chuyển sang quét chỉ mục bitmap. (Hoặc kết hợp nhiều chỉ mục riêng biệt.) Dù sao đi nữa thì một phần trăm lớn các trang dữ liệu vẫn phải được truy cập, sẽ rẻ hơn nếu bạn chạy quét tuần tự, lọc các hàng thặng dư và bỏ qua hoàn toàn chi phí cho các chỉ mục.

Việc sử dụng chỉ mục trở nên rẻ hơn (nhiều) và có nhiều khả năng hơn khi truy cập các trang dữ liệu theo thứ tự ngẫu nhiên không đắt hơn (nhiều) so với việc truy cập chúng theo thứ tự tuần tự. Đó là trường hợp khi sử dụng SSD thay vì đĩa quay, hoặc thậm chí nhiều hơn để càng nhiều càng được lưu trong bộ nhớ cache - và các thông số cấu hình tương ứng random_page_costeffective_cache_size được đặt cho phù hợp.

Trong trường hợp của bạn, Postgres chuyển sang quét tuần tự, hy vọng tìm thấy rows=263962 , đó đã là 3% của toàn bộ bảng. (Trong khi chỉ rows=47935 thực sự được tìm thấy, hãy xem bên dưới.)

Thêm trong câu trả lời có liên quan này:

  • Truy vấn PostgreSQL hiệu quả trên dấu thời gian bằng cách sử dụng chỉ mục hoặc quét chỉ mục bitmap?

Cẩn thận với việc buộc các kế hoạch truy vấn

Bạn không thể bắt buộc một phương pháp lập kế hoạch nhất định trực tiếp trong Postgres, nhưng bạn có thể thực hiện khác các phương pháp dường như cực kỳ tốn kém cho mục đích gỡ lỗi. Xem Cấu hình Phương pháp Lập kế hoạch trong sách hướng dẫn.

SET enable_seqscan = off (như được gợi ý trong một câu trả lời khác) thực hiện điều đó với các lần quét tuần tự. Nhưng điều đó chỉ dành cho mục đích gỡ lỗi trong phiên của bạn. Đừng không sử dụng cài đặt này làm cài đặt chung trong quá trình sản xuất trừ khi bạn biết chính xác mình đang làm gì. Nó có thể buộc các kế hoạch truy vấn vô lý. Hướng dẫn sử dụng:

Các tham số cấu hình này cung cấp một phương pháp thô sơ để ảnh hưởng đến các kế hoạch truy vấn được chọn bởi trình tối ưu hóa truy vấn. Nếu kế hoạch mặc định được trình tối ưu hóa chọn cho một truy vấn cụ thể không phải là tối ưu, một tạm thời giải pháp là sử dụng một trong các thông số cấu hình này để buộc trình tối ưu hóa chọn một gói khác. Các cách tốt hơn để cải thiện chất lượng của các kế hoạch do trình tối ưu hóa chọn bao gồm điều chỉnh các hằng số chi phí của trình lập kế hoạch (xem Phần 19.7.2), chạy ANALYZE theo cách thủ công, tăng giá trị của default_statistics_target tham số cấu hình và tăng số lượng thống kê được thu thập cho các cột cụ thể bằng cách sử dụng ALTER TABLE SET STATISTICS .

Đó đã là hầu hết những lời khuyên bạn cần.

  • Giữ cho PostgreSQL đôi khi chọn một kế hoạch truy vấn không tốt

Trong trường hợp cụ thể này, Postgres dự kiến ​​số lần truy cập vào email_activities.email_recipient_id nhiều gấp 5-6 lần so với thực tế được tìm thấy:

ước tính rows=227007 so với actual ... rows=40789
ước tính rows=263962 so với actual ... rows=47935

Nếu bạn chạy truy vấn này thường xuyên, bạn sẽ phải trả tiền để có ANALYZE nhìn vào một mẫu lớn hơn để có số liệu thống kê chính xác hơn về cột cụ thể. Bảng của bạn lớn (~ 10 triệu hàng), vì vậy hãy làm như vậy:

ALTER TABLE email_activities ALTER COLUMN email_recipient_id
SET STATISTICS 3000;  -- max 10000, default 100

Sau đó, ANALYZE email_activities;

Biện pháp cuối cùng

Trong rất hiếm các trường hợp bạn có thể sử dụng để buộc chỉ mục với SET LOCAL enable_seqscan = off trong một giao dịch riêng biệt hoặc trong một chức năng với môi trường riêng của nó. Như:

CREATE OR REPLACE FUNCTION f_count_dist_recipients(_email_campaign_id int, _limit int)
  RETURNS bigint AS
$func$
   SELECT COUNT(DISTINCT a.email_recipient_id)
   FROM   email_activities a
   WHERE  a.email_recipient_id IN (
      SELECT id
      FROM   email_recipients
      WHERE  email_campaign_id = $1
      LIMIT  $2)       -- or consider query below
$func$  LANGUAGE sql VOLATILE COST 100000 SET enable_seqscan = off;

Cài đặt chỉ áp dụng cho phạm vi cục bộ của hàm.

Cảnh báo: Đây chỉ là một bằng chứng về khái niệm. Ngay cả sự can thiệp thủ công ít triệt để hơn nhiều này cũng có thể gây hại cho bạn về lâu dài. Cardinalities, tần số giá trị, lược đồ của bạn, cài đặt Postgres toàn cầu, mọi thứ thay đổi theo thời gian. Bạn sẽ nâng cấp lên phiên bản Postgres mới. Kế hoạch truy vấn mà bạn bắt buộc bây giờ, có thể trở thành một ý tưởng rất tồi sau này.

Và thông thường, đây chỉ là một giải pháp thay thế cho sự cố với thiết lập của bạn. Tốt hơn hãy tìm và sửa nó.

Truy vấn thay thế

Thông tin cần thiết bị thiếu trong câu hỏi, nhưng truy vấn tương đương này có thể nhanh hơn và nhiều khả năng sử dụng chỉ mục trên (email_recipient_id ) - ngày càng như vậy để có LIMIT lớn hơn .

SELECT COUNT(*) AS ct
FROM  (
   SELECT id
   FROM   email_recipients
   WHERE  email_campaign_id = 1607
   LIMIT  43000
   ) r
WHERE  EXISTS (
   SELECT FROM email_activities
   WHERE  email_recipient_id = r.id);


  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Cách Cosh () hoạt động trong PostgreSQL

  2. Các phương pháp hay nhất về bảo mật PostgreSQL

  3. Hướng dẫn bất hợp pháp:4 khi chạy Django

  4. Công bố Barman 1.0, Trình quản lý sao lưu và phục hồi cho PostgreSQL

  5. Chiến lược hiệu quả để để lại dấu vết kiểm tra / lịch sử thay đổi cho các ứng dụng DB?