Diseño de base de datos para manejar mil millones de filas y contar

10

Recibimos datos de GPS en tiempo real a una velocidad de alrededor de 5000 pr. minuto (de 4 servidores TCP). Cada servidor usa una única conexión para insertar los datos y almacena los datos entre inserciones. Cada 15 minutos más o menos, un servicio obtiene estos datos y los procesa en viajes. Una vez que se han generado los viajes, los datos reales del GPS generalmente no son tan importantes, solo si el usuario desea ver la ruta en un mapa.

El problema es que parece que la base de datos está luchando para mantenerse al día con la velocidad de los datos que se insertan. A veces, cuando aumenta la carga, el tiempo de inserción aumenta repentinamente drásticamente (> 30 segundos), lo que a su vez permite que se almacenen más datos, lo que a su vez da como resultado inserciones más grandes y una mayor duración de inserción.

Espero recibir algunos comentarios sobre el diseño actual, y algunas de las ideas que tenemos para mejorar el rendimiento, y respuestas a algunas de nuestras preguntas, ¡y cualquier otro consejo que la gente pueda tener!

Diseño actual

Los datos están actualmente separados en tablas que representan una semana, y los datos de más de un año se archivan en una base de datos secundaria. Todo se une en una vista editable, que se utiliza tanto para inserciones como para lecturas.

Diseño de la mesa

  • Id (PK, identificador único)
  • DeviceId (FK, int)
  • PersonId (FK, int)
  • VehicleId (FK, int)
  • TokenId (FK, int)
  • UtcTime (PK, datetime2 (3))
  • Latitud (flotante)
  • Longitud (flotante)
  • Velocidad (smallint)
  • Encabezado (smallint)
  • Satélites (tinyint)
  • IOData (varbinary (100))
  • IgnitionState (tinyint)
  • Entrada de usuario (tinyint)
  • CreateTimeUtc (datetime2 (3))

Índices

  • DeviceId_CreateTimeUtc_Desc
  • DeviceId_UtcTime_Desc (agrupado)
  • PersonId_UtcTime_Desc
  • TokenId_UtcTime_Desc
  • VehicleId_UtcTime_Desc

Cada semana actualmente ocupa alrededor de 10 GB, incluidos los índices, y actualmente hay alrededor de 300 GB de datos en la base de datos principal.

Las tablas de datos en la base de datos principal tienen su propio grupo de archivos con 1 archivo, pero está en el mismo disco que todas las otras tablas en la base de datos principal. La base de datos secundaria está en un disco diferente, pero en la misma máquina.

Creo que también estamos ejecutando un trabajo de reconstrucción de índice semanalmente, cuando se usa una nueva partición de tabla (semana). No se realiza contracción.

La máquina es una HP de 8 núcleos con 12 GB de memoria, y el disco que contiene la base de datos principal ejecuta RAID 10.

Ideas

  • Limite la cantidad de datos almacenados en la base de datos primaria a, por ejemplo, un máximo de 1 mes. Como mínimo, haría que la base de datos sea más manejable para la copia de seguridad / restauración, pero ¿podríamos esperar ver una mejora en el rendimiento al hacer esto?
  • Cree 2 archivos en el grupo de archivos para los datos actuales y distribúyalos en 2 particiones físicas diferentes
  • Cree bases de datos maestro-esclavo que contengan datos actuales, de modo que las inserciones y lecturas se realicen en diferentes bases de datos.
  • Coloque archivos para datos actuales en discos SSD (¿la duplicación haría alguna diferencia de rendimiento con los discos SSD?)

Avíseme si necesita más información. Hay terriblemente muchos factores que influyen en el rendimiento, y probablemente igualmente muchas formas de modificarlo.

Sondergard
fuente
Los comentarios no son para discusión extendida; Esta conversación se ha movido al chat .
Paul White 9

Respuestas:

8

5000 insertos por minuto son aproximadamente 83 insertos por segundo. Con 5 índices, eso es 400 filas físicas insertadas por segundo. Si la carga de trabajo estuviera en la memoria, esto no representaría un problema incluso para los servidores más pequeños. Incluso si se tratara de una inserción fila por fila utilizando la forma más ineficiente que se me ocurre. 83 consultas triviales por segundo simplemente no son interesantes desde el punto de vista de la CPU.

Probablemente, estás en un disco. Puede verificar esto mirando las estadísticas de espera o STATISTICS IO.

Sus consultas probablemente toquen muchas páginas diferentes para que el grupo de búferes no tenga espacio para todas ellas. Esto provoca lecturas frecuentes de página y probablemente escrituras aleatorias de disco también.

Imagine una tabla donde solo inserta físicamente al final debido a una clave cada vez mayor. El conjunto de trabajo sería una página: la última. Esto generaría IO secuencial también cuando el proceso de escritura diferida o punto de control escribe el "final" de la tabla en el disco.

Imagine una tabla con inserciones colocadas al azar (ejemplo clásico: una clave guid). Aquí, todas las páginas son el conjunto de trabajo porque se tocará una página aleatoria para cada inserción. Las IO son aleatorias. Este es el peor de los casos cuando se trata de un conjunto de trabajo.

Estás en el medio Sus índices son de la estructura (SomeValue, SequentialDateTime). El primer componente aleatoriza parcialmente la secuencialidad proporcionada por el segundo. Supongo que hay bastantes valores posibles para " SomeValue", de modo que tenga muchos puntos de inserción colocados al azar en sus índices.

Usted dice que los datos se dividen en tablas de 10 GB por semana. Ese es un buen punto de partida porque el conjunto de trabajo ahora está limitado por 10 GB (sin tener en cuenta las lecturas que pueda hacer). Sin embargo, con 12 GB de memoria del servidor es poco probable que todas las páginas relevantes puedan permanecer en la memoria.

Si pudiera reducir el tamaño de las "particiones" semanales o aumentar un poco la memoria del servidor, probablemente esté bien.

Esperaría que los insertos al comienzo de la semana sean más rápidos que al final. Puede probar esta teoría en un servidor de desarrollo ejecutando un punto de referencia con un cierto tamaño de datos y reduciendo gradualmente la memoria del servidor hasta que vea el tanque de rendimiento.

Ahora, incluso si todas las lecturas y escrituras se ajustan a la memoria, es posible que todavía tenga E / S de descarga de página sucia aleatoria. La única forma de deshacerse de eso es escribir en posiciones compartidas en sus índices. Si puede convertir sus índices para usar (más) claves secuenciales que ayudarían mucho.

Como solución rápida, agregaría una capa de almacenamiento intermedio entre los clientes y la tabla principal. Tal vez acumule 15 minutos de escrituras en una tabla de preparación y la vacíe periódicamente. Eso elimina los picos de carga y utiliza un plan más eficiente para escribir en la tabla grande.

usr
fuente
1
@usr ¡Gracias por la respuesta muy completa y bien explicada! De hecho, hemos discutido el aumento de la memoria del servidor, sin saber cuánto efecto tendría, pero ahora realmente tenemos una razón muy convincente para hacerlo :) Tiene razón en que "SomeValue" aleatoriza parcialmente los puntos de inserción, probablemente hay alrededor de 10000 identificadores de dispositivo. Con respecto a la tabla de etapas, ¿su sugerencia es una tabla sin ningún índice, y luego un trabajo para insertar en la tabla principal cada X minutos?
sondergard
@usr Reg. Su sugerencia para convertir el índice agrupado en secuencial, podríamos agregar un auto-inc. columna de identidad (entero), y cambiar el índice agrupado a esta columna con el único propósito de mantenerlo secuencial? No sería único en todas las tablas, pero mientras la clave principal lo sea, deberíamos estar bien.
sondergard
1
Si la tabla de etapas es pequeña y sus consultas pueden funcionar con ella, entonces no necesita indexar en absoluto. Pero podrías; Una estrategia sería hacer el CI en una columna de identidad (como usted dice). Esto puede hacer maravillas si el CI es grande y los otros índices son pequeños. Debido a que las escrituras de CI ahora son secuenciales, contribuyen mucho menos a su problema. Esta estrategia es más exitosa si hay una diferencia de tamaño significativa .; Otra idea sería tener una mesa por día. Quizás fusionarse mensualmente.
Usr
Bien, buscamos crear una columna de identidad para CI, pero desafortunadamente no es posible en una vista dividida (no se permite la columna de identidad, no hay valores predeterminados y todas las columnas deben incluirse en la inserción). Tal vez la vista dividida era un diseño mal elegido, aunque fue recomendado por un consultor
sondergard
2
Sin embargo, en serio, para cualquiera que se enfrente al mismo problema, si tiene muchas escrituras y solo unas pocas lecturas, realmente desea agregar al final y retrasar cualquier indexación. Por otro lado, si desea lecturas rápidas y no le importa cuánto tiempo lleva insertar, necesita un índice agrupado.
tiktak