Лучший индекс для функции подобия

8

Таким образом, у меня есть эта таблица с 6,2 миллионами записей, и я должен выполнить поисковые запросы с подобием для одного столбца. Запросы могут быть:

 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

Дополнительные условия могут быть добавлены в where (year = X, worksite_state = N, status = 'Certified', visa_class = Z).

Выполнение некоторых из этих запросов может занять очень много времени, более 30 секунд. Иногда больше минуты.

EXPLAIN ANALYZE из ранее упомянутого запроса дает мне это:

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

Я не могу понять, как мне индексировать мою колонку, чтобы она быстро вспыхнула.

РЕДАКТИРОВАТЬ: Вот версия postgres:

PostgreSQL 9.3.5 для x86_64-unknown-linux-gnu, скомпилированный gcc (Debian 4.7.2-5) 4.7.2, 64-битный

Вот определение таблицы:

                                                         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
bl0b
источник
Должно быть очевидно, по крайней мере, включить определение таблицы (с точными типами данных и ограничениями) и вашу версию Postgres. Рассмотрите инструкции в tag-info для postgresql-performance . Также уточните, всегда ли выполняется условие равенства worksite_city.
Эрвин Брандштеттер,
Спасибо, я отредактировал свой пост, чтобы включить эту информацию /. И да, всегда есть условие равенства на worksite_city, worksite_state, yearи / или status
bl0b

Ответы:

14

Вы забыли упомянуть, что вы установили дополнительный модуль pg_trgm, который обеспечивает similarity()функцию.

Оператор подобия %

Прежде всего, что бы вы ни делали, используйте %вместо выражения выражение сходства (similarity(job_title, 'sales executive') > 0.6). Намного дешевле. И поддержка индекса связана с операторами в Postgres, а не с функциями.

Чтобы получить желаемое минимальное сходство 0.6, запустите:

SELECT set_limit(0.6);

Настройка остается до конца вашего сеанса, пока не будет сброшена на что-то другое. Проверить с:

SELECT show_limit();

Это немного неуклюже, но отлично подходит для производительности.

Простой случай

Если бы вы просто хотели получить наилучшие совпадения в столбце job_titleдля строки «руководитель продаж», то это был бы простой случай поиска «ближайшего соседа», и его можно было бы решить с помощью индекса GiST с использованием класса операторов триграммы gist_trgm_ops(но не с помощью индекса GIN). :

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

Для включения условия равенства worksite_cityвам понадобится дополнительный модуль btree_gist. Выполнить (один раз на БД):

CREATE EXTENSION btree_gist;

Затем:

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

Запрос:

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;

<-> будучи оператором «расстояния»:

один минус similarity()значение.

Postgres также может объединять два отдельных индекса: обычный индекс btree worksite_cityи отдельный индекс GiST job_title, но многостолбцовый индекс должен быть самым быстрым - если вы регулярно объединяете два таких столбца в запросах.

Ваш случай

Однако ваш запрос сортируется salaryне по расстоянию / сходству, что полностью меняет характер игры. Теперь мы можем использовать как GIN, так и индекс GiST, и GIN будет быстрее (тем более в Postgres 9.4, который значительно улучшил индексы GIN - подсказка!)

Аналогичная история для проверки дополнительного равенства worksite_city: установите дополнительный модуль btree_gin. Выполнить (один раз на БД):

CREATE EXTENSION btree_gin;

Затем:

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

Запрос:

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

Опять же, это также должно работать (менее эффективно) с более простым индексом, который у вас уже есть ( "index_lcas_job_title_trigram"), возможно, в сочетании с другими индексами. Лучшее решение зависит от полной картины.

Asides

  • У вас много индексов. Вы уверены, что они все в использовании и оплачивают расходы по их обслуживанию?

  • У вас есть несколько сомнительных типов данных:

    employement_start_date | character varying
    employement_end_date   | character varying

    Похоже, так должно быть date. И т.п.

Связанные ответы:

Эрвин Брандштеттер
источник
Я "index_lcas_job_title_trigram" gin (job_title gin_trgm_ops)где-то читал, что джин быстрее, чем гист. Это правда?
bl0b
1
@ bl0b, джин вообще не поддерживает similarity, поэтому для этого не быстрее.
Джанес
@ bl0b: Хотя jjanes прав (и это было моей первой идеей), у вас другой случай, и вы все-таки можете использовать индекс GIN. Я добавил намного больше.
Эрвин Брандштеттер
@ErwinBrandstetter большое спасибо за ответ! Быстрый вопрос: Вы говорите, что GIN быстрее и что я должен установить btree_gin. Но затем при создании индекса вы говорите: « CREATE INDEX lcas_trgm_gin_idx ON lcas USING gist (worksite_city, job_title gist_trgm_ops);Просто опечатка?
bl0b
1
@ErwinBrandstetter Пошел от 30 секунд до 6 секунд. Большие улучшения! Большое спасибо!
bl0b