sql >> Base de Datos >  >> NoSQL >> MongoDB

Rendimiento de consultas de MongoDB para más de 5 millones de registros

Esto es buscar la aguja en un pajar. Necesitaríamos alguna salida de explain() para aquellas consultas que no funcionan bien. Desafortunadamente, incluso eso solucionaría el problema solo para esa consulta en particular, así que aquí hay una estrategia sobre cómo abordar esto:

  1. Asegúrese de que no se deba a una memoria RAM insuficiente o a una paginación excesiva
  2. Habilite el perfilador de base de datos (usando db.setProfilingLevel(1, timeout) donde timeout es el umbral para la cantidad de milisegundos que toma la consulta o el comando, cualquier cosa más lenta se registrará)
  3. Inspeccione las consultas lentas en db.system.profile y ejecute las consultas manualmente usando explain()
  4. Intente identificar las operaciones lentas en explain() salida, como scanAndOrder o grande nscanned , etc.
  5. Razón sobre la selectividad de la consulta y si es posible mejorar la consulta usando un índice en absoluto . De lo contrario, considere no permitir la configuración del filtro para el usuario final o dele un cuadro de diálogo de advertencia de que la operación puede ser lenta.

Un problema clave es que aparentemente está permitiendo que sus usuarios combinen filtros a voluntad. Sin la intersección de índices, eso aumentará drásticamente la cantidad de índices requeridos.

Además, lanzar ciegamente un índice en cada consulta posible es una estrategia muy mala. Es importante estructurar las consultas y asegurarse de que los campos indexados tengan suficiente selectividad .

Supongamos que tiene una consulta para todos los usuarios con status "activo" y algunos otros criterios. Pero de los 5 millones de usuarios, 3 millones están activos y 2 millones no, así que sobre 5 millones de entradas solo hay dos valores diferentes. Tal índice no suele ayudar. Es mejor buscar primero los otros criterios y luego escanear los resultados. En promedio, al devolver 100 documentos, tendrá que escanear 167 documentos, lo que no perjudicará demasiado el rendimiento. Pero no es tan simple. Si el criterio principal es joined_at fecha del usuario y la probabilidad de que los usuarios suspendan el uso con el tiempo es alta, es posible que termine escaneando miles de documentos antes de encontrar cien coincidencias.

Por lo tanto, la optimización depende mucho de los datos (no solo de su estructura , sino también los datos en sí ), sus correlaciones internas y sus patrones de consulta .

Las cosas empeoran cuando los datos son demasiado grandes para la RAM, porque entonces, tener un índice es excelente, pero escanear (o simplemente devolver) los resultados puede requerir obtener una gran cantidad de datos del disco al azar, lo que lleva mucho tiempo.

La mejor manera de controlar esto es limitar la cantidad de tipos de consultas diferentes, no permitir consultas sobre información de baja selectividad e intentar evitar el acceso aleatorio a datos antiguos.

Si todo lo demás falla y si realmente necesita tanta flexibilidad en los filtros, podría valer la pena considerar una base de datos de búsqueda separada que admita intersecciones de índice, obtenga las identificaciones de mongo desde allí y luego obtenga los resultados de mongo usando $in . Pero eso está lleno de sus propios peligros.

-- EDITAR --

La explicación que publicó es un hermoso ejemplo del problema con el escaneo de campos de baja selectividad. Aparentemente, hay muchos documentos para "[email protected]". Ahora, encontrar esos documentos y ordenarlos de forma descendente por marca de tiempo es bastante rápido, porque está respaldado por índices de alta selectividad. Desafortunadamente, dado que solo hay dos tipos de dispositivos, mongo necesita escanear 30060 documentos para encontrar el primero que coincida con 'móvil'.

Supongo que se trata de algún tipo de seguimiento web, y el patrón de uso del usuario hace que la consulta sea lenta (si cambiara de móvil a web todos los días, la consulta sería rápida).

Hacer que esta consulta en particular sea más rápida podría hacerse usando un índice compuesto que contenga el tipo de dispositivo, p. usando

a) ensureIndex({'username': 1, 'userAgent.deviceType' : 1, 'timestamp' :-1})

o

b) ensureIndex({'userAgent.deviceType' : 1, 'username' : 1, 'timestamp' :-1})

Desafortunadamente, eso significa que consultas como find({"username" : "foo"}).sort({"timestamp" : -1}); ya no puede usar el mismo índice, así que, como se describe, la cantidad de índices crecerá muy rápidamente.

Me temo que no hay una muy buena solución para esto usando mongodb en este momento.