Chỉ số tốt nhất cho chức năng tương tự


8

Vì vậy, tôi có bảng này với 6,2 triệu bản ghi và tôi phải thực hiện các truy vấn tìm kiếm tương tự với một cho cột. Các truy vấn có thể là:

 SELECT  "lca_test".* FROM "lca_test"
 WHERE (similarity(job_title, 'sales executive') > 0.6)
 AND worksite_city = 'los angeles' 
 ORDER BY salary ASC LIMIT 50 OFFSET 0

Nhiều điều kiện có thể được thêm vào trong đó (năm = X, worksite_state = N, status = 'Certified', visa_group = Z).

Chạy một số truy vấn đó có thể mất nhiều thời gian, hơn 30 giây. Đôi khi hơn một phút.

EXPLAIN ANALYZE của truy vấn được đề cập trước đây cho tôi điều này:

Limit  (cost=0.43..42523.04 rows=50 width=254) (actual time=9070.268..33487.734 rows=2 loops=1)
->  Index Scan using index_lca_test_on_salary on lca_test  (cost=0.43..23922368.16 rows=28129 width=254) (actual time=9070.265..33487.727 rows=2 loops=1)
>>>> Filter: (((worksite_city)::text = 'los angeles'::text) AND (similarity((job_title)::text, 'sales executive'::text) > 0.6::double precision))
>>>> Rows Removed by Filter: 6330130 Total runtime: 33487.802 ms
Total runtime: 33487.802 ms

Tôi không thể tìm ra làm thế nào tôi nên lập chỉ mục cột của tôi để làm cho nó nhanh chóng.

EDIT: Đây là phiên bản postgres:

PostgreQuery 9.3.5 trên x86_64-unknown-linux-gnu, được biên dịch bởi gcc (Debian 4.7.2-5) 4.7.2, 64-bit

Đây là định nghĩa bảng:

                                                         Table "public.lca_test"
         Column         |       Type        |                       Modifiers                       | Storage  | Stats target | Description
------------------------+-------------------+-------------------------------------------------------+----------+--------------+-------------
 id                     | integer           | not null default nextval('lca_test_id_seq'::regclass) | plain    |              |
 raw_id                 | integer           |                                                       | plain    |              |
 year                   | integer           |                                                       | plain    |              |
 company_id             | integer           |                                                       | plain    |              |
 visa_class             | character varying |                                                       | extended |              |
 employement_start_date | character varying |                                                       | extended |              |
 employement_end_date   | character varying |                                                       | extended |              |
 employer_name          | character varying |                                                       | extended |              |
 employer_address1      | character varying |                                                       | extended |              |
 employer_address2      | character varying |                                                       | extended |              |
 employer_city          | character varying |                                                       | extended |              |
 employer_state         | character varying |                                                       | extended |              |
 employer_postal_code   | character varying |                                                       | extended |              |
 employer_phone         | character varying |                                                       | extended |              |
 employer_phone_ext     | character varying |                                                       | extended |              |
 job_title              | character varying |                                                       | extended |              |
 soc_code               | character varying |                                                       | extended |              |
 naic_code              | character varying |                                                       | extended |              |
 prevailing_wage        | character varying |                                                       | extended |              |
 pw_unit_of_pay         | character varying |                                                       | extended |              |
 wage_unit_of_pay       | character varying |                                                       | extended |              |
 worksite_city          | character varying |                                                       | extended |              |
 worksite_state         | character varying |                                                       | extended |              |
 worksite_postal_code   | character varying |                                                       | extended |              |
 total_workers          | integer           |                                                       | plain    |              |
 case_status            | character varying |                                                       | extended |              |
 case_no                | character varying |                                                       | extended |              |
 salary                 | real              |                                                       | plain    |              |
 salary_max             | real              |                                                       | plain    |              |
 prevailing_wage_second | real              |                                                       | plain    |              |
 lawyer_id              | integer           |                                                       | plain    |              |
 citizenship            | character varying |                                                       | extended |              |
 class_of_admission     | character varying |                                                       | extended |              |
Indexes:
    "lca_test_pkey" PRIMARY KEY, btree (id)
    "index_lca_test_on_id_and_salary" btree (id, salary)
    "index_lca_test_on_id_and_salary_and_year" btree (id, salary, year)
    "index_lca_test_on_id_and_salary_and_year_and_wage_unit_of_pay" btree (id, salary, year, wage_unit_of_pay)
    "index_lca_test_on_id_and_visa_class" btree (id, visa_class)
    "index_lca_test_on_id_and_worksite_state" btree (id, worksite_state)
    "index_lca_test_on_lawyer_id" btree (lawyer_id)
    "index_lca_test_on_lawyer_id_and_company_id" btree (lawyer_id, company_id)
    "index_lca_test_on_raw_id_and_visa_and_pw_second" btree (raw_id, visa_class, prevailing_wage_second)
    "index_lca_test_on_raw_id_and_visa_class" btree (raw_id, visa_class)
    "index_lca_test_on_salary" btree (salary)
    "index_lca_test_on_visa_class" btree (visa_class)
    "index_lca_test_on_wage_unit_of_pay" btree (wage_unit_of_pay)
    "index_lca_test_on_worksite_state" btree (worksite_state)
    "index_lca_test_on_year_and_company_id" btree (year, company_id)
    "index_lca_test_on_year_and_company_id_and_case_status" btree (year, company_id, case_status)
    "index_lcas_job_title_trigram" gin (job_title gin_trgm_ops)
    "lca_test_company_id" btree (company_id)
    "lca_test_employer_name" btree (employer_name)
    "lca_test_id" btree (id)
    "lca_test_on_year_and_companyid_and_wage_unit_and_salary" btree (year, company_id, wage_unit_of_pay, salary)
Foreign-key constraints:
    "fk_rails_8a90090fe0" FOREIGN KEY (lawyer_id) REFERENCES lawyers(id)
Has OIDs: no

Rõ ràng ít nhất phải bao gồm định nghĩa bảng (với các loại dữ liệu và ràng buộc chính xác) và phiên bản Postgres của bạn. Xem xét các hướng dẫn trong thông tin thẻ để hiệu suất postgresql . Cũng làm rõ nếu luôn luôn có một điều kiện bình đẳng trên worksite_city.
Erwin Brandstetter

Cảm ơn, tôi đã chỉnh sửa bài viết của mình để bao gồm những thông tin /. Và có luôn có một điều kiện bình đẳng trên worksite_city, worksite_state, yearvà / hoặc status
bl0b

Câu trả lời:


14

Bạn đã quên đề cập rằng bạn đã cài đặt mô-đun bổ sung pg_trgm, cung cấp similarity()chức năng.

Toán tử tương tự %

Trước hết, bất cứ điều gì bạn làm, sử dụng toán tử tương tự %thay vì biểu thức (similarity(job_title, 'sales executive') > 0.6). Rẻ hơn nhiều. Và hỗ trợ chỉ mục bị ràng buộc với các nhà khai thác trong Postgres, không phải cho các chức năng.

Để có được độ tương tự tối thiểu mong muốn 0.6, hãy chạy:

SELECT set_limit(0.6);

Cài đặt duy trì trong phần còn lại của phiên trừ khi đặt lại thành thứ khác. Kiểm tra với:

SELECT show_limit();

Điều này là một chút vụng về, nhưng tuyệt vời cho hiệu suất.

Trường hợp đơn giản

Nếu bạn chỉ muốn các kết quả phù hợp nhất trong cột job_titlecho chuỗi 'nhân viên bán hàng' thì đây sẽ là một trường hợp đơn giản của tìm kiếm "hàng xóm gần nhất" và có thể được giải quyết bằng chỉ mục GiST bằng lớp toán tử trigram gist_trgm_ops(nhưng không phải bằng chỉ số GIN) :

CREATE INDEX trgm_idx ON lcas USING gist (job_title gist_trgm_ops);

Để bao gồm một điều kiện bình đẳng trên worksite_citybạn sẽ cần mô-đun bổ sung btree_gist. Chạy (một lần cho mỗi DB):

CREATE EXTENSION btree_gist;

Sau đó:

CREATE INDEX lcas_trgm_gist_idx ON lcas USING gist (worksite_city, job_title gist_trgm_ops);

Truy vấn:

SELECT set_limit(0.6);  -- once per session

SELECT *
FROM   lca_test
WHERE  job_title % 'sales executive'
AND    worksite_city = 'los angeles' 
ORDER  BY (job_title <-> 'sales executive')
LIMIT  50;

<-> là toán tử "khoảng cách":

trừ đi similarity()giá trị

Postgres cũng có thể kết hợp hai chỉ mục riêng biệt, chỉ mục btree đơn giản worksite_cityvà chỉ mục GiST riêng trên job_title, nhưng chỉ mục nhiều màu nên nhanh nhất - nếu bạn thường xuyên kết hợp hai cột như thế này trong các truy vấn.

Trường hợp của bạn

Tuy nhiên, truy vấn của bạn sắp xếp theo salary, không phải theo khoảng cách / độ tương tự, làm thay đổi hoàn toàn bản chất của trò chơi. Bây giờ chúng ta có thể sử dụng cả chỉ số GIN và GiST và GIN sẽ nhanh hơn (thậm chí nhiều hơn trong Postgres 9.4, phần lớn đã cải thiện chỉ số GIN - gợi ý!)

Câu chuyện tương tự cho việc kiểm tra đẳng thức bổ sung trên worksite_city: cài đặt mô-đun bổ sung btree_gin. Chạy (một lần cho mỗi DB):

CREATE EXTENSION btree_gin;

Sau đó:

CREATE INDEX lcas_trgm_gin_idx ON lcas USING gin (worksite_city, job_title gin_trgm_ops);

Truy vấn:

SELECT set_limit(0.6);  -- once per session

SELECT *
FROM   lca_test
WHERE  job_title % 'sales executive'
AND    worksite_city = 'los angeles' 
ORDER  BY salary 
LIMIT  50 -- OFFSET 0

Một lần nữa, điều này cũng sẽ hoạt động (kém hiệu quả hơn) với chỉ mục đơn giản hơn bạn đã có ( "index_lcas_job_title_trigram"), có thể kết hợp với các chỉ mục khác. Giải pháp tốt nhất phụ thuộc vào bức tranh hoàn chỉnh.

Ngoài ra

  • Bạn có rất nhiều chỉ số. Bạn có chắc chắn rằng tất cả chúng đang được sử dụng và trả chi phí bảo trì?

  • Bạn có một số loại dữ liệu đáng ngờ:

    employement_start_date | character varying
    employement_end_date   | character varying
    

    Có vẻ như những người nên được date. Vân vân.

Câu trả lời liên quan:


Tôi đã "index_lcas_job_title_trigram" gin (job_title gin_trgm_ops)đọc ở đâu đó rằng gin nhanh hơn ý chính. Điều đó có đúng không?
bl0b

1
@ bl0b, gin hoàn toàn không hỗ trợ similarity, vì vậy mục đích đó không nhanh hơn.
jjanes

@ bl0b: Mặc dù jjanes đúng (và đó cũng là ý tưởng đầu tiên của tôi), trường hợp của bạn lại khác và bạn có thể sử dụng chỉ số GIN sau tất cả. Tôi đã thêm rất nhiều.
Erwin Brandstetter

@ErwinBrandstetter cảm ơn bạn rất nhiều vì câu trả lời! Câu hỏi nhanh: Bạn nói rằng GIN nhanh hơn và tôi nên cài đặt btree_gin. Nhưng sau đó trong việc tạo chỉ mục bạn bảo chạy: CREATE INDEX lcas_trgm_gin_idx ON lcas USING gist (worksite_city, job_title gist_trgm_ops);Chỉ là một lỗi đánh máy?
bl0b

1
@ErwinBrandstetter Đã đi từ 30 đến 6 giây. Những cải tiến tuyệt vời! Cảm ơn rất nhiều!
bl0b
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.