Tối ưu hóa ĐẶT HÀNG B inNG trong một truy vấn tìm kiếm toàn văn


8

Tôi có một bảng lớn entitiesvới hồ sơ ~ 15M. Tôi muốn tìm 5 hàng đầu phù hợp với 'khúc côn cầu' trong đó name.

Tôi có một chỉ mục toàn văn trên name, được sử dụng:gin_ix_entity_full_text_search_name

Truy vấn:

 SELECT "entities".*,
         ts_rank(to_tsvector('english', "entities"."name"::text),
         to_tsquery('english', 'hockey'::text)) AS "rank0.48661998202865475"
    FROM "entities" 
         WHERE "entities"."place" = 'f' 
              AND (to_tsvector('english', "entities"."name"::text) @@ to_tsquery('english', 'hockey'::text)) 
         ORDER BY "rank0.48661998202865475" DESC LIMIT 5

Thời lượng 25.623 ms

Giải thích kế hoạch
1 Giới hạn (chi phí = 12666,89..12666,89 hàng = 5 chiều rộng = 3116)
2 -> Sắp xếp (chi phí = 12666,89..12670,18 hàng = 6571 chiều rộng = 3116)
3 Khóa sắp xếp: (ts_rank (to_tsvector ('english' :: regconfig, (name) :: text), '' 'hockey' '' :: tsquery))
4 -> Bitmap Heap Quét trên các thực thể (chi phí = 124,06..12645,06 hàng = 6571 width = 3116)
5 Kiểm tra lại Cond: (to_tsvector ('english' :: regconfig, (name) :: text) @@ '' 'hockey' '' :: tsquery)
6 Bộ lọc: (KHÔNG đặt)
7 -> Quét chỉ mục Bitmap trên gin_ix_entity_full lòng_search_name (chi phí = 0,00..123.74 hàng = 6625 width = 0)
8 Chỉ số Cond: (to_tsvector ('english' :: regconfig, (name) :: text) @@ '' 'hockey' '' :: tsquery)

Tôi không hiểu tại sao nó xác minh điều kiện chỉ số hai lần. (Kế hoạch truy vấn bước 4 và 7). Có phải vì điều kiện boolean của tôi ( not place)? Nếu vậy, tôi có nên thêm nó vào chỉ mục của mình để nhận được một truy vấn rất nhanh không? Hay là điều kiện phân loại làm cho nó chậm?

EXPLAIN ANALYZE đầu ra:

  Giới hạn (chi phí = 4447,28..4447,29 hàng = 5 chiều rộng = 3116) (thời gian thực tế = 18509.274..18509.282 hàng = 5 vòng = 1)
  -> Sắp xếp (chi phí = 4447.28..4448.41 hàng = 2248 width = 3116) (thời gian thực tế = 18509.271..18509.273 hàng = 5 vòng = 1)
         Khóa sắp xếp: (ts_rank (to_tsvector ('english' :: regconfig, (name) :: text), '' 'test' '' :: tsquery))
         Phương pháp sắp xếp: heapsort top-N Bộ nhớ: 19kB
     -> Quét bitmap trên các thực thể (chi phí = 43,31..4439.82 hàng = 2248 width = 3116) (thời gian thực tế = 119.003..18491.408 hàng = 2533 vòng = 1)
           Kiểm tra lại Cond: (to_tsvector ('english' :: regconfig, (name) :: text) @@ '' 'test' '' :: tsquery)
           Bộ lọc: (KHÔNG đặt)
           -> Quét chỉ mục Bitmap trên gin_ix_entity_full lòng_search_name (chi phí = 0,00..43.20 hàng = 2266 width = 0) (thời gian thực tế = 74.093..74.093 hàng = 2593 vòng = 1)
                 Chỉ mục Cond: (to_tsvector ('english' :: regconfig, (name) :: text) @@ '' 'test' '' :: tsquery)
 Tổng thời gian chạy: 18509.381 ms

Dưới đây là các thông số DB của tôi. Nó được lưu trữ bởi Heroku, trên các dịch vụ của Amazon. Họ mô tả nó có RAM 1,7 GB, 1 đơn vị xử lý và DB tối đa 1TB.

tên | thiết lập hiện tại
------------------------------ + ------------------- -------------------------------------------------- ------------------------------------
 phiên bản | PostgreSQL 9.0.7 trên i486-pc-linux-gnu, được biên soạn bởi GCC gcc-4.4.real (Ubuntu 4.4.3-4ubfox5) 4.4.3, 32-bit
 archive_command | kiểm tra -f /etc/postgresql/9.0/main/wal-ed/ARCHIVING_OFF || envdir /etc/postgresql/9.0/resource29857_heroku_com/wal-ed/env wal-e wal-đẩy% p
 archive_mode | trên
 archive_timeout | 1 phút
 checkpoint_completion_target | 0,7
 checkpoint_segments | 40
 client_min_messages | để ý
 cpu_index_tuple_cost | 0,001
 cpu_operator_cost | 0,0005
 cpu_tuple_cost | 0,003
 hiệu quả_cache_size | 1530000kB
 nóng_standby | trên
 lc_collate | vi_US.UTF-8
 lc_ctype | vi_US.UTF-8
 nghe_addresses | *
 log_checkpoint | trên
 log_destination | nhật ký hệ thống
 log_line_prefix | % u [VÀNG]
 log_min_duration_statement | 50ms
 log_min_messages | để ý
 log_collector | trên
 bảo trì_work_mem | 64 MB
 max_connections | 500
 max_prepared_transilities | 500
 max_stack_depth | 2 MB
 max_standby_archive_delay | -1
 max_standby_streaming_delay | -1
 max_wal_senders | 10
 cảng | 
 ngẫu nhiên_page_cost | 2
 server_encoding | UTF8
 shared_buffers | 415 MB
 ssl | trên
 syslog_ident | tài nguyên29857_heroku_com
 TimeZone | UTC
 wal_buffers | 8 MB
 wal_keep_segments | 127
 wal_level | nóng
 làm việc_mem | 100 MB
 (39 hàng)

BIÊN TẬP

Hình như ORDER BYlà phần chậm:

d6ifslbf0ugpu=> EXPLAIN ANALYZE SELECT "entities"."name",
     ts_rank(to_tsvector('english', "entities"."name"::text),
     to_tsquery('english', 'banana'::text)) AS "rank0.48661998202865475"
FROM "entities" 
     WHERE (to_tsvector('english', "entities"."name"::text) @@ to_tsquery('english', 'banana'::text)) 
     LIMIT 5;

QUERY PLAN                                                                         
-----------------------------------------------------------------------------------------------------------------------------------------------------------
 Limit  (cost=43.31..53.07 rows=5 width=24) (actual time=76.583..103.623 rows=5 loops=1)
->  Bitmap Heap Scan on entities  (cost=43.31..4467.60 rows=2266 width=24) (actual time=76.581..103.613 rows=5 loops=1)
     Recheck Cond: (to_tsvector('english'::regconfig, (name)::text) @@ '''banana'''::tsquery)
     ->  Bitmap Index Scan on gin_ix_entity_full_text_search_name  (cost=0.00..43.20 rows=2266 width=0) (actual time=53.592..53.592 rows=1495 loops=1)
           Index Cond: (to_tsvector('english'::regconfig, (name)::text) @@ '''banana'''::tsquery)
 Total runtime: 103.680 ms

Vs với ORDER BY:

d6ifslbf0ugpu=> EXPLAIN ANALYZE SELECT "entities"."name",
     ts_rank(to_tsvector('english', "entities"."name"::text),
     to_tsquery('english', 'banana'::text)) AS "rank0.48661998202865475"
FROM "entities" 
     WHERE (to_tsvector('english', "entities"."name"::text) @@ to_tsquery('english', 'banana'::text)) 
     ORDER BY "rank0.48661998202865475" DESC
     LIMIT 5;

QUERY PLAN                                                                           
---------------------------------------------------------------------------------------------------------------------------------------------------------------
Limit  (cost=4475.12..4475.13 rows=5 width=24) (actual time=15013.735..15013.741 rows=5 loops=1)
->  Sort  (cost=4475.12..4476.26 rows=2266 width=24) (actual time=15013.732..15013.735 rows=5 loops=1)
     Sort Key: (ts_rank(to_tsvector('english'::regconfig, (name)::text), '''banana'''::tsquery))
     Sort Method:  top-N heapsort  Memory: 17kB
     ->  Bitmap Heap Scan on entities  (cost=43.31..4467.60 rows=2266 width=24) (actual time=0.872..15006.763 rows=1495 loops=1)
           Recheck Cond: (to_tsvector('english'::regconfig, (name)::text) @@ '''banana'''::tsquery)
           ->  Bitmap Index Scan on gin_ix_entity_full_text_search_name  (cost=0.00..43.20 rows=2266 width=0) (actual time=0.549..0.549 rows=1495 loops=1)
                 Index Cond: (to_tsvector('english'::regconfig, (name)::text) @@ '''banana'''::tsquery)
 Total runtime: 15013.805 ms

Bit tôi vẫn không hiểu tại sao điều này chậm hơn. Có vẻ như nó đang tìm nạp cùng một lượng hàng từ Bitmap Heap Scan, nhưng mất nhiều thời gian hơn?


Nếu tăng work_mem không cung cấp đủ hiệu suất tăng, vui lòng hiển thị kết quả của GIẢI THÍCH ANALYZE thay vì chỉ GIẢI THÍCH. Nó cũng giúp hiển thị kết quả của việc chạy truy vấn trên trang này: wiki.postgresql.org/wiki/Server_Configuration Một mô tả ngắn gọn về phần cứng cũng giúp.
kgrittn

Đây là một
PHÂN TÍCH

Tôi nên chỉ ra rằng cấu hình PostgreQuery mà bạn hiển thị không giống với mức tối ưu. Tuy nhiên, điều đó đủ lạc đề đến mức có lẽ nó nên được giải quyết trong một câu hỏi riêng biệt. Tôi khuyên bạn nên làm như vậy.
kgrittn

Nếu bạn gặp khó khăn trong việc hiểu đầu ra GIẢI THÍCH ANALYZE của mình, có một trang Wiki sẽ giúp: wiki.postgresql.org/wiki/Using_EXPLAIN Rất nhiều người thấy trang giải thích.depesz.com hữu ích; bạn có thể muốn chọc xung quanh sự giúp đỡ và thử nó.
kgrittn

Câu trả lời:


8

Những gì tôi vẫn không hiểu, là tại sao điều này chậm hơn.

Việc sắp xếp các hàng sẽ có giá là một điều hiển nhiên. Nhưng sao nhiều thế?
Không có ORDER BY rank0...Postgres chỉ có thể

  • chọn 5 hàng đầu tiên nó tìm thấy và dừng tìm nạp các hàng ngay khi có 5 hàng.

    Bitmap Heap Quét trên các thực thể ... hàng = 5 ...

  • sau đó tính toán ts_rank()chỉ 5 hàng.
Trong trường hợp thứ hai, Postgres phải

  • tìm nạp tất cả (1495 theo kế hoạch truy vấn của bạn) đủ điều kiện.

    Bitmap Heap Quét trên các thực thể ... hàng = 1495 ...

  • tính toán ts_rank()cho tất cả chúng
  • sắp xếp tất cả chúng để tìm 5 đầu tiên theo giá trị tính toán.
Hãy thử ORDER BY namechỉ để xem chi phí điện toán to_tsquery('english', 'hockey'::text))cho các hàng thừa và số tiền còn lại để tìm nạp thêm hàng và sắp xếp.


Bộ nhớ đệm đi theo cách ... nó đại khái cho một hiệu suất là xấu. 10 giây 1500 hàng. Tôi hiểu lời giải thích của bạn. Nó có ý nghĩa. Nhưng trong khi thực hiện tìm kiếm văn bản .... có cách nào để xây dựng chỉ mục của tôi để sắp xếp chất lượng phù hợp mà không cần trích xuất mọi thứ không?
xlash

5

Quét bitmap hoạt động như thế này: Chỉ mục được quét để tìm vị trí của các bộ dữ liệu khớp với các điều kiện chỉ mục. Bitmap có thể trải qua sự kết hợp logic với kết quả của các lần quét bitmap khác, sử dụng logic boolean trên các bit. Sau đó, các trang giữ dữ liệu được truy cập theo thứ tự số trang, để giảm quyền truy cập đĩa và hy vọng biến một số lần đọc ngẫu nhiên thành đọc liên tiếp.

Quét chỉ mục bitmap có thể cần phải trở nên "mất" để phù hợp với bộ nhớ - nó làm giảm độ chính xác của nó từ cấp độ tuple xuống cấp độ trang. Nếu bạn tăng work_mem (ít nhất là cho một truy vấn này), bạn có thể tránh điều đó. Sẽ không thể bỏ qua quá trình quét heap bitmap trên dòng 4 hoặc bộ lọc trên dòng 6, nhưng có thể bỏ qua kiểm tra lại trên dòng 5.


1
Tăng từ 100MB lên 650MB, không có sự khác biệt về hiệu suất. (work_mem)
xlash

5

Vì câu hỏi được chỉnh sửa làm cho nó giống như mục tiêu là chọn một vài trận đấu hàng đầu, thay vì danh sách tất cả mọi thứ phù hợp, tôi sẽ đăng một câu trả lời riêng cho điều đó, vì đó là một vấn đề khá khác biệt.

Bạn có thể muốn xem xét chỉ mục KNN - GiST (đối với K Neighbor Neighbor - General Search Tree), có thể lấy ngay từ chỉ mục theo thứ tự tương tự - vì vậy bạn không cần phải đọc ngẫu nhiên tất cả các bộ dữ liệu heap và sắp xếp họ xuống để tìm K phù hợp nhất.

Đến nay tôi không nghĩ có ai đã triển khai hỗ trợ KNN-GIST cho các truy vấn tsearch (mặc dù tôi đã chắc chắn rằng nó có thể được thực hiện, đó chỉ là vấn đề ai đó dành thời gian để làm điều đó), nhưng có thể là hỗ trợ bát quái được thực hiện) sẽ làm việc cho ứng dụng của bạn. Sự khác biệt chính là các tìm kiếm bát quái không sử dụng từ điển để bắt nguồn và từ đồng nghĩa theo cách tsearch thực hiện; bạn sẽ chỉ tìm thấy từ khớp chính xác.

Để thử bát quái cho ví dụ của bạn, có lẽ bạn muốn lập chỉ mục "tên" như thế này:

CREATE INDEX entities_name_trgm ON entities USING gist (name gist_trgm_ops);

Sau đó, bạn có thể tìm kiếm như thế này:

SELECT
    *,
    name <-> 'banana' AS sim
  FROM entities 
  WHERE name % 'banana'
  ORDER BY sim DESC
  LIMIT 5;

Lưu ý các toán tử được sử dụng và ORDER BYsử dụng bí danh của cột "tương tự". Tôi sẽ không đi quá xa mô hình này khi thử nó. Chỉ mục trên tsvector không được sử dụng cho tìm kiếm này.

Việc chặn các vấn đề với cấu hình hiện tại của bạn (có thể dễ dàng ném toàn bộ VM của bạn vào trạng thái phân trang vô vọng từ bộ nhớ quá mức), bạn có thể sẽ thực sự thích hiệu suất của việc này. Cho dù nó có hành vi bạn muốn là những gì tôi không biết.

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.