Miglior indice per la funzione di somiglianza


8

Quindi ho questa tabella con 6,2 milioni di record e devo eseguire query di ricerca con somiglianza con una per la colonna. Le query possono essere:

 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

Altre condizioni possono essere aggiunte nel dove (anno = X, worksite_state = N, status = 'certificato', visa_class = Z).

L'esecuzione di alcune di queste query può richiedere molto tempo, oltre 30 secondi. A volte più di un minuto.

EXPLAIN ANALYZE della query menzionata in precedenza mi dà questo:

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

Non riesco a capire come dovrei indicizzare la mia colonna per renderla velocissima.

EDIT: Ecco la versione di postgres:

PostgreSQL 9.3.5 su x86_64-unknown-linux-gnu, compilato da gcc (Debian 4.7.2-5) 4.7.2, 64-bit

Ecco la definizione della tabella:

                                                         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

Dovrebbe essere ovvio includere almeno la definizione della tabella (con tipi di dati e vincoli esatti) e la tua versione di Postgres. Considera le istruzioni nel tag-info per postgresql-performance . Chiarire anche se esiste sempre una condizione di uguaglianza worksite_city.
Erwin Brandstetter,

Grazie, ho modificato il mio post per includere quelle informazioni /. E sì, c'è sempre una condizione di uguaglianza worksite_city, worksite_state, yeare / o status
bl0b

Risposte:


14

Hai dimenticato di dire che hai installato il modulo aggiuntivo pg_trgm, che fornisce la similarity()funzione.

Operatore di somiglianza %

Prima di tutto, qualunque cosa tu faccia, usa l'operatore di somiglianza %invece dell'espressione (similarity(job_title, 'sales executive') > 0.6). Più economico. E il supporto degli indici è legato agli operatori di Postgres, non alle funzioni.

Per ottenere la somiglianza minima desiderata di 0.6, eseguire:

SELECT set_limit(0.6);

L'impostazione rimane per il resto della sessione, a meno che non venga ripristinata su qualcos'altro. Controllare con:

SELECT show_limit();

Questo è un po 'goffo, ma ottimo per le prestazioni.

Caso semplice

Se volessi semplicemente le migliori corrispondenze nella colonna job_titleper la stringa "dirigente di vendita", questo sarebbe un semplice caso di ricerca "vicino più vicino" e potrebbe essere risolto con un indice GiST usando la classe operatore trigramma gist_trgm_ops(ma non con un indice GIN) :

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

Per includere anche una condizione di uguaglianza worksite_cityè necessario il modulo aggiuntivo btree_gist. Esegui (una volta per DB):

CREATE EXTENSION btree_gist;

Poi:

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

Query:

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;

<-> essendo l'operatore "distanza":

uno meno il similarity()valore.

Postgres può anche combinare due indici separati, un semplice indice btree su worksite_citye un indice GiST separato su job_title, ma l'indice a più colonne dovrebbe essere più veloce - se si combinano regolarmente le due colonne in questo modo nelle query.

Il tuo caso

Tuttavia, la query viene ordinata per salary, non per distanza / somiglianza, il che cambia completamente la natura del gioco. Ora possiamo usare sia l'indice GIN che GiST, e GIN sarà più veloce (ancora di più in Postgres 9.4 che ha ampiamente migliorato gli indici GIN - suggerimento!)

Storia simile per il controllo di uguaglianza aggiuntivo su worksite_city: installa il modulo aggiuntivo btree_gin. Esegui (una volta per DB):

CREATE EXTENSION btree_gin;

Poi:

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

Query:

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

Ancora una volta, questo dovrebbe funzionare anche (in modo meno efficiente) con l'indice più semplice che hai già ( "index_lcas_job_title_trigram"), possibilmente in combinazione con altri indici. La soluzione migliore dipende dal quadro completo.

asides

  • Hai molti indici. Sei sicuro che siano tutti in uso e paghino i loro costi di manutenzione?

  • Hai alcuni tipi di dati dubbi:

    employement_start_date | character varying
    employement_end_date   | character varying
    

    Sembra che dovrebbero essere quelli date. Eccetera.

Risposte correlate:


Devo aver "index_lcas_job_title_trigram" gin (job_title gin_trgm_ops)letto da qualche parte che il gin è più veloce dell'essenza. È vero?
bl0b

1
@ bl0b, gin non supporta similarityaffatto, quindi a tale scopo non è più veloce.
jjanes,

@ bl0b: Mentre jjanes ha ragione (e questa è stata anche la mia prima idea), il tuo caso è diverso e dopo tutto puoi usare un indice GIN. Ho aggiunto molto di più.
Erwin Brandstetter,

@ErwinBrandstetter grazie mille per la risposta! Domanda veloce: dici che GIN è più veloce e che dovrei installare btree_gin. Ma poi nella creazione dell'indice dici di eseguire: CREATE INDEX lcas_trgm_gin_idx ON lcas USING gist (worksite_city, job_title gist_trgm_ops);solo un errore di battitura?
bl0b

1
@ErwinBrandstetter è passato da 30 a 6 secondi. Grandi miglioramenti! Molte grazie!
bl0b
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.