A mio parere, non è corretto confrontare il database embedded (come SQL CE) con il database relazionale lato server (come tutto il resto, ad eccezione di SQLite e della versione Embedded di Firebird).
La principale differenza tra loro è che i database relazionali lato server per uso generico (come MS SQL, MySQL, Firebird Classic e SuperServer ecc.) sono installati come servizio indipendente e eseguiti al di fuori dell'ambito dell'applicazione principale . Questo è il motivo per cui possono funzionare molto meglio grazie al supporto intrinseco per architetture multi-core e multi-CPU, utilizzando funzionalità del sistema operativo come pre-caching, VSS ecc. Per aumentare il throughput in caso di operazioni intensive del database e possono richiedere tanta memoria quanto il tuo sistema operativo può fornire un singolo servizio/applicazione. Significa anche che gli indicatori di prestazione per loro sono più o meno indipendenti dalla tua applicazione, ma dipendono in gran parte dal tuo hardware. A questo proposito direi che le versioni server di qualsiasi database sono sempre più performanti rispetto a quelle embedded.
SQL CE (insieme a Firebird Embedded, SQLite, TurboSQL e altri) sono motori DB integrati , il che significa che il database completo è compresso in un singolo (o massimo 2) file DLL che vengono distribuiti insieme all'applicazione. A causa degli evidenti limiti di dimensione (vorresti dover distribuire una DLL da 30 MB insieme alla tua applicazione lunga 2-3 MB?) inoltre vengono eseguiti direttamente nel contesto della tua applicazione e la memoria e le prestazioni totali per le operazioni di accesso ai dati sono condivise con altre parti della tua applicazione -- che riguarda sia la memoria disponibile, il tempo della CPU, la velocità effettiva del disco, ecc. Avere thread ad alta intensità di calcolo in esecuzione in parallelo con il thread di accesso ai dati potrebbe portare a un drastico calo delle prestazioni del database.
A causa delle diverse aree di applicazione, questi database hanno una gamma di opzioni diversa:server-db fornisce un'ampia gestione di utenti e diritti, supporto per viste e procedure memorizzate, mentre i database incorporati normalmente non hanno alcun supporto per utenti e gestione dei diritti e hanno un supporto limitato per le viste e stored procedure (questi ultimi perdono la maggior parte dei vantaggi dell'esecuzione sul lato server). La velocità effettiva dei dati è un normale collo di bottiglia dell'RDBMS, le versioni del server sono generalmente installate su volumi RAID con striping, mentre i DB incorporati sono spesso orientati alla memoria (cerca di mantenere tutti i dati effettivi nella memoria) e riducono al minimo le operazioni di accesso all'archiviazione dei dati.
Quindi, ciò che probabilmente avrebbe senso sarebbe confrontare diversi RDBMS embedded per .Net per le loro prestazioni, come MS SQL CE 4.0, SQLite, Firebird Embedded, TurboSQL . Non mi aspetterei differenze drastiche durante le normali operazioni non di picco, mentre alcuni database potrebbero fornire un supporto migliore per BLOB di grandi dimensioni grazie alla migliore integrazione con il sistema operativo.
-- aggiornamento --
Devo riprendere le mie ultime parole, perché la mia rapida implementazione mostra risultati molto interessanti.
Ho scritto una breve applicazione console per testare entrambi i fornitori di dati, ecco il codice sorgente per te se vuoi sperimentarli da solo.
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();
}
}
}
Disclaimer necessario:
- Ho ottenuto questi risultati sulla mia macchina:Dell Precision WorkStation T7400 dotato di 2 CPU Intel Xeon E5420 e 8 GB di RAM, con Win7 Enterprise a 64 bit .
- Ho utilizzato le impostazioni predefinite per entrambi i DB con stringa di connessione "Data Source=database_file_name".
- Ho utilizzato le ultime versioni di SQL CE 4.0 e SQLite/System.Data.SQLite (da oggi, 3 giugno 2011).
Ecco i risultati per due diversi campioni:
> =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 un campione più 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
Quindi, come puoi vedere, qualsiasi operazione di scrittura (crea, aggiorna, elimina) richiede quasi 1000 volte più tempo in SQLite rispetto a SQLCE. Non riflette necessariamente le cattive prestazioni generali di questo database e potrebbe essere dovuto a quanto segue:
- Il provider di dati che utilizzo per SQLite è System.Data.SQLite , ovvero un assembly misto contenente codice gestito e non gestito (SQLite è originariamente scritto completamente in C e la DLL fornisce solo collegamenti). Probabilmente P/Invoke e il marshalling dei dati consumano una buona parte del tempo dell'operazione.
- Molto probabilmente SQLCE 4.0 memorizza nella cache tutti i dati in memoria per impostazione predefinita, mentre SQLite scarica la maggior parte delle modifiche ai dati direttamente nella memoria del disco ogni volta che si verifica la modifica. È possibile fornire centinaia di parametri per entrambi i database tramite stringa di connessione e ottimizzarli in modo appropriato.
- Ho utilizzato una serie di query singole per testare il DB. Almeno SQLCE supporta operazioni di massa tramite classi .Net speciali che sarebbero più adatte qui. Se anche SQLite li supporta (scusate, non sono un esperto qui e la mia rapida ricerca non ha prodotto nulla di promettente) sarebbe bello confrontarli anche.
- Ho osservato molti problemi con SQLite su macchine x64 (usando lo stesso adattatore .net):dalla chiusura imprevista della connessione dati al danneggiamento del file di database. Presumo che ci siano alcuni problemi di stabilità con l'adattatore dati o con la libreria stessa.