Indeks terbaik untuk fungsi kesamaan


8

Jadi saya memiliki tabel ini dengan 6,2 juta catatan dan saya harus melakukan permintaan pencarian dengan kesamaan untuk satu untuk kolom. Pertanyaannya bisa:

 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

Lebih banyak kondisi dapat ditambahkan di mana (tahun = X, worksite_state = N, status = 'bersertifikat', visa_class = Z).

Menjalankan beberapa pertanyaan itu bisa memakan waktu sangat lama, lebih dari 30 detik. Terkadang lebih dari satu menit.

EXPLAIN ANALYZE kueri yang disebutkan sebelumnya memberi saya ini:

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

Saya tidak tahu bagaimana saya harus mengindeks kolom saya untuk membuatnya sangat cepat.

EDIT: Ini adalah versi postgres:

PostgreSQL 9.3.5 pada x86_64-unknown-linux-gnu, dikompilasi oleh gcc (Debian 4.7.2-5) 4.7.2, 64-bit

Berikut adalah definisi tabel:

                                                         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

Seharusnya jelas untuk setidaknya memasukkan definisi tabel (dengan tipe dan batasan data yang tepat) dan versi Postgres Anda. Pertimbangkan instruksi dalam tag-info untuk kinerja postgresql . Juga mengklarifikasi jika selalu ada kondisi kesetaraan worksite_city.
Erwin Brandstetter

Terima kasih, saya mengedit posting saya untuk memasukkan informasi tersebut. Dan ya selalu ada kondisi kesetaraan pada worksite_city, worksite_state, yeardan / atau status
bl0b

Jawaban:


14

Anda lupa menyebutkan bahwa Anda menginstal modul tambahan pg_trgm, yang menyediakan similarity()fungsi.

Operator kesamaan %

Pertama-tama, apa pun yang Anda lakukan, gunakan operator kesamaan %alih-alih ekspresi (similarity(job_title, 'sales executive') > 0.6). Lebih murah. Dan dukungan indeks terikat pada operator di Postgres, bukan ke fungsi.

Untuk mendapatkan kesamaan minimum yang diinginkan 0.6, jalankan:

SELECT set_limit(0.6);

Pengaturan tetap untuk sisa sesi Anda kecuali diatur ulang ke yang lain. Periksa dengan:

SELECT show_limit();

Ini agak canggung, tetapi bagus untuk kinerja.

Kasus sederhana

Jika Anda hanya menginginkan kecocokan terbaik dalam kolom job_titleuntuk 'eksekutif penjualan' string maka ini akan menjadi kasus sederhana pencarian "tetangga terdekat" dan dapat diselesaikan dengan indeks GiST menggunakan kelas operator trigram gist_trgm_ops(tetapi tidak dengan indeks GIN) :

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

Untuk juga memasukkan kondisi kesetaraan pada worksite_cityAnda akan membutuhkan modul tambahan btree_gist. Jalankan (sekali per DB):

CREATE EXTENSION btree_gist;

Kemudian:

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

Pertanyaan:

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;

<-> menjadi operator "jarak":

satu minus similarity()nilainya.

Postgres juga dapat menggabungkan dua indeks terpisah, indeks btree biasa worksite_city, dan indeks GiST terpisah job_title, tetapi indeks multikolom harus tercepat - jika Anda menggabungkan dua kolom seperti ini dalam kueri secara teratur.

Kasus Anda

Namun, kueri Anda diurutkan berdasarkan salary, bukan berdasarkan jarak / kesamaan, yang mengubah sifat permainan sepenuhnya. Sekarang kita dapat menggunakan indeks GIN dan GiST, dan GIN akan lebih cepat (bahkan lebih lagi di Postgres 9.4 yang sebagian besar telah meningkatkan indeks GIN - petunjuk!)

Kisah serupa untuk pemeriksaan kesetaraan tambahan tentang worksite_city: pasang modul tambahan btree_gin. Jalankan (sekali per DB):

CREATE EXTENSION btree_gin;

Kemudian:

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

Pertanyaan:

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

Sekali lagi, ini juga harus bekerja (kurang efisien) dengan indeks sederhana yang sudah Anda miliki ( "index_lcas_job_title_trigram"), mungkin dalam kombinasi dengan indeks lain. Solusi terbaik tergantung pada gambar lengkap.

Selain itu

  • Anda memiliki banyak indeks. Apakah Anda yakin mereka semua digunakan dan membayar biaya perawatannya?

  • Anda memiliki beberapa tipe data yang meragukan:

    employement_start_date | character varying
    employement_end_date   | character varying

    Sepertinya itu seharusnya date. Dll

Jawaban terkait:


Saya pernah "index_lcas_job_title_trigram" gin (job_title gin_trgm_ops)membaca di suatu tempat bahwa gin lebih cepat daripada inti. Benarkah?
bl0b

1
@ bl0b, gin tidak mendukung similaritysama sekali, jadi untuk tujuan itu tidak lebih cepat.
jjanes

@ bl0b: Meskipun jjanes benar (dan itu juga ide pertama saya), kasing Anda berbeda, dan toh Anda bisa menggunakan indeks GIN. Saya menambahkan lebih banyak.
Erwin Brandstetter

@ ErwinBrandstetter terima kasih banyak atas jawabannya! Pertanyaan cepat: Anda mengatakan bahwa GIN lebih cepat dan saya harus menginstal btree_gin. Tetapi kemudian dalam pembuatan indeks Anda mengatakan untuk menjalankan: CREATE INDEX lcas_trgm_gin_idx ON lcas USING gist (worksite_city, job_title gist_trgm_ops);Hanya salah ketik?
bl0b

1
@ErwinBrandstetter Pergi dari 30-an ke 6seconds. Perbaikan hebat! Terima kasih banyak!
bl0b
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.