Ich habe also diese Tabelle mit 6,2 Millionen Datensätzen und muss Suchabfragen mit Ähnlichkeit für einen für die Spalte durchführen. Die Abfragen können sein:
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
Weitere Bedingungen können im where hinzugefügt werden (Jahr = X, Baustelle_Zustand = N, Status = 'zertifiziert', Visa_Klasse = Z).
Das Ausführen einiger dieser Abfragen kann sehr lange dauern, über 30 Sekunden. Manchmal länger als eine Minute.
EXPLAIN ANALYZE
der zuvor erwähnten Abfrage gibt mir Folgendes:
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
Ich kann nicht herausfinden, wie ich meine Spalte indizieren soll, damit sie blitzschnell ist.
EDIT: Hier ist die Postgres-Version:
PostgreSQL 9.3.5 unter x86_64-unknown-linux-gnu, kompiliert von gcc (Debian 4.7.2-5) 4.7.2, 64-Bit
Hier ist die Tabellendefinition:
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
und / oderstatus
Antworten:
Sie haben vergessen zu erwähnen, dass Sie das zusätzliche Modul installiert haben
pg_trgm
, das diesimilarity()
Funktion bereitstellt .Ähnlichkeitsoperator
%
Was auch immer Sie tun, verwenden Sie zunächst den Ähnlichkeitsoperator
%
anstelle des Ausdrucks(similarity(job_title, 'sales executive') > 0.6)
. Viel billiger. Die Indexunterstützung ist an Operatoren in Postgres gebunden , nicht an Funktionen.Um die gewünschte minimale Ähnlichkeit von zu erhalten
0.6
, führen Sie Folgendes aus:Die Einstellung bleibt für den Rest Ihrer Sitzung erhalten, sofern sie nicht auf etwas anderes zurückgesetzt wird. Überprüfen Sie mit:
Dies ist etwas ungeschickt, aber großartig für die Leistung.
Einfacher Fall
Wenn Sie nur die besten Übereinstimmungen in der Spalte
job_title
für die Zeichenfolge "Vertriebsleiter" wünschen, ist dies ein einfacher Fall für die Suche nach "nächsten Nachbarn" und könnte mit einem GiST-Index unter Verwendung der Trigrammoperatorklassegist_trgm_ops
(jedoch nicht mit einem GIN-Index) gelöst werden. ::Um auch eine Gleichheitsbedingung einzuschließen
worksite_city
, benötigen Sie das zusätzliche Modulbtree_gist
. Ausführen (einmal pro DB):Dann:
Abfrage:
<->
als "Distanz" -Operator:Postgres kann auch zwei separate Indizes kombinieren, einen einfachen btree-Index
worksite_city
und einen separaten GiST-Index.job_title
Der mehrspaltige Index sollte jedoch am schnellsten sein - wenn Sie die beiden Spalten in Abfragen regelmäßig wie folgt kombinieren.Dein Fall
Ihre Abfrage wird
salary
jedoch nach Entfernung und Ähnlichkeit sortiert , wodurch sich die Art des Spiels vollständig ändert. Jetzt können wir sowohl den GIN- als auch den GiST-Index verwenden, und GIN wird schneller sein (noch mehr in Postgres 9.4, das die GIN-Indizes stark verbessert hat - Hinweis!).Ähnliches gilt für die zusätzliche Gleichheitsprüfung
worksite_city
: Installieren Sie das Zusatzmodulbtree_gin
. Ausführen (einmal pro DB):Dann:
Abfrage:
Auch dies sollte (weniger effizient) mit dem einfacheren Index funktionieren, den Sie bereits haben (
"index_lcas_job_title_trigram"
), möglicherweise in Kombination mit anderen Indizes. Die beste Lösung hängt vom Gesamtbild ab.Nebenbei
Sie haben viele Indizes. Sind Sie sicher, dass sie alle in Gebrauch sind und ihre Wartungskosten bezahlen?
Sie haben einige zweifelhafte Datentypen:
Scheint so zu sein
date
. Usw.Verwandte Antworten:
quelle
"index_lcas_job_title_trigram" gin (job_title gin_trgm_ops)
irgendwo gelesen, dass Gin schneller ist als das Wesentliche. Ist das wahr?similarity
, daher ist es für diesen Zweck nicht schneller.btree_gin
. Aber dann sagen Sie bei der Indexerstellung, dass Sie Folgendes ausführen sollen:CREATE INDEX lcas_trgm_gin_idx ON lcas USING gist (worksite_city, job_title gist_trgm_ops);
Nur ein Tippfehler?