Entonces tengo esta tabla con 6.2 millones de registros y tengo que realizar consultas de búsqueda con similitud para una para la columna. Las consultas pueden ser:
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
Se pueden agregar más condiciones en where (año = X, worksite_state = N, status = 'certified', visa_class = Z).
La ejecución de algunas de esas consultas puede llevar mucho tiempo, más de 30 segundos. A veces más de un minuto.
EXPLAIN ANALYZE de la consulta mencionada anteriormente me da esto:
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
No puedo entender cómo debo indexar mi columna para que sea increíblemente rápida.
EDITAR: Aquí está la versión de postgres:
PostgreSQL 9.3.5 en x86_64-unknown-linux-gnu, compilado por gcc (Debian 4.7.2-5) 4.7.2, 64 bits
Aquí está la definición de la tabla:
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,yeary / ostatusRespuestas:
Olvidó mencionar que instaló el módulo adicional
pg_trgm, que proporciona lasimilarity()función.Operador de similitud
%En primer lugar, haga lo que haga, use el operador de similitud en
%lugar de la expresión(similarity(job_title, 'sales executive') > 0.6). Mucho mas barato. Y el soporte de índice está vinculado a operadores en Postgres, no a funciones.Para obtener la similitud mínima deseada de
0.6, ejecute:La configuración permanece para el resto de su sesión a menos que se restablezca a otra cosa. Verifícalo con:
Esto es un poco torpe, pero excelente para el rendimiento.
Caso simple
Si solo quisiera las mejores coincidencias en la columna
job_titlepara la cadena 'ejecutivo de ventas', este sería un caso simple de búsqueda de "vecino más cercano" y podría resolverse con un índice GiST utilizando la clase de operador trigramagist_trgm_ops(pero no con un índice GIN) :Para incluir también una condición de igualdad
worksite_city, necesitará el módulo adicionalbtree_gist. Ejecutar (una vez por DB):Entonces:
Consulta:
<->siendo el operador de "distancia":Postgres también puede combinar dos índices separados, un índice btree simple activado
worksite_cityy un índice GiST separado activadojob_title, pero el índice de varias columnas debería ser más rápido, si combina las dos columnas de esta manera en consultas regularmente.Tu caso
Sin embargo, su consulta se ordena por
salary, no por distancia / similitud, lo que cambia la naturaleza del juego por completo. Ahora podemos usar tanto el índice GIN como el GiST, y GIN será más rápido (aún más en Postgres 9.4, que ha mejorado en gran medida los índices GIN, ¡pista!)Historia similar para la verificación de igualdad adicional en
worksite_city: instalar el módulo adicionalbtree_gin. Ejecutar (una vez por DB):Entonces:
Consulta:
Nuevamente, esto también debería funcionar (menos eficientemente) con el índice más simple que ya tiene (
"index_lcas_job_title_trigram"), posiblemente en combinación con otros índices. La mejor solución depende de la imagen completa.Aparte
Tienes muchos índices. ¿Está seguro de que todos están en uso y pagan su costo de mantenimiento?
Tienes algunos tipos de datos dudosos:
Parece que esos deberían ser
date. Etc.Respuestas relacionadas:
fuente
"index_lcas_job_title_trigram" gin (job_title gin_trgm_ops)he leído en alguna parte que la ginebra es más rápida que la esencia. ¿Es eso cierto?similarityen absoluto, por lo que para ese propósito no es más rápido.btree_gin. Pero luego, en la creación del índice, le dice que ejecute: ¿CREATE INDEX lcas_trgm_gin_idx ON lcas USING gist (worksite_city, job_title gist_trgm_ops);Solo un error tipográfico?