Un hombre volvió a casa después de dar la vuelta al mundo durante once años. Al día siguiente, cuando le dijo a su mujer que iba a la tienda de la esquina, ella le preguntó: "¿vas por el camino corto o por el largo?".
Las consultas pueden ejecutarse de muchas formas distintas. Todos los caminos conducen al mismo resultado de consulta. La herramienta NoSQL y de optimización de consultas evalúa las posibilidades y selecciona el plan eficiente. La eficiencia se mide en latencia y rendimiento, dependiendo de la carga de trabajo. El coste del uso de memoria, CPU y disco se añade al coste de un plan en un optimizador basado en costes.
Ahora bien, en la mayoría de los casos, un Base de datos NoSQL tendrá Consulta tipo SQL soporte de idiomas. Por lo tanto, un buen optimizador es obligatorio. Cuando no se dispone de un buen optimizador, los desarrolladores tienen que vivir con restricciones de funciones y los administradores de bases de datos tienen que vivir con problemas de rendimiento de las consultas NoSQL.
Optimizador de bases de datos para mejorar el rendimiento de las búsquedas NoSQL
La optimización de consultas NosSQL permite elegir un índice y unas rutas de acceso óptimas para ejecutar la búsqueda. A muy alto nivel, los optimizadores de SQL deciden lo siguiente antes de crear el árbol de ejecución:
- Reescritura de consultas basada en la heurística, el coste o ambos.
- Al igual que un editor elimina las palabras innecesarias de un escrito, el trabajo de reescritura de consultas puede abarcar desde la eliminación de predicados innecesarios hasta el aplanamiento de subconsultas, la conversión de LEFT OUTER JOINS adecuados en INNER JOINS, el plegado de tablas derivadas, etc.
- Selección del índice.
- Selección del índice o índices óptimos para cada una de las tablas (keyspaces en Couchbase N1QL, colección en el caso de las mejores prácticas de rendimiento de MongoDB)
- En función del índice seleccionado, elija los predicados que se van a empujar hacia abajo, vea si la consulta está cubierta o no, decida la estrategia de ordenación y paginación.
- Reordenación de uniones
- (A INNER JOIN B INNER JOIN C) es equivalente a (B INNER JOIN C INNER JOIN A). El optimizador tendrá que determinar la forma más óptima de secuenciar estas uniones.
- Tipo de unión
- Las bases de datos pueden implementar múltiples tipos de algoritmos de unión: bucle anidado, hash, sort merge, zigzag, estrella (copo de nieve), etc. En función de la estructura y el coste, el optimizador tendrá que decidir el tipo de algoritmo de unión para cada operación de unión.
Consideremos el caso de la restricción de MongoDB. "Una colección puede tener como máximo un texto índice". https://docs.mongodb.com/manual/core/index-text/#restrictions Además, documenta otras restricciones. Para este artículo, bastará con explicar esta única restricción.
¿Por qué debería importarle esta restricción?
- MongoDB y otras bases de datos NoSQL le animan a desnormalizar (agregar) su esquema para crear un único documento de gran tamaño que representa un objeto: un cliente, un socio, etc por lo que la mayoría de sus operaciones ocurren en un único documento (JSON). Así, un único documento de cliente puede contener información del cliente, pedidos de clientes, información de envío del cliente, información de facturación del cliente. Tener un único índice de búsqueda significa que tienes que crear un único índice MUY GRANDE que combine todos los campos en los que quieras buscar. Este es el problema: cuando busca la dirección del cliente, no quiere ver la dirección de envío. Cuando busque el ID de pedido de envío, no querrá ver el ID de pedido devuelto.
- En MongoDB se pueden crear múltiples índices sobre escalares. Por qué la restricción en el índice de texto?
¿Por qué el índice de texto de MongoDB está restringido a un índice por colección?
-
- ¿Es el número de índices de texto? Los índices de búsqueda se construyen normalmente con un estructura de datos de árbol invertido. Pero.., MongoDB ha optado por construir con el índice B-Tree. Es poco probable que este sea el problema.
- ¿Es el tamaño de los índices de texto? Los índices de texto generan un array de tokens sobre el texto y los indexa. Por lo tanto, es un índice de matriz. Su tamaño puede crecer exponencialmente cuando se utiliza un índice de matriz. El tamaño del índice aumenta linealmente al número de palabras indexadas y no al número de documentos. Esto puede causar problemas.
- ¿Es un problema del optimizador? Cuando tienes varios índices, el optimizador tendrá que elegir el índice adecuado para la consulta. Si se restringe a un índice de texto, la elección es fácil. Esto es un síntoma de un problema mayor en las técnicas de optimización de MongoDB - Toma decisiones adhoc que resultan en restricciones como esta.
El lenguaje de planes de consulta de MongoDB es simplista, aunque intente imitan las operaciones SQL.. Veamos cómo gestiona esto la herramienta de optimización de consultas de MongoDB.
- Reescritura de consultas: Sin soporte. Las consultas de MongoDB son simplistas en los métodos find(), save(), remove(), update(). La tubería de agregación es procedimental y verbosa. Aunque teóricamente es posible reescribir, no hay nada en la documentación o en el plan que indique una reescritura de las consultas.
- Selección del índice: Soporte. El optimizador de MongoDB intenta elegir un índice adecuado para cada parte de la consulta y el índice puede/debe ser utilizado. Más sobre esto a continuación.
- Reordenación de uniones: Sin soporte. $lookup de MongoDB forma parte del enrevesado marco de agregación en el que la consulta se escribe como el pipeline de Unix, un enfoque procedimental.
- Selección del tipo de unión: Sin soporte ya que sólo hay un tipo de join en MongoDB. MongoDB tiene un soporte de unión exterior izquierda restringida a través del operador $lookup - las matrices no están soportadas en la condición de unión. Si utiliza $lookup, el optimizador utilizará automáticamente el algoritmo de unión predeterminado. No se menciona el tipo de unión realizada.
Esencialmente, la optimización de consultas de MongoDB sólo hace la selección de índices antes de crear el plan de ejecución. Pero, la optimización de consultas en MongoDB parece seleccionar los índices de una manera extraña - ni por regla ni por estadística.
- Elige un índice aleatorio en uno o varios índices cualificados.
- Utilice ese plan si una consulta posterior coincide con los predicados de la consulta, aunque las constantes, selectividades y cardinalidades sean diferentes.
- Luego, en tiempo de ejecución, si el escaneo del índice devuelve más de 100 claves (¡!), ejecuta cada uno de los planes alternativos para ver cuál devuelve las claves primero. En algún momento, aborta la ejecución paralela y recoge uno de ellos. También sustituye el plan en su caché de planes.
1 2 3 4 5 6 7 8 9 10 |
Colección t1, con 3000 documenbts. Cree el siguiente índices: Anexo 1 para el definición: MongoDB Empresa > db.t1.createIndex({x:1}) MongoDB Empresa > db.t1.createIndex({y:1}) MongoDB Empresa > db.t1.createIndex({x:1, y:1}) MongoDB Empresa > db.t1.createIndex({y:1, x:1}) |
Se trata de una única colección con 4 índices en (x), (y), (x, y) e (y, x). Ahora, mira esto:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 |
MongoDB Empresa > db.t1.encontrar({x:{$gt:0}, y:99}).explique() { "queryPlanner" : { "plannerVersion" : 1, "espacio de nombres" : "prueba.t1", "indexFilterSet" : falso, "parsedQuery" : { "$and" : [ { "y" : { "$eq" : 99 } }, { "x" : { "$gt" : 0 } } ] }, "winningPlan" : { "escenario" : "FETCH", "filtro" : { "x" : { "$gt" : 0 } }, "inputStage" : { "escenario" : "IXSCAN", "keyPattern" : { "y" : 1 }, "nombredelíndice" : "y_1", "isMultiKey" : falso, "multiKeyPaths" : { "y" : [ ] }, "isUnico" : falso, "isSparse" : falso, "isPartial" : falso, "indexVersion" : 2, "dirección" : "adelante", "indexBounds" : { "y" : [ "[99.0, 99.0]" ] } } }, |
Incluso en esta estructura de documento simple, MongoDB selecciona el índice en (y) aunque allí la consulta tenga filtros en x e y: ({x:{$gt:0}, y:99}).
Para gestionar todas estas incertidumbres y los problemas de rendimiento que provocará, MongoDB proporciona un número de API para gestionar la caché del plan de consultaFlush: vaciar una entrada específica de la caché, vaciar toda la caché de planes. En lugar de desarrollar aplicaciones, los desarrolladores y DBAs de MongoDB necesitan gestionar la caché de planes. Los desarrolladores y administradores de bases de datos no necesitan gestionar la caché de planes en otras bases de datos empresariales.
Volviendo a la pregunta original: ¿Por qué no se pueden crear múltiples índices de texto en MongoDB?
Construir múltiples índices no debería ser un problema si simplemente lo permitieran. El verdadero problema es que cuando se da un predicado de texto en la consulta, el optimizador de MongoDB no puede elegir el índice correcto. No puede validar estos índices de texto contra el predicado de texto. El optimizador de MongoDB no sigue una lógica natural o un marco lógico. De ahí la restricción.
E incluso podría hacerte daño.
Me gusta mucho #MongoDB pero escribir consultas en JSON es doloroso (especialmente la agregación). Es un infierno de paréntesis rizados con poco soporte de herramientas. Lo mismo ocurre con #ElasticSearch. Afortunadamente, gracias a las librerías Java/Kotlin, rara vez tenemos que escribirlos directamente. Pero cuando tenemos que hacerlo, duele.
- Philipp Hauer (@philipp_hauer) 18 de abril de 2019
Couchbase N1QL ha añadido índice de texto a N1QL para la próxima versión. Vea los detalles en https://www.couchbase.com/blog/n1ql-and-search-how-to-leverage-fts-index-in-n1ql-query/. Los usuarios pueden crear cualquier número de índices de texto y el optimizador elegirá un índice cualificado (sargable) y lo utilizará. También permite realizar búsquedas durante las uniones, las exploraciones posteriores al índice, etc., ya que el optimizador entiende el predicado de búsqueda y lo incorpora a su lógica de decisión. No hay una nueva API ni un nuevo plan que gestionar. ¡Ese es el poder de tener Couchbase!
Recursos:
- Visión general de la optimización de consultas en sistemas relacionales: https://web.stanford.edu/class/cs345d-01/rl/chaudhuri98.pdf
- Una inmersión profunda en la optimización de consultas N1QL de Couchbase: https://dzone.com/articles/a-deep-dive-into-couchbase-n1ql-query-optimization
- https://docs.mongodb.com/manual/reference/method/js-plan-cache/
- https://docs.mongodb.com/manual/core/query-plans/
- https://docs.mongodb.com/manual/reference/method/js-plan-cache/