Comparación entre la búsqueda de texto completo de PostgreSQL y Elasticsearch

Una sola base de datos o una pila de búsqueda real

Índice

El verdadero debate no es si PostgreSQL puede buscar texto o si Elasticsearch puede almacenar documentos. Ambos pueden hacerlo. La pregunta interesante es dónde debe residir la complejidad de la búsqueda.

La búsqueda de texto completo de PostgreSQL reside dentro de una base de datos relacional transaccional con tsvector, tsquery, diccionarios, clasificación (ranking) e índices GIN. Elasticsearch es un motor de búsqueda y análisis distribuido construido sobre Lucene, con analizadores, puntuación BM25, escalado basado en fragmentos (shards), agregaciones e indexación casi en tiempo real.

postgresql-vs-elasticsearch

Estas son filosofías operativas diferentes antes de ser listas de características diferentes.

Si está mapeando esta elección hacia el almacenamiento, las tuberías (pipelines) y las operaciones, esta visión general de la infraestructura de datos proporciona el contexto del sistema más amplio.

De qué trata realmente esta comparación

A un nivel bajo, ambos sistemas dependen de ideas de índices invertidos, pero los empaquetan de manera muy diferente. PostgreSQL recomienda GIN como el tipo de índice de búsqueda de texto preferido y lo describe como un índice invertido sobre lexemas en valores tsvector. Elasticsearch analiza campos text e los indexa para búsqueda de texto completo, y luego distribuye esos índices a través de fragmentos y nodos para escalar. En la práctica, PostgreSQL se siente como una búsqueda incrustada en la base de datos de la aplicación, mientras que Elasticsearch se siente como una plataforma de búsqueda dedicada con su propio tiempo de ejecución, ciclo de vida y modelo de escalado.

Esta comparación trata principalmente sobre la búsqueda de texto completo nativa de PostgreSQL más el muy común pg_trgm como ayuda para la coincidencia difusa. Ese alcance importa porque el ecosistema más amplio de PostgreSQL se está volviendo más pesado en búsqueda con el tiempo. Extensiones como RUM agregan un comportamiento de índice más rico para la búsqueda de frases y escaneos orientados a la clasificación, mientras que PGroonga extiende PostgreSQL con otra ruta de indexación de texto completo. Eso no hace que el PostgreSQL nativo sea igual a Elasticsearch, pero sí significa que la frontera es menos estática de lo que muchos comparaciones antiguas asumen.

Mi encuadre de opinión es simple. La búsqueda suele ser una característica hasta que se convierte en una superficie de producto. PostgreSQL tiende a ganar mientras la búsqueda sigue siendo una característica. Elasticsearch tiende a ganar cuando la búsqueda se convierte en lo que los usuarios juzgan primero. Eso es menos cuestión de nombres de marca y más sobre dónde se permite que vivan la lógica de relevancia, la política de indexación y el dolor operativo.

Cómo funciona la búsqueda de texto completo de PostgreSQL

La búsqueda de texto completo de PostgreSQL comienza convirtiendo el texto crudo en lexemas. to_tsvector tokeniza el texto, lo normaliza a través de los diccionarios configurados, descarta las palabras vacías y almacena los lexemas supervivientes con sus posiciones. setweight permite etiquetar lexemas de diferentes partes del documento, como título, resumen y cuerpo, para que esas partes puedan influir en la clasificación de manera diferente. PostgreSQL también soporta múltiples configuraciones de idioma predefinidas y permite construir configuraciones personalizadas con analizadores y diccionarios. Si desea una referencia SQL compacta mientras implementa estos patrones, esta hoja de trucos de PostgreSQL y esta hoja de trucos SQL con los comandos SQL más útiles son compañeros prácticos.

Un patrón de producción típico es una columna tsvector generada almacenada más un índice GIN. La documentación de PostgreSQL es directa en que la búsqueda de texto práctica generalmente requiere un índice, y muestra explícitamente una columna generada almacenada alimentando un índice GIN. Ese patrón evita recalcular to_tsvector durante la verificación y mantiene la superficie de consulta limpia.

alter table posts
  add column search_vector tsvector
  generated always as (
    setweight(to_tsvector('english', coalesce(title, '')), 'A') ||
    setweight(to_tsvector('english', coalesce(summary, '')), 'B') ||
    setweight(to_tsvector('english', coalesce(body, '')), 'D')
  ) stored;

create index posts_search_idx
  on posts using gin (search_vector);

select id,
       title,
       ts_rank_cd(
         search_vector,
         websearch_to_tsquery('english', '"query planner" -mysql')
       ) as rank
from posts
where search_vector @@ websearch_to_tsquery('english', '"query planner" -mysql')
order by rank desc
limit 20;

En el lado de la consulta, PostgreSQL le ofrece varios analizadores porque la entrada del usuario es más desordenada de lo que admiten los blogs de ingeniería. to_tsquery es explícito y poderoso. phraseto_tsquery preserva el orden de las palabras con el operador <->. websearch_to_tsquery acepta entradas similares a las de un motor de búsqueda, entiende frases entre comillas, OR y negación con -, y nunca genera errores de sintaxis con la entrada cruda del usuario. PostgreSQL también soporta la coincidencia de prefijos adjuntando * a un lexema en to_tsquery.

La clasificación es donde el PostgreSQL nativo muestra tanto su fortaleza como su límite. ts_rank y ts_rank_cd pueden usar frecuencia, proximidad y pesos estructurales, y el modelo de ponderación es sorprendentemente bueno para muchas tareas de búsqueda de aplicaciones. Al mismo tiempo, los propios documentos de PostgreSQL indican que la clasificación puede ser costosa y que las funciones de clasificación integradas no usan información global. Ese es el límite silencioso pero importante de la búsqueda de texto completo nativa de PostgreSQL. Puede clasificar, pero la relevancia no es el centro de gravedad del motor.

Cuando PostgreSQL es suficiente para la búsqueda de texto completo

PostgreSQL es suficiente más a menudo de lo que a los proveedores de búsqueda dedicados les gustaría. Es particularmente convincente cuando la búsqueda se mantiene muy cerca de las filas transaccionales, las uniones, los permisos y las escrituras recientes. El modelo MVCC de PostgreSQL proporciona consistencia transaccional y lecturas basadas en instantáneas, por lo que la misma base de datos que acepta la escritura puede responder a la búsqueda sin una ventana de actualización estilo Elasticsearch. Cuando una caja de búsqueda es realmente “encontrar registros dentro de la aplicación que acabo de editar”, esa propiedad importa más que las demostraciones de relevancia brillantes.

También es suficiente cuando el filtrado SQL es la mitad de la característica. Los filtros de estado, el aislamiento de inquilinos, los estados de publicación, las marcas de tiempo y las uniones relacionales a menudo importan tanto como la relevancia de palabras clave en los sistemas de línea de negocio. En esos casos, la búsqueda de texto completo de PostgreSQL se comporta como otro predicado indexado en un plan de consulta relacional, no como una plataforma separada que necesita ser alimentada y mantenida caliente. Esa es una arquitectura aburrida, y lo aburrido a menudo es el tipo de rapidez correcto.

Cómo funciona Elasticsearch como motor de búsqueda

Elasticsearch se presenta muy diferente. Sus propios documentos lo definen como un motor de búsqueda y análisis distribuido, almacén de datos escalable y base de datos vectorial construido sobre Apache Lucene, optimizado para velocidad y relevancia a escala de producción y operando casi en tiempo real. Elasticsearch divide cada índice en fragmentos, replica esos fragmentos y los distribuye a través de nodos para aumentar la capacidad de indexación y consulta. Por eso Elasticsearch rara vez es “solo un índice”. Es una arquitectura de clúster.

Bajo el capó, los analizadores hacen la mayor parte del trabajo pesado. Un analizador de Elasticsearch es una composición de filtros de caracteres, tokenizadores y filtros de tokens. Hay analizadores integrados, analizadores de idioma y analizadores personalizados, y el manejo de sinónimos es una parte de primera clase del análisis. Eso significa que el comportamiento de la búsqueda no es solo sobre la consulta. También es sobre cómo se normalizan tanto los documentos como las consultas antes de que comience la puntuación.

Para una referencia de API práctica mientras implementa estos patrones, esta hoja de trucos de Elasticsearch recopila comandos esenciales y atajos operativos.

PUT posts
{
  "mappings": {
    "properties": {
      "title":   { "type": "text" },
      "summary": { "type": "text" },
      "body":    { "type": "text" },
      "tags":    { "type": "keyword" }
    }
  }
}

GET posts/_search
{
  "query": {
    "bool": {
      "must": [
        {
          "multi_match": {
            "query": "query planner",
            "fields": ["title^3", "summary^2", "body"],
            "type": "best_fields"
          }
        }
      ],
      "must_not": [
        { "match": { "body": "mysql" } }
      ]
    }
  },
  "aggs": {
    "by_tag": {
      "terms": {
        "field": "tags"
      }
    }
  },
  "highlight": {
    "fields": {
      "body": {}
    }
  }
}

El DSL de consultas es donde Elasticsearch comienza a sentirse nativo de la búsqueda en lugar de parecido a una base de datos. bool combina cláusulas con must, should, filter y must_not. multi_match puede buscar a través de muchos campos con aumentos de campo y diferentes modos de ejecución como best_fields, most_fields, cross_fields, phrase y bool_prefix. Las agregaciones, resaltados y filtros pueden coexistir junto a la consulta principal en la misma solicitud. BM25 es el modelo de similitud predeterminado.

El modelo de frescura también es explícito. Elasticsearch es casi en tiempo real, no inmediatamente consistente en la búsqueda. Las operaciones recientes se vuelven visibles para la búsqueda cuando una actualización abre un nuevo segmento, y por defecto esa actualización ocurre cada segundo en índices que han sido consultados recientemente. Los documentos de Elastic también advierten que las actualizaciones son intensivas en recursos y recomiendan esperar actualizaciones periódicas o usar refresh=wait_for cuando un flujo de trabajo necesita visibilidad de búsqueda de lectura-después-escritura. Ese es un contrato muy diferente al de PostgreSQL.

Por qué Elasticsearch generalmente clasifica mejor la búsqueda compleja

Esta es la razón técnica más profunda por la que muchos equipos eventualmente se mudan de la búsqueda de texto completo de PostgreSQL a Elasticsearch. Las funciones de clasificación integradas de PostgreSQL no usan información global, mientras que Elasticsearch usa BM25 por defecto y expone configuraciones de similitud específicas de campo, analizadores, formas de consulta multi-campo y un DSL de búsqueda diseñado alrededor del ajuste de relevancia. Una vez que la búsqueda se vuelve menos sobre “si coincidió” y más sobre “por qué ganaron estos diez resultados”, Elasticsearch generalmente tiene más espacio expresivo.

Elasticsearch también tiene un sesgo claro hacia documentos desnormalizados. Su documentación de campos de unión advierte explícitamente contra modelar múltiples niveles de relaciones para replicar un esquema relacional y recomienda la desnormalización para un mejor rendimiento de búsqueda. Esa elección de diseño explica muchos de los puntos fuertes y las frustraciones de Elasticsearch. No intenta ser PostgreSQL con un LIKE más rápido. Intenta ser un motor de búsqueda que pueda puntuar y recuperar colecciones de documentos grandes rápidamente.

Búsqueda de texto completo de PostgreSQL vs Elasticsearch en características reales

La tolerancia a errores tipográficos es donde los dos sistemas divergen drásticamente. Elasticsearch proporciona consultas difusas basadas en la distancia de edición de Levenshtein y también ofrece tipos de campos de sugerencia y escritura a medida dedicada. La búsqueda de texto completo nativa de PostgreSQL no es tolerante a errores tipográficos por sí misma. La respuesta habitual de PostgreSQL es pg_trgm, que agrega operadores de similitud y soporte de índice para similitud de trigramas, LIKE e ILIKE. Eso funciona bien, pero es una estrategia de composición en lugar de un conjunto de características de motor de búsqueda integrado.

El resaltado existe en ambos stacks, pero los detalles de implementación cuentan una historia. PostgreSQL usa ts_headline, que puede devolver fragmentos útiles, pero los documentos indican que usa el documento original, puede ser lento y no está garantizado seguro para la inserción directa en páginas web. El resaltado de Elasticsearch puede usar desplazamientos de publicaciones o vectores de términos, lo cual es especialmente valioso en campos grandes porque evita reanalizar el texto completo para cada solicitud de resaltado. En resumen, PostgreSQL puede resaltar, mientras que Elasticsearch está construido para resaltar a escala.

Las facetas y la analítica de búsqueda son otra línea de falla. Elasticsearch trata las agregaciones como una parte de primera clase del modelo de búsqueda, con agregaciones métricas, de cubos y de tubería disponibles directamente en la respuesta de búsqueda. PostgreSQL obviamente puede agregar porque es SQL, pero una vez que los cubos contados, los histogramas y la analítica de búsqueda compuesta se convierten en parte del producto de búsqueda mismo, Elasticsearch se siente mucho más nativo. La diferencia no es de capacidad en principio. Es cuánto ergonómico de consulta y política de rendimiento dedica el motor a esa carga de trabajo.

La autocompletación sigue el mismo patrón. PostgreSQL puede hacer coincidencia de prefijos en to_tsquery, lo cual es útil y a menudo suficiente para herramientas internas. Elasticsearch va más lejos con campos search_as_you_type que construyen automáticamente múltiples subcampos analizados para completación de prefijos e infixes, además de sugeridores de completación construidos específicamente para sugerencias rápidas. Esa brecha es menor en un panel de administración y mayor en una superficie de descubrimiento orientada al usuario.

El costo operativo importa más que las capturas de pantalla de pruebas de rendimiento

La pregunta tentadora del motor de búsqueda es “¿Es Elasticsearch más rápido que PostgreSQL para la búsqueda?” La respuesta honesta es “¿para qué forma de búsqueda?” Elasticsearch está diseñado alrededor de fragmentos, réplicas, indexación por lotes, políticas de actualización y gestión del ciclo de vida. Los propios documentos de producción de Elastic profundizan en la estrategia de fragmentos, el tamaño de las solicitudes por lotes, el rendimiento de indexación, los intervalos de actualización e ILM. PostgreSQL evita un segundo clúster, pero el mantenimiento de GIN no es gratis. Los documentos de PostgreSQL advierten que las inserciones de GIN pueden ser lentas, que la limpieza de la lista pendiente puede causar fluctuaciones en el tiempo de respuesta y que la estrategia de autovacuum importa si el índice se actualiza intensamente.

Eso hace que la historia del rendimiento sea más matizada de lo que la mayoría de los posts de comparación admiten. Elasticsearch generalmente tiene más margen para la búsqueda léxica de gran tamaño top-N, facetas, autocompletado y volumen de lectura distribuido porque su arquitectura está dedicada a esas tareas. PostgreSQL a menudo se siente más rápido para consultas de aplicación relacionales con requisitos de frescura estrictos porque no hay un segundo almacén de datos, ningún límite de actualización y ninguna ruta de sincronización para depurar. El ganador suele ser la forma de la carga de trabajo, no la captura de pantalla de la prueba de rendimiento. Eso es parcialmente una inferencia, pero sigue directamente del modelo transaccional MVCC de PostgreSQL y del diseño basado en fragmentos casi en tiempo real de Elasticsearch.

¿Deben vivir los datos transaccionales y los índices de búsqueda en el mismo sistema? Cuando la relevancia de la búsqueda es moderada pero la frescura, los permisos y la verdad transaccional son críticos, el diseño del mismo sistema tiene ventajas obvias. Cuando la calidad de la búsqueda, las facetas, la política de sinónimos, la tolerancia a errores tipográficos y el escalado horizontal de búsqueda se convierten en preocupaciones de producto de primera clase, un segundo sistema comienza a parecer justificado. La propia guía de tamaño de fragmentos de Elasticsearch dice que no hay una estrategia única para todos y recomienda realizar pruebas de rendimiento con datos de producción en hardware de producción. Esa frase captura el compromiso perfectamente. Elasticsearch compra margen pidiéndole que opere una arquitectura más específica de búsqueda.

El veredicto práctico

La búsqueda de texto completo de PostgreSQL gana el primer 80 por ciento sorprendentemente a menudo. Soporta tokenización, palabras vacías, derivación (stemming), consultas de frases, pesos, clasificación, resaltado, vectores de búsqueda generados, índices GIN y ayudantes de similitud basados en trigramas. Combinado con la semántica transaccional de PostgreSQL, le da a muchas aplicaciones un stack de búsqueda que es simple, actual y cercano a los datos. Para oficinas de respaldo de SaaS, herramientas internas, sitios de contenido moderados y búsqueda nativa de aplicaciones, esa combinación es difícil de descartar.

Elasticsearch se vuelve convincente cuando la búsqueda no es meramente un filtro sino una superficie de producto. BM25 por defecto, analizadores personalizados, filtros de sinónimos, consultas difusas, clasificación multi-campo, agregaciones, opciones dedicadas de autocompletado, estrategias de resaltado de campos grandes y escalado distribuido basado en fragmentos no son características secundarias. Son la razón por la que el motor existe. Esa es la razón por la que las comparaciones de Elasticsearch que se enfocan solo en la latencia cruda generalmente pierden el punto. La diferencia más grande es cuánto lógica de producto de búsqueda está dispuesto el motor a asumir.

El modelo mental más limpio es este. La búsqueda de texto completo de PostgreSQL es excelente cuando la búsqueda pertenece a la base de datos. Elasticsearch es excelente cuando la base de datos debe alimentar una plataforma de búsqueda. La mayoría de los equipos se enfocan demasiado en la velocidad y se enfocan poco en los modos de fallo. El verdadero compromiso es dónde se permite que residan el ajuste de relevancia, la frescura de los datos y la complejidad operativa.