sql >> Base de Datos >  >> RDS >> Sqlserver

Comparación de rendimiento de SQL Server CE 4.0

En mi opinión, es incorrecto comparar la base de datos integrada (como SQL CE) con la base de datos relacional del lado del servidor (como el resto, excepto SQLite y la versión integrada de Firebird).

La principal diferencia entre ellas es que las bases de datos relacionales del lado del servidor de uso general (como MS SQL, MySQL, Firebird Classic y SuperServer, etc.) se instalan como un servicio independiente y se ejecutan fuera del alcance de su aplicación principal . Es por eso que pueden funcionar mucho mejor debido al soporte intrínseco para arquitecturas multinúcleo y multi-CPU, utilizando características del sistema operativo como almacenamiento previo en caché, VSS, etc. para aumentar el rendimiento en caso de una operación intensiva de la base de datos y pueden reclamar tanta memoria como su sistema operativo puede proporcionar un solo servicio/aplicación. También significa que los indicadores de rendimiento para ellos son más o menos independientes de su aplicación, pero dependen en gran medida de su hardware. En este sentido, diría que las versiones de servidor de cualquier base de datos siempre tienen más rendimiento en comparación con las integradas.

SQL CE (junto con Firebird Embedded, SQLite, TurboSQL y algunos otros) son motores de base de datos integrados , lo que significa que la base de datos completa se empaqueta en un solo (o máximo 2) archivos DLL que se distribuyen junto con su aplicación. Debido a las evidentes limitaciones de tamaño (¿le gustaría tener que distribuir una DLL de 30 MB junto con su aplicación de 2-3 MB de longitud?) también se ejecutan directamente en el contexto de su aplicación y la memoria y el rendimiento totales para las operaciones de acceso a datos se comparten con otras partes de su aplicación -- eso se refiere tanto a la memoria disponible, el tiempo de CPU, el rendimiento del disco, etc. Tener subprocesos de computación intensivos que se ejecutan en paralelo con su subproceso de acceso a datos podría conducir a una disminución dramática del rendimiento de su base de datos.

Debido a las diferentes áreas de aplicación, estas bases de datos tienen diferentes paletas de opciones:server-db proporciona una amplia gestión de usuarios y derechos, soporte para vistas y procedimientos almacenados, mientras que la base de datos integrada normalmente carece de soporte para la gestión de usuarios y derechos y tiene soporte limitado para vistas. y procedimientos almacenados (los últimos pierden la mayoría de sus beneficios de ejecutarse en el lado del servidor). El rendimiento de datos es un cuello de botella habitual de RDBMS, las versiones de servidor generalmente se instalan en volúmenes RAID divididos, mientras que la base de datos integrada a menudo está orientada a la memoria (trate de mantener todos los datos reales en la memoria) y minimiza las operaciones de acceso al almacenamiento de datos.

Entonces, lo que probablemente tendría sentido es comparar diferentes RDBMS incorporados para .Net por su rendimiento, como MS SQL CE 4.0, SQLite, Firebird Embedded, TurboSQL . No esperaría diferencias drásticas durante el funcionamiento habitual en horas no pico, mientras que algunas bases de datos pueden proporcionar un mejor soporte para BLOB grandes debido a una mejor integración con el sistema operativo.

-- actualizar --

Tengo que retractarme de mis últimas palabras, ya que mi rápida implementación muestra resultados muy interesantes.

Escribí una aplicación de consola corta para probar ambos proveedores de datos, aquí está el código fuente si quieres experimentar con ellos por tu cuenta.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data.SQLite;
using System.Data.SqlServerCe;
using System.Data.Common;

namespace TestSQL
{
    class Program
    {
        const int NUMBER_OF_TESTS = 1000;

        private static string create_table;

        private static string create_table_sqlce =  "CREATE TABLE Test ( id integer not null identity primary key, textdata nvarchar(500));";
        private static string create_table_sqlite = "CREATE TABLE Test ( id integer not null primary key, textdata nvarchar(500));";

        private static string drop_table = "DROP TABLE Test";
        private static string insert_data = "INSERT INTO Test (textdata) VALUES ('{0}');";
        private static string read_data = "SELECT textdata FROM Test WHERE id = {0}";
        private static string update_data = "UPDATE Test SET textdata = '{1}' WHERE id = {0}";
        private static string delete_data = "DELETE FROM Test WHERE id = {0}";

        static Action<DbConnection> ACreateTable = (a) => CreateTable(a);
        static Action<DbConnection> ATestWrite = (a) => TestWrite(a, NUMBER_OF_TESTS);
        static Action<DbConnection> ATestRead = (a) => TestRead(a, NUMBER_OF_TESTS);
        static Action<DbConnection> ATestUpdate = (a) => TestUpdate(a, NUMBER_OF_TESTS);
        static Action<DbConnection> ATestDelete = (a) => TestDelete(a, NUMBER_OF_TESTS);
        static Action<DbConnection> ADropTable = (a) => DropTable(a);

        static Func<Action<DbConnection>,DbConnection, TimeSpan> MeasureExecTime = (a,b) => { var start = DateTime.Now; a(b); var finish = DateTime.Now; return finish - start; };

        static Action<string, TimeSpan> AMeasureAndOutput = (a, b) => Console.WriteLine(a, b.TotalMilliseconds);

        static void Main(string[] args)
        {
            // opening databases
            SQLiteConnection.CreateFile("sqlite.db");
            SQLiteConnection sqliteconnect = new SQLiteConnection("Data Source=sqlite.db");
            SqlCeConnection sqlceconnect = new SqlCeConnection("Data Source=sqlce.sdf");

            sqlceconnect.Open();
            sqliteconnect.Open();

            Console.WriteLine("=Testing CRUD performance of embedded DBs=");
            Console.WriteLine(" => Samplesize: {0}", NUMBER_OF_TESTS);

            create_table = create_table_sqlite;
            Console.WriteLine("==Testing SQLite==");
            DoMeasures(sqliteconnect);

            create_table = create_table_sqlce;
            Console.WriteLine("==Testing SQL CE 4.0==");
            DoMeasures(sqlceconnect);



            Console.ReadKey();

        }

        static void DoMeasures(DbConnection con)
        {
            AMeasureAndOutput("Creating table: {0} ms", MeasureExecTime(ACreateTable, con));
            AMeasureAndOutput("Writing data: {0} ms", MeasureExecTime(ATestWrite, con));
            AMeasureAndOutput("Updating data: {0} ms", MeasureExecTime(ATestUpdate, con));
            AMeasureAndOutput("Reading data: {0} ms", MeasureExecTime(ATestRead, con));
            AMeasureAndOutput("Deleting data: {0} ms", MeasureExecTime(ATestDelete, con));
            AMeasureAndOutput("Dropping table: {0} ms", MeasureExecTime(ADropTable, con));
        }



        static void CreateTable(DbConnection con)
        {
            var sqlcmd = con.CreateCommand();
            sqlcmd.CommandText = create_table;
            sqlcmd.ExecuteNonQuery();
        }

        static void TestWrite(DbConnection con, int num)
        {
            for (; num-- > 0; )
            {
                var sqlcmd = con.CreateCommand();
                sqlcmd.CommandText = string.Format(insert_data,Guid.NewGuid().ToString());
                sqlcmd.ExecuteNonQuery();
            }

        }

        static void TestRead(DbConnection con, int num)
        {
            Random rnd = new Random(DateTime.Now.Millisecond);
            for (var max = num; max-- > 0; )
            {
                var sqlcmd = con.CreateCommand();
                sqlcmd.CommandText = string.Format(read_data, rnd.Next(1,num-1));
                sqlcmd.ExecuteNonQuery();
            }
        }

        static void TestUpdate(DbConnection con, int num)
        {
            Random rnd = new Random(DateTime.Now.Millisecond);
            for (var max = num; max-- > 0; )
            {
                var sqlcmd = con.CreateCommand();
                sqlcmd.CommandText = string.Format(update_data, rnd.Next(1, num - 1), Guid.NewGuid().ToString());
                sqlcmd.ExecuteNonQuery();
            }
        }

        static void TestDelete(DbConnection con, int num)
        {
            Random rnd = new Random(DateTime.Now.Millisecond);
            var order = Enumerable.Range(1, num).ToArray<int>();
            Action<int[], int, int> swap = (arr, a, b) => { int c = arr[a]; arr[a] = arr[b]; arr[b] = c; };

            // shuffling the array
            for (var max=num; max-- > 0; ) swap(order, rnd.Next(0, num - 1), rnd.Next(0, num - 1));


            foreach(int index in order)
            {
                var sqlcmd = con.CreateCommand();
                sqlcmd.CommandText = string.Format(delete_data, index);
                sqlcmd.ExecuteNonQuery();
            }
        }

        static void DropTable(DbConnection con)
        {
            var sqlcmd = con.CreateCommand();
            sqlcmd.CommandText = drop_table;
            sqlcmd.ExecuteNonQuery();
        }


    }
}

Descargo de responsabilidad necesario:

  1. Obtuve estos resultados en mi máquina:Estación de trabajo Dell Precision T7400 equipada con 2 CPU Intel Xeon E5420 y 8 GB de RAM, con Win7 Enterprise de 64 bits .
  2. Utilicé la configuración predeterminada para ambas bases de datos con la cadena de conexión "Fuente de datos=nombre_de_archivo_de_base_de_datos".
  3. Utilicé las últimas versiones de SQL CE 4.0 y SQLite/System.Data.SQLite (a partir de hoy, 3 de junio de 2011).

Estos son los resultados de dos muestras diferentes:

> =Testing CRUD performance of embedded DBs=  
> => Samplesize: 200
> ==Testing SQLite== 
> Creating table: 396.0396 ms 
> Writing data: 22189.2187 ms 
> Updating data: 23591.3589 ms
> Reading data: 21.0021 ms 
> Deleting data: 20963.0961 ms 
> Dropping table: 85.0085 ms

> ==Testing SQL CE 4.0== 
> Creating table: 16.0016 ms 
> Writing data: 25.0025 ms 
> Updating data: 56.0056 ms 
> Reading data: 28.0028 ms 
> Deleting data: 53.0053 ms 
> Dropping table: 11.0011 ms

... y una muestra más grande:

=Testing CRUD performance of embedded DBs=
 => Samplesize: 1000
==Testing SQLite==
Creating table: 93.0093 ms
Writing data: 116632.6621 ms
Updating data: 104967.4957 ms
Reading data: 134.0134 ms
Deleting data: 107666.7656 ms
Dropping table: 83.0083 ms

==Testing SQL CE 4.0==
Creating table: 16.0016 ms
Writing data: 128.0128 ms
Updating data: 307.0307 ms
Reading data: 164.0164 ms
Deleting data: 306.0306 ms
Dropping table: 13.0013 ms

Entonces, como puede ver, cualquier operación de escritura (crear, actualizar, eliminar) requiere casi 1000 veces más tiempo en SQLite en comparación con SQLCE. No refleja necesariamente el mal rendimiento general de esta base de datos y puede deberse a lo siguiente:

  1. El proveedor de datos que uso para SQLite es System.Data.SQLite , que es un ensamblado mixto que contiene código administrado y no administrado (SQLite originalmente se escribió completamente en C y la DLL solo proporciona enlaces). Probablemente P/Invoke y la clasificación de datos consumen una buena parte del tiempo de operación.
  2. Lo más probable es que SQLCE 4.0 almacene en caché todos los datos en la memoria de forma predeterminada, mientras que SQLite vacía la mayoría de los cambios de datos directamente en el almacenamiento del disco cada vez que se produce un cambio. Se pueden proporcionar cientos de parámetros para ambas bases de datos a través de una cadena de conexión y ajustarlos adecuadamente.
  3. Utilicé una serie de consultas individuales para probar la base de datos. Al menos SQLCE admite operaciones masivas a través de clases especiales de .Net que serían más adecuadas aquí. Si SQLite también los admite (lo siento, no soy un experto aquí y mi búsqueda rápida no arrojó nada prometedor), sería bueno compararlos también.
  4. He observado muchos problemas con SQLite en máquinas x64 (usando el mismo adaptador .net):desde el cierre inesperado de la conexión de datos hasta la corrupción del archivo de la base de datos. Supongo que hay algunos problemas de estabilidad con el adaptador de datos o con la propia biblioteca.