Pregunta

Tenemos un requisito para almacenar 500 mediciones por segundo, procedentes de varios dispositivos. Cada medición se compone de una marca de tiempo, un tipo de cantidad, y varios valores de vector. En este momento hay 8 valores del vector por medición, y podemos considerar que este número es constante para las necesidades de nuestro proyecto prototipo. Estamos utilizando HNibernate. Las pruebas se realizan en SQLite (db archivo de disco, no en memoria), pero la producción será probablemente MsSQL.

Nuestra clase de entidad de medida es la que tiene una sola medición, y es similar al siguiente:

public class Measurement
{
    public virtual Guid Id { get; private set; }
    public virtual Device Device { get; private set; }
    public virtual Timestamp Timestamp { get; private set; }
    public virtual IList<VectorValue> Vectors { get; private set; }
}

valores vectoriales se almacenan en una tabla separada, de modo que cada uno de ellos hace referencia a su medición parental a través de una clave externa.

Hemos hecho un par de cosas para asegurarse de que SQL generado es (razonablemente) eficiente: estamos usando Guid.Comb para generar identificadores, estamos Flushing alrededor de 500 artículos en una sola transacción, ADO.Net tamaño del lote se establece en 100 (creo que SQLite no soporta actualizaciones por lotes? Pero podría ser útil más adelante).

El problema

Ahora podemos insertar 150-200 mediciones por segundo (que no es lo suficientemente rápido, aunque esto es SQLite que estamos hablando). Mirando el SQL generado, podemos ver que en una sola transacción insertamos (como se esperaba):

  • 1 timestamp
  • 1 de medición
  • 8 valores de vector

Lo que significa que en realidad estamos haciendo 10 veces más insertos individuales de mesa:. 1500-2000 por segundo

Si colocamos todo (los 8 valores del vector y la marca de tiempo) en la tabla de la medida (la adición de 9 columnas dedicadas), parece que podríamos aumentar nuestra velocidad de inserción de hasta 10 veces.

El cambio a servidor SQL mejorará el rendimiento, pero nos gustaría saber si podría haber una manera de evitar costes innecesarios de rendimiento relacionados con la forma en la base de datos está organizada en este momento.

[Editar]

Con SQLite en memoria consigo alrededor de 350 artículos / seg (3500 inserciones individuales de mesa), que creo que es casi tan bueno como se pone con NHibernate (teniendo este post para referencia: http://ayende.com/Blog/archive/2009/08/22/nhibernate-perf -tricks.aspx ).

Pero que bien podría cambiar a servidor y parada SQL asumiendo cosas, ¿verdad? Voy a actualizar mi post tan pronto como lo prueba.

[Actualización]

Me he mudado al servidor SQL y aplasté mi jerarquía, he comprobado mediante el almacenamiento de 3000 mediciones / seg durante varias horas y parece estar funcionando bien.

¿Fue útil?

Solución

En lo personal, yo diría que ir a por ello:. Anulación de la normalización, y luego crear un proceso ETL para traer estos datos en un formato más normalizado para el análisis habitual / a

Básicamente la situación ideal para usted podría ser tener una base de datos separada (o tablas aunque sólo sea por separado en la misma base de datos si es necesario) que trata la adquisición de datos como una cuestión totalmente separada de tenerlo en el formato en el que necesidad de procesarla.

Eso no significa que usted necesita para deshacerse de las entidades que ha creado en torno a la estructura de su base de datos actual:. Solo que también debe crear esas tablas no normalizados y crea un ETL para que estén en Usted podría utilizar SSIS ( aunque todavía con muchos errores e irritable) para llevar los datos en su conjunto normalizado de las tablas de forma periódica, o incluso una aplicación de C # o cualquier otro proceso de carga a granel.

EDIT: Esto es suponiendo, por supuesto, que no necesita su análisis que se hace en tiempo real: sólo la colección de datos. Muy a menudo, la gente no necesita (y, a veces, en realidad prefiere no tener) la actualización en tiempo real de los datos de análisis. Es una de esas cosas que suena bien en papel, pero en la práctica es innecesaria.

Si algunas personas que analizan estos datos requieren acceso en tiempo real, se podría construir un conjunto de herramientas contra el "metal desnudo" desnormalizado datos transaccionales, si lo desea, pero con bastante frecuencia cuando realmente profundizar en los requisitos, las personas que realizan el análisis no es necesario genuina en tiempo real (y en algunos casos, que preferirían tener un conjunto más estática de datos para trabajar!): y en ese caso, el ETL periódica podría funcionar bastante bien. Usted sólo tiene que juntarse con los usuarios de destino y averiguar lo que realmente necesitan.

Otros consejos

Bueno, que dependerá. 8 son los valores del vector de un número fuerte y rápido que nunca cambiará? Entonces desnormalización en su caso podría tener sentido (pero sólo las pruebas en el hardware y base de datos que está utilizando le dirá real). Si pudiera ser de 9 mediciones de la próxima semana, no lo haga.

Yo diría que tiene que cambiar primero en el servidor SQL y el equipo que va a correr antes de tratar de decidir qué hacer.

Una vez que haya cambiado de ejecución de perfiles. Es muy posible que NHibernate no está creando el mejor SQl realizar para su inserción.

El hecho de que usted tiene un conjunto de vectores que son probablemente estando partido en el inserto puede ser parte de su problema de rendimiento. Puede ser que sea mejor tener 8 variables independientes en lugar de un conjunto que tiene que ser dividido.

Usted está hablando de más de 40 millones de registros al día, esto va a requerir algún hardware importante y una base de datos muy bien diseñado. También es posible que una base de datos relacional no es la mejor opción para este (no tengo ni idea de cómo desea utilizar esta cantidad de datos). ¿Cuánto tiempo va guardando estos datos, el tamaño de aquí va a ir de las manos muy rápidamente.

¿Es posible bulkinsert los registros de un grupo de una vez por minuto en lugar? inserción masiva es más rápido en gran medida que la fila de inserciones de fila.

Su diseño tiene que tener en cuenta cómo se utilizan los datos, así como de insertarlo. En general las cosas para acelerar insertos puede ralentizar selecciona y viceversa. Es posible que necesite un almacén de datos que se carga una vez al día para el análisis (y una consulta rápida para poder mostrar la prima hasta que el segundo de datos).

En primer lugar, se mueven a la base de datos de destino; rendimiento basado en SqlLite puede no ser indicativo del rendimiento basado en MsSql

En segundo lugar, medir, donde el cuello de botella es; improviso me atrevería a decir que es el disco y una base de datos en memoria llevaría a cabo mucho mejor.

A continuación, anulación de la normalización en caso necesario, con un proceso de ETL como se sugirió anteriormente.

El procesamiento de eventos de la corriente tiene un dicho: " si se golpea el disco, estás muerto "; -)

Ha considerado el uso SqlBulkCopy? Funciona muy rápido. Lo he utilizado en un entorno de producción y ha logrado 10.000+ insertos en una sola mesa de menos de un segundo con una máquina de SQL Server 2005. Sólo tiene que preparar DataTable (s) a ser mayor insertada en su aplicación. Aquí está una muestra.

        public static void SQLBulkCopyInsert(DataTable dtInsertRows, string destinationTableName, string[] columnMappings)
    {
        using (SqlBulkCopy sbc = new SqlBulkCopy(DBHelper.Secim2009DB.ConnectionString, SqlBulkCopyOptions.UseInternalTransaction))
        {                
            sbc.DestinationTableName = destinationTableName;
            // Number of records to be processed in one go
            sbc.BatchSize = 30000;
            // Map the Source Column from DataTabel to the Destination Columns in SQL Server 2005 Person Table

            foreach (string columnMapping in columnMappings)
            {
                sbc.ColumnMappings.Add(columnMapping, columnMapping);
            }

            // Number of records after which client has to be notified about its status
            sbc.NotifyAfter = dtInsertRows.Rows.Count;
            // Event that gets fired when NotifyAfter number of records are processed.
            sbc.SqlRowsCopied += new SqlRowsCopiedEventHandler(sbc_SqlRowsCopied);
            // Finally write to server
            sbc.WriteToServer(dtInsertRows);
            sbc.Close();
        }
    }

    public static void sbc_SqlRowsCopied(object sender, SqlRowsCopiedEventArgs e)
    {            

    }

No se limite a eliminar la normalización. El diseño de los resultados, usando un patrón de diseño útil. A veces, un patrón de diseño útil para el rendimiento da un diseño diferente que el que se obtiene siguiendo las reglas de normalización.

No creo que su situación va a ser ayudado por desnormalización. Casi todas las personas que abogan por desnormalización decir que las mejoras de rendimiento no vienen cuando se está almacenando nuevos datos. Vienen al recuperar los datos. Usted tendrá que encontrar la manera de que se aplica a su caso.

Me puede decir esto. Si al final el almacenamiento a través de múltiples procesos concurrentes, su diseño dará lugar a graves cuellos de botella, y bien podría funcionar más lento que un diseño normalizado.

Pero no tome mi palabra para ella. Experimentar. Analizar. Aprender. Próspero.

"Tenemos un requisito para almacenar 500 mediciones por segundo, procedentes de varios dispositivos."

No utilice DBMS para almacenar ese tipo de datos.

¿Cuáles son las razones por las que el uso del DBMS?

(a) Pueden hacer cumplir las restricciones para que los datos que está intentando registrarse. Pero usted no tiene ninguna. Los datos de las mediciones son lo que son y tienen que ser aceptadas. No hay restricciones.

(b) Se puede asegurar la coherencia y la integridad de sus datos empresariales valiosos en el caso de violaciónes (1) de restricción y (2) graves fallos del sistema, tales como errores de disco S /. Pero ya que no tiene limitaciones, (1) no se aplica. Y en cuanto a (2), lo que haría usted con sus medidas si un disco Error de E / it impide que se grabe? Sus mediciones se pierden no importa qué.

Así imo, usted no tiene ninguna razón de lo que nunca utilizar un DBMS. Volcar su carga de las mediciones en un archivo plano y un proceso que, según sea necesario.

Se podría considerar otras alternativas de bases de datos. MSSQL proporciona una gran cantidad de funcionalidad, sino que añade algo de sobrecarga.

Un excelente recurso para el procesamiento de alto rendimiento (como lo que está tratando de hacer) es en http://highscalability.com/

Uno de los estudios de casos que tenían está almacenando miles estadísticas del dispositivo en una base de datos. La solución fue múltiples bases de datos MySQL y de petición de ruta a Basado en el ID de dispositivo. En general - el sitio puede proporcionar excelentes estudios de casos. Puede ser que usted puede encontrar una posible solución allí.

Timur

Utilice el DBMS y hardware adecuado. Las pruebas en otra plataforma con diferente hardware le dirá nada sobre el rendimiento.

Desnormalización es poco probable que el rendimiento de escritura ayuda porque, por definición, significa que está creando datos redundantes y por lo tanto que estaría haciendo más trabajo para cada escritura, no menos.

Las cifras que ha mencionado no son excepcionales para la transmisión de datos y escenarios perfectamente alcanzable usando el hardware adecuado, pero creo que NHibernate va a ser un factor limitante importante para usted. Creo que es poco probable NHib es una buena opción para este tipo de cosas.

¿Usted ha considerado el uso de algunas de las tecnologías que proporcionan suport especial para la transmisión de fuentes de datos y CEP? Por ejemplo:. OSISoft PI, Microsoft StreamInsight y característica FILESTREAM de SQL Server

Usted tiene que preguntarse, "¿Por qué normalizar?"

Hay tres razones principales:

  1. La consistencia de datos
  2. Actualización de velocidad
  3. Tamaño

coherencia de datos

Es bueno tener menús desplegables y todas las filas que tienen el mismo significado que tiene la misma FK, ¿verdad? Bastante obvio. Esto es muy importante para la base de datos de múltiples "editores" de datos. Pero esto es sólo tan buena como nuestros procesos. Digamos que es una base de datos de vuelo y hay una entrada para el Aeropuerto Nacional de Washington DC ... y algunos añade una nueva entrada para el Aeropuerto Nacional Reagan en Washington DC ... el FK estará allí, y ser usado en la tabla de los niños, pero ganó 't ser vale mucho ... Pero aún así es una cosa buena para hacerlo ...

Actualización de velocidad

Lo que debería haber hecho es actualizar la fila para el Aeropuerto Nacional con un nuevo nombre. Debido a que sólo hay una fila padre, hace que sea un cambio muy simple. Si mi mesa vuelo tenía el texto que habría ido actualizando millones de filas.

Tamaño

Si lo hiciera tienda "Aeropuerto Nacional Reagan" en cada disco, se necesitaría más espacio que una FK de, por ejemplo, 19. Tamaño solía ser un acuerdo muy grande, pero SAN hace bastante irrelevante.


Conclusiones

Ok, así que ¿Le preocupa que su aplicación para la recogida de datos SOLO no puede mantener los nombres de los instrumentos rectos? Se consistencia de los datos va a ser un reto?

Ok, así que ¿Cuántas veces crees que va a cambiar el nombre del instrumento o datos de punto? Me refiero a O2 disuelto es O2 disuelto, turbidez La turbidez es, ¿verdad? Y si lo tiene que hacer una actualización masiva apuesto a que tiene el tiempo de inactividad entre las corridas de hacerlo. Así que esto no es un problema.

Ok, tamaño lo tanto, seguro ... eso es una gran cantidad de mediciones; pero, no hacer la medición "oxígeno disuelto", DO2 está bien ... ¿cuánto más grande es que lo que algunos FK como "7? Pasa el espacio para ahorrar tiempo.

no se normalizan, porque siempre has sido dijo que los diseñadores de bases de datos lo hacen bien. Sabe por qué lo estás haciendo y por qué usted está eligiendo lo que está eligiendo.

Sí. Me gustaría considerar la reducción de la sobrecarga de los insertos por tanto desnormalización (aplanamiento de los datos) y fragmentación de los datos por el tiempo. Me gustaría diseñar mi base de datos para que cada registro almacena un valor de un segundo conjunto de datos por dispositivo:

public class Measurement 
{ 
    public Guid ID { get; private set; } 
    public Device Device { get; private set; }
    public Sample[] { get; private set; }

    public DateTime FirstTimestamp { get; private set; } 
    public DateTime LastTimestamp { get; private set; } 
} 

public class Sample
{ 
    public DateTime Timestamp { get; private set; } 
    public VectorValue[] Vectors { get; private set; } 
}

Hay varias formas de almacenar tipos complejos (como una lista de listas, en este caso) en un único registro. columnas XML y tipos definidos por el usuario CLR , son dos ejemplos.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top