J'ai donc ce tableau avec 6,2 millions d'enregistrements et je dois effectuer des requêtes de recherche avec une similitude pour un pour la colonne. Les requêtes peuvent être:
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
Plus de conditions peuvent être ajoutées dans le où (année = X, worksite_state = N, statut = 'certifié', visa_class = Z).
L'exécution de certaines de ces requêtes peut prendre très longtemps, plus de 30 secondes. Parfois plus d'une minute.
EXPLAIN ANALYZE
de la requête mentionnée précédemment me donne ceci:
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
Je ne peux pas comprendre comment je dois indexer ma colonne pour la rendre rapide.
EDIT: Voici la version postgres:
PostgreSQL 9.3.5 sur x86_64-unknown-linux-gnu, compilé par gcc (Debian 4.7.2-5) 4.7.2, 64 bits
Voici la définition du tableau:
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
et / oustatus
Réponses:
Vous avez oublié de mentionner que vous avez installé le module supplémentaire
pg_trgm
, qui fournit lasimilarity()
fonction.Opérateur de similarité
%
Tout d'abord, quoi que vous fassiez, utilisez l'opérateur de similarité
%
au lieu de l'expression(similarity(job_title, 'sales executive') > 0.6)
. Beaucoup moins cher. Et le support d'index est lié aux opérateurs dans Postgres, pas aux fonctions.Pour obtenir la similitude minimale souhaitée
0.6
, exécutez:Le paramètre reste pour le reste de votre session, sauf s'il est réinitialisé sur autre chose. Vérifier avec:
C'est un peu maladroit, mais idéal pour les performances.
Cas simple
Si vous vouliez simplement les meilleures correspondances dans la colonne
job_title
pour la chaîne 'sales executive', ce serait un cas simple de recherche du "plus proche voisin" et pourrait être résolu avec un index GiST en utilisant la classe d'opérateur trigrammegist_trgm_ops
(mais pas avec un index GIN) :Pour inclure également une condition d'égalité,
worksite_city
vous auriez besoin du module supplémentairebtree_gist
. Exécuter (une fois par DB):Alors:
Requete:
<->
étant l'opérateur "distance":Postgres peut également combiner deux index distincts, un index btree simple sur
worksite_city
activé et un index GiST distinct activéjob_title
, mais l'index multicolonne doit être le plus rapide - si vous combinez régulièrement les deux colonnes comme celle-ci dans les requêtes.Ton cas
Cependant, votre requête est triée par
salary
, et non par distance / similitude, ce qui change complètement la nature du jeu. Maintenant, nous pouvons utiliser à la fois les index GIN et GiST, et GIN sera plus rapide (encore plus dans Postgres 9.4 qui a largement amélioré les index GIN - indice!)Histoire similaire pour la vérification d'égalité supplémentaire
worksite_city
: installez le module supplémentairebtree_gin
. Exécuter (une fois par DB):Alors:
Requete:
Encore une fois, cela devrait également fonctionner (moins efficacement) avec l'index plus simple que vous avez déjà (
"index_lcas_job_title_trigram"
), éventuellement en combinaison avec d'autres index. La meilleure solution dépend de l'image complète.À part
Vous avez beaucoup d'index. Êtes-vous sûr qu'ils sont tous en service et paient leurs frais d'entretien?
Vous avez des types de données douteux:
On dirait que ça devrait l'être
date
. Etc.Réponses associées:
la source
"index_lcas_job_title_trigram" gin (job_title gin_trgm_ops)
lu quelque part que le gin est plus rapide que l'essentiel. Est-ce vrai?similarity
du tout, donc à cette fin n'est pas plus rapide.btree_gin
. Mais ensuite, dans la création de l'index, vous dites d'exécuter:CREATE INDEX lcas_trgm_gin_idx ON lcas USING gist (worksite_city, job_title gist_trgm_ops);
juste une faute de frappe?