Таким образом, у меня есть эта таблица с 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
worksite_city
.worksite_city
,worksite_state
,year
и / илиstatus
Ответы:
Вы забыли упомянуть, что вы установили дополнительный модуль
pg_trgm
, который обеспечиваетsimilarity()
функцию.Оператор подобия
%
Прежде всего, что бы вы ни делали, используйте
%
вместо выражения выражение сходства(similarity(job_title, 'sales executive') > 0.6)
. Намного дешевле. И поддержка индекса связана с операторами в Postgres, а не с функциями.Чтобы получить желаемое минимальное сходство
0.6
, запустите:Настройка остается до конца вашего сеанса, пока не будет сброшена на что-то другое. Проверить с:
Это немного неуклюже, но отлично подходит для производительности.
Простой случай
Если бы вы просто хотели получить наилучшие совпадения в столбце
job_title
для строки «руководитель продаж», то это был бы простой случай поиска «ближайшего соседа», и его можно было бы решить с помощью индекса GiST с использованием класса операторов триграммыgist_trgm_ops
(но не с помощью индекса GIN). :Для включения условия равенства
worksite_city
вам понадобится дополнительный модульbtree_gist
. Выполнить (один раз на БД):Затем:
Запрос:
<->
будучи оператором «расстояния»:Postgres также может объединять два отдельных индекса: обычный индекс btree
worksite_city
и отдельный индекс GiSTjob_title
, но многостолбцовый индекс должен быть самым быстрым - если вы регулярно объединяете два таких столбца в запросах.Ваш случай
Однако ваш запрос сортируется
salary
не по расстоянию / сходству, что полностью меняет характер игры. Теперь мы можем использовать как GIN, так и индекс GiST, и GIN будет быстрее (тем более в Postgres 9.4, который значительно улучшил индексы GIN - подсказка!)Аналогичная история для проверки дополнительного равенства
worksite_city
: установите дополнительный модульbtree_gin
. Выполнить (один раз на БД):Затем:
Запрос:
Опять же, это также должно работать (менее эффективно) с более простым индексом, который у вас уже есть (
"index_lcas_job_title_trigram"
), возможно, в сочетании с другими индексами. Лучшее решение зависит от полной картины.Asides
У вас много индексов. Вы уверены, что они все в использовании и оплачивают расходы по их обслуживанию?
У вас есть несколько сомнительных типов данных:
Похоже, так должно быть
date
. И т.п.Связанные ответы:
источник
"index_lcas_job_title_trigram" gin (job_title gin_trgm_ops)
где-то читал, что джин быстрее, чем гист. Это правда?similarity
, поэтому для этого не быстрее.btree_gin
. Но затем при создании индекса вы говорите: «CREATE INDEX lcas_trgm_gin_idx ON lcas USING gist (worksite_city, job_title gist_trgm_ops);
Просто опечатка?