En una base de datos Postgres 9.1, tengo una tabla table1
con ~ 1.5M filas y una columna label
(nombres simplificados por el bien de esta pregunta).
Hay un funcional trigrama-índice en lower(unaccent(label))
( unaccent()
se ha hecho inmutable para permitir su uso en el índice).
La siguiente consulta es bastante rápida:
SELECT count(*) FROM table1
WHERE (lower(unaccent(label)) like lower(unaccent('%someword%')));
count
-------
1
(1 row)
Time: 394,295 ms
Pero la siguiente consulta es más lenta:
SELECT count(*) FROM table1
WHERE (lower(unaccent(label)) like lower(unaccent('%someword and some more%')));
count
-------
1
(1 row)
Time: 1405,749 ms
Y añadiendo más palabras es aún más lenta, a pesar de que la búsqueda es más estricta.
Intenté un simple truco para ejecutar una subconsulta para la primera palabra y luego una consulta con la cadena de búsqueda completa, pero (tristemente) el planificador de consultas vio a través de mis maquinaciones:
EXPLAIN ANALYZE
SELECT * FROM (
SELECT id, title, label from table1
WHERE lower(unaccent(label)) like lower(unaccent('%someword%'))
) t1
WHERE lower(unaccent(label)) like lower(unaccent('%someword and some more%'));
Bitmap Heap Scan en la tabla1 (costo = 16216.01..16220.04 filas = 1 ancho = 212) (tiempo real = 1824.017..1824.019 filas = 1 bucles = 1) Vuelva a verificar Cond: ((lower (unaccent ((label) :: text)) ~~ '% someword%' :: text) AND (lower (unaccent ((label) :: text)) ~~ '% someword y algo más %'::texto)) -> Escaneo de índice de mapa de bits en table1_label_hun_gin_trgm (costo = 0.00..16216.01 filas = 1 ancho = 0) (tiempo real = 1823.900..1823.900 filas = 1 bucles = 1) Índice Cond: ((lower (unaccent ((label) :: text)) ~~ '% someword%' :: text) AND (lower (unaccent ((label) :: text)) ~~ '% someword y algo más %'::texto)) Tiempo de ejecución total: 1824.064 ms
Mi problema principal es que la cadena de búsqueda proviene de una interfaz web que puede enviar cadenas bastante largo y por lo tanto ser muy lento y también puede constituir un vector de DOS.
Entonces mis preguntas son:
- ¿Cómo acelerar la consulta?
- ¿Hay una manera de dividirlo en sub consultas por lo que es más rápido?
- ¿Quizás una versión posterior de Postgres es mejor? (Intenté 9.4 y no parece más rápido: sigue siendo el mismo efecto. ¿Quizás una versión posterior?)
- ¿Quizás se necesita una estrategia de indexación diferente?
unaccent
inmutable. Agregué esto a la pregunta.
unaccent
módulo. Una de las razones por las que sugiero un envoltorio de funciones en su lugar.
unaccent()
también es proporcionado por un módulo adicional y Postgres no admite índices en la función de forma predeterminada, ya que no lo esIMMUTABLE
. Debe haber alterado algo y debe mencionar lo que hizo exactamente en su pregunta. Mi consejo permanente: stackoverflow.com/a/11007216/939860 . Además, los índices de trigram admiten compatibilidad sin distinción entre mayúsculas y minúsculas. Se puede simplificar a:WHERE f_unaccent(label) ILIKE f_unaccent('%someword%')
- con un índice coincidente. Detalles: stackoverflow.com/a/28636000/939860 .