Na minha opinião, é incorreto comparar o banco de dados embutido (como o SQL CE) versus o banco de dados relacional do lado do servidor (como todo o resto, exceto SQLite e a versão Embutida do Firebird).
A principal diferença entre eles é que os bancos de dados relacionais de uso geral do lado do servidor (como MS SQL, MySQL, Firebird Classic e SuperServer etc.) são instalados como um serviço independente e são executados fora do escopo de seu aplicativo principal . É por isso que eles podem ter um desempenho muito melhor devido ao suporte intrínseco para arquiteturas multi-core e multi-CPU, usando recursos do sistema operacional como pré-caching, VSS etc. seu sistema operacional pode fornecer um único serviço/aplicativo. Isso também significa que os indicadores de desempenho para eles são mais ou menos independentes do seu aplicativo, mas dependem muito do seu hardware. A este respeito eu diria que as versões de servidor de qualquer banco de dados são sempre mais performance em comparação com os embutidos.
SQL CE (junto com Firebird Embedded, SQLite, TurboSQL e alguns outros) são motores de banco de dados embutidos , o que significa que o banco de dados completo é compactado em um único (ou no máximo 2) arquivos DLL que são distribuídos junto com seu aplicativo. Devido às limitações de tamanho evidentes (você gostaria de ter que distribuir uma DLL de 30 MB junto com seu aplicativo de 2-3 MB?) eles também são executados diretamente no contexto de seu aplicativo e a memória total e o desempenho das operações de acesso a dados são compartilhados com outras partes do seu aplicativo -- que diz respeito a memória disponível, tempo de CPU, taxa de transferência de disco, etc. Ter threads de computação intensiva rodando em paralelo com seu thread de acesso a dados pode levar a uma diminuição drástica do desempenho do seu banco de dados.
Devido às diferentes áreas de aplicação, esses bancos de dados têm uma paleta de opções diferente:server-db fornece gerenciamento extensivo de usuários e direitos, suporte para visualizações e procedimentos armazenados, enquanto o banco de dados incorporado normalmente não possui suporte para gerenciamento de usuários e direitos e possui suporte limitado para visualizações e procedimentos armazenados (os últimos perdem a maioria dos benefícios de serem executados no lado do servidor). A taxa de transferência de dados é um gargalo comum do RDBMS, as versões do servidor geralmente são instaladas em volumes RAID distribuídos, enquanto os bancos de dados incorporados geralmente são orientados à memória (tente manter todos os dados reais na memória) e minimizam as operações de acesso ao armazenamento de dados.
Então, o que provavelmente faria sentido é comparar diferentes RDBMS embutidos para .Net quanto ao seu desempenho, como MS SQL CE 4.0, SQLite, Firebird Embedded, TurboSQL .
-- atualizar --
Devo retirar minhas últimas palavras, pois minha rápida implementação mostra resultados muito interessantes.
Eu escrevi um pequeno aplicativo de console para testar os dois provedores de dados, aqui está o código-fonte para você, se você quiser experimentá-los por conta própria.
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();
}
}
}
Isenção de responsabilidade necessária:
- Recebi estes resultados em minha máquina:Dell Precision WorkStation T7400 equipada com 2 CPUs Intel Xeon E5420 e 8 GB de RAM, executando Win7 Enterprise de 64 bits .
- Usei as configurações padrão para ambos os bancos de dados com a cadeia de conexão "Data Source=database_file_name".
- Usei as versões mais recentes do SQL CE 4.0 e do SQLite/System.Data.SQLite (a partir de hoje, 3 de junho de 2011).
Aqui estão os resultados para duas amostras 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
... e uma amostra maior:
=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
Assim, como você pode ver, qualquer operação de escrita (criar, atualizar, excluir) requer quase 1000 vezes mais tempo no SQLite em comparação com o SQLCE. Isso não reflete necessariamente o mau desempenho geral desse banco de dados e pode ser devido ao seguinte:
- O provedor de dados que uso para SQLite é o System.Data.SQLite , que é um assembly misto contendo código gerenciado e não gerenciado (o SQLite foi originalmente escrito completamente em C e a DLL fornece apenas ligações). Provavelmente o P/Invoke e o empacotamento de dados consomem uma boa parte do tempo de operação.
- Provavelmente, o SQLCE 4.0 armazena em cache todos os dados na memória por padrão, enquanto o SQLite libera a maioria das alterações de dados diretamente no armazenamento em disco sempre que a alteração ocorre. Pode-se fornecer centenas de parâmetros para ambos os bancos de dados via string de conexão e ajustá-los adequadamente.
- Usei uma série de consultas únicas para testar o banco de dados. Pelo menos o SQLCE suporta operações em massa por meio de classes .Net especiais que seriam mais adequadas aqui. Se o SQLite também os suporta (desculpe, não sou um especialista aqui e minha pesquisa rápida não rendeu nada promissor), seria bom compará-los também.
- Tenho observado muitos problemas com o SQLite em máquinas x64 (usando o mesmo adaptador .net):desde a conexão de dados sendo fechada inesperadamente até a corrupção do arquivo de banco de dados. Presumo que haja alguns problemas de estabilidade com o adaptador de dados ou com a própria biblioteca.