Sự không phù hợp lớn giữa kích thước chỉ mục được báo cáo và số lượng bộ đệm trong kế hoạch thực hiện


10

Vấn đề

Chúng tôi có một truy vấn như

SELECT COUNT(1) 
  FROM article
  JOIN reservation ON a_id = r_article_id 
 WHERE r_last_modified < now() - '8 weeks'::interval 
   AND r_group_id = 1 
   AND r_status = 'OPEN';

Vì nó chạy vào thời gian chờ (sau 10 phút) thường xuyên hơn không, tôi quyết định điều tra vấn đề.

Đầu EXPLAIN (ANALYZE, BUFFERS)ra trông như thế này:

 Aggregate  (cost=264775.48..264775.49 rows=1 width=0) (actual time=238960.290..238960.291 rows=1 loops=1)
   Buffers: shared hit=200483 read=64361 dirtied=666 written=8, temp read=3631 written=3617
   I/O Timings: read=169806.955 write=0.154
   ->  Hash Join  (cost=52413.67..264647.65 rows=51130 width=0) (actual time=1845.483..238957.588 rows=21644 loops=1)
         Hash Cond: (reservation.r_article_id = article.a_id)
         Buffers: shared hit=200483 read=64361 dirtied=666 written=8, temp read=3631 written=3617
         I/O Timings: read=169806.955 write=0.154
         ->  Index Scan using reservation_r_article_id_idx1 on reservation  (cost=0.42..205458.72 rows=51130 width=4) (actual time=34.035..237000.197 rows=21644 loops=1)
               Filter: ((r_group_id = 1) AND (r_status = 'OPEN') AND (r_last_modified < (now() - '56 days'::interval)))
               Rows Removed by Filter: 151549
               Buffers: shared hit=200193 read=48853 dirtied=450 written=8
               I/O Timings: read=168614.105 write=0.154
         ->  Hash  (cost=29662.22..29662.22 rows=1386722 width=4) (actual time=1749.392..1749.392 rows=1386814 loops=1)
               Buckets: 32768  Batches: 8  Memory Usage: 6109kB
               Buffers: shared hit=287 read=15508 dirtied=216, temp written=3551
               I/O Timings: read=1192.850
               ->  Seq Scan on article  (cost=0.00..29662.22 rows=1386722 width=4) (actual time=23.822..1439.310 rows=1386814 loops=1)
                     Buffers: shared hit=287 read=15508 dirtied=216
                     I/O Timings: read=1192.850
 Total runtime: 238961.812 ms

Nút thắt cổ chai rõ ràng là quét chỉ mục. Vì vậy, hãy xem định nghĩa chỉ mục:

CREATE INDEX reservation_r_article_id_idx1 
    ON reservation USING btree (r_article_id)
 WHERE (r_status <> ALL (ARRAY['FULFILLED', 'CLOSED', 'CANCELED']));

Kích thước và số hàng

Kích thước của nó (được báo cáo bởi \di+hoặc bằng cách truy cập tệp vật lý) là 36 MB. Vì các đặt phòng thường chỉ dành một khoảng thời gian tương đối ngắn trong tất cả các trạng thái không được liệt kê ở trên, có rất nhiều cập nhật xảy ra, vì vậy chỉ số này khá phình to (khoảng 24 MB bị lãng phí ở đây) - tuy nhiên, kích thước tương đối nhỏ.

Chiếc reservationbàn có kích thước khoảng 3,8 GB, chứa khoảng 40 triệu hàng. Số lượng đặt phòng chưa được đóng là khoảng 170.000 (số chính xác được báo cáo trong nút quét chỉ mục ở trên).

Bây giờ thật bất ngờ: quét chỉ mục báo cáo tìm nạp một lượng lớn bộ đệm (nghĩa là, các trang 8 kb):

Buffers: shared hit=200193 read=48853 dirtied=450 written=8

Các số được đọc từ bộ đệm và đĩa (hoặc bộ đệm của hệ điều hành) thêm tới 1,9 GB!

Trường hợp xấu nhất

Mặt khác, trường hợp xấu nhất, khi mỗi bộ dữ liệu nằm trên một trang khác nhau của bảng, sẽ chiếm tài khoản truy cập (21644 + 151549) + 4608 trang (tổng số hàng được lấy từ bảng cộng với số trang chỉ mục từ vật lý kích thước). Con số này vẫn chỉ dưới 180.000 - thấp hơn nhiều so với mức gần 250.000 được quan sát.

Điều thú vị (và có thể quan trọng) là tốc độ đọc đĩa khoảng 2,2 MB / s, điều này khá bình thường, tôi đoán vậy.

Vậy thì sao?

Có ai có ý tưởng về sự khác biệt này có thể đến từ đâu không?

Lưu ý: Để rõ ràng, chúng tôi có ý tưởng cần cải thiện / thay đổi ở đây, nhưng tôi thực sự muốn hiểu những con số tôi nhận được - đây là những gì câu hỏi là về.

Cập nhật: kiểm tra hiệu quả của bộ nhớ đệm hoặc vi lọc

Dựa trên câu trả lời của jjanes , tôi đã kiểm tra xem điều gì sẽ xảy ra khi tôi chạy lại chính xác cùng một truy vấn. Số lượng bộ đệm bị ảnh hưởng không thực sự thay đổi. (Để làm điều này, tôi đã đơn giản hóa truy vấn đến mức tối thiểu mà vẫn hiển thị vấn đề.) Đây là những gì tôi thấy từ lần chạy đầu tiên:

 Aggregate  (cost=240541.52..240541.53 rows=1 width=0) (actual time=97703.589..97703.590 rows=1 loops=1)
   Buffers: shared hit=413981 read=46977 dirtied=56
   I/O Timings: read=96807.444
   ->  Index Scan using reservation_r_article_id_idx1 on reservation  (cost=0.42..240380.54 rows=64392 width=0) (actual time=13.757..97698.461 rows=19236 loops=1)
         Filter: ((r_group_id = 1) AND (r_status = 'OPEN') AND (r_last_modified < (now() - '56 days'::interval)))
         Rows Removed by Filter: 232481
         Buffers: shared hit=413981 read=46977 dirtied=56
         I/O Timings: read=96807.444
 Total runtime: 97703.694 ms

và sau cái thứ hai:

 Aggregate  (cost=240543.26..240543.27 rows=1 width=0) (actual time=388.123..388.124 rows=1 loops=1)
   Buffers: shared hit=460990
   ->  Index Scan using reservation_r_article_id_idx1 on reservation  (cost=0.42..240382.28 rows=64392 width=0) (actual time=0.032..385.900 rows=19236 loops=1)
         Filter: ((r_group_id = 1) AND (r_status = 'OPEN') AND (r_last_modified < (now() - '56 days'::interval)))
         Rows Removed by Filter: 232584
         Buffers: shared hit=460990
 Total runtime: 388.187 ms

1
Có lẽ không liên quan nhưng bạn có cần tham gia articlekhông? Có vẻ như tất cả các cột liên quan là từ reservationbảng và (giả sử) có FK, kết quả sẽ giống nhau.
ypercubeᵀᴹ

Đó là một câu hỏi rất hay. Và bạn đã đúng, không cần thiết - đây là một truy vấn được sử dụng trong giám sát bởi một nhóm khác. Tuy nhiên, ít nhất là nhìn vào kế hoạch truy vấn, mọi thứ khác chỉ là trang trí cho việc quét chỉ mục khó chịu đó :)
dezso

1
Hãy để tôi nói thêm rằng việc xóa tham gia không tạo ra sự khác biệt lớn - quá trình quét chỉ mục bị che khuất vẫn còn đó.
dezso

Truy cập bảng bánh mì nướng? Mặc dù tôi nghi ngờ bất kỳ cột nào bạn hiển thị sẽ được nướng. Nếu bạn có một bản sao nhàn rỗi của cơ sở dữ liệu cho mục đích thử nghiệm, bạn có thể chạy pg_stat_reset()trên nó, sau đó chạy truy vấn, và sau đó nhìn vào pg_statio_user_tablesđể xem nó thuộc tính các khối ở đâu.
jjanes

Câu trả lời:


4

Tôi nghĩ rằng chìa khóa ở đây là rất nhiều cập nhật và sự phình to về chỉ số.

Chỉ mục chứa các con trỏ tới các hàng trong bảng không còn 'sống'. Đây là các phiên bản cũ của hàng cập nhật. Các phiên bản hàng cũ được giữ xung quanh trong một thời gian, để đáp ứng các truy vấn với ảnh chụp nhanh cũ và sau đó được giữ lại một lúc nữa vì không ai muốn thực hiện công việc loại bỏ chúng thường xuyên hơn mức cần thiết.

Khi quét chỉ mục, nó phải truy cập vào các hàng này và sau đó thông báo chúng không còn hiển thị nữa, vì vậy bỏ qua chúng. Các explain (analyze,buffers)tuyên bố không báo cáo về hoạt động này một cách rõ ràng, ngoại trừ thông qua đếm của bộ đệm đọc / hit trong quá trình kiểm tra những hàng này.

Có một số mã "microvacuum" cho btrees, như vậy khi quét trở lại chỉ mục một lần nữa, nó nhớ rằng con trỏ mà nó đuổi theo không còn tồn tại và đánh dấu nó là đã chết trong chỉ mục. Bằng cách đó, truy vấn tương tự tiếp theo được chạy không cần phải truy tìm lại. Vì vậy, nếu bạn chạy lại cùng một truy vấn, có thể bạn sẽ thấy các truy cập bộ đệm giảm xuống gần hơn với những gì bạn dự đoán.

Bạn cũng có thể VACUUMbảng thường xuyên hơn, điều đó sẽ dọn sạch các bộ dữ liệu chết ra khỏi bảng, không chỉ ra khỏi chỉ mục một phần. Nhìn chung, các bảng có chỉ số một phần quay vòng cao có thể được hưởng lợi từ chân không mạnh hơn so với mức mặc định.


Xin vui lòng xem chỉnh sửa của tôi - đối với tôi, nó trông giống như bộ nhớ đệm, không phải vi lọc.
dezso

Số mới của bạn khác nhiều so với số cũ của bạn (gần gấp đôi), vì vậy thật khó để hiểu ý nghĩa của chúng mà không nhìn thấy các số mới cho các hàng và hàng thực tế được lọc để quét chỉ mục.
jjanes

Đã thêm các kế hoạch đầy đủ như họ nhìn ngày hôm nay. Số lượng bộ đệm bị ảnh hưởng đã tăng rất nhiều kể từ thứ Sáu, cũng như số lượng hàng.
dezso

Bạn có giao dịch lâu dài treo xung quanh? Nếu vậy, có thể quét chỉ mục vẫn đang theo dõi các hàng không hiển thị với nó (điều này gây ra các lần truy cập bộ đệm bổ sung), nhưng nó không thể di chuyển chúng đi vì chúng có thể được nhìn thấy bởi người khác có tuổi ảnh chụp nhanh.
jjanes

Tôi không có gì - giao dịch thông thường mất ít hơn một giây. Thỉnh thoảng một vài giây, nhưng không lâu hơn.
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.