Efter min mening er det forkert at sammenligne den indlejrede database (som SQL CE) versus den relationelle database på serversiden (som alle de andre, undtagen SQLite og den indlejrede version af Firebird).
Den største forskel mellem dem er, at generelle relationelle databaser på serversiden (som MS SQL, MySQL, Firebird Classic og SuperServer osv.) installeres som en uafhængig service og kører uden for rammerne af din hovedapplikation . Det er grunden til, at de kan yde meget bedre på grund af den iboende understøttelse af multi-core og multi-CPU-arkitekturer, ved at bruge OS-funktioner som pre-caching, VSS osv. for at øge gennemløbet i tilfælde af intensiv databasedrift og kan kræve lige så meget hukommelse som dit OS kan levere en enkelt tjeneste/applikation. Det betyder også, at ydeevneindikatorerne for dem er mere eller mindre uafhængige af din applikation, men i høj grad afhænger af din hardware. I denne henseende vil jeg sige, at serverversionerne af enhver database altid er mere ydeevne sammenlignet med de indlejrede.
SQL CE (sammen med Firebird Embedded, SQLite, TurboSQL og nogle andre) er indlejrede DB-motorer , hvilket betyder, at hele databasen er pakket i en enkelt (eller maksimalt 2) DLL-filer, der distribueres sammen med din applikation. På grund af de åbenlyse størrelsesbegrænsninger (vil du gerne distribuere en 30 MB DLL sammen med din 2-3 MB lange applikation?) kører de også direkte i forbindelse med din applikation og den samlede hukommelse og ydeevne for dataadgangshandlinger deles med andre dele af din applikation -- det angår både tilgængelig hukommelse, CPU-tid, diskgennemløb osv. At have en beregningsintensiv tråd, der kører parallelt med din dataadgangstråd, kan føre til et dramatisk fald i din databaseydeevne.
På grund af de forskellige anvendelsesområder har disse databaser forskellige muligheder:server-db giver omfattende bruger- og rettighedsstyring, understøttelse af visninger og lagrede procedurer, hvorimod indlejret database normalt mangler nogen støtte til brugere og rettighedsstyring og har begrænset understøttelse af visninger og lagrede procedurer (sidstnævnte mister størstedelen af deres fordele ved at køre på serversiden). Datagennemstrømning er en sædvanlig flaskehalse i RDBMS, serverversioner installeres normalt på stribede RAID-volumener, hvorimod indlejrede DB ofte er hukommelsesorienterede (prøv at beholde alle de faktiske data i hukommelsen) og minimerer datalageradgangshandlingerne.
Så hvad der sandsynligvis ville give mening er at sammenligne forskellige indlejrede RDBMS for .Net for deres ydeevne, såsom MS SQL CE 4.0, SQLite, Firebird Embedded, TurboSQL . Jeg ville ikke forvente drastiske forskelle under sædvanlig ikke-spidsbelastning, hvorimod nogle databaser kan give bedre støtte til store BLOB'er på grund af bedre integration med OS.
-- opdatering --
Jeg er nødt til at tage mine sidste ord tilbage, for min hurtige implementering viser meget interessante resultater.
Jeg skrev en kort konsolapplikation for at teste begge dataudbydere. Her er kildekoden til dig, hvis du vil eksperimentere med dem på egen hånd.
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();
}
}
}
Nødvendig ansvarsfraskrivelse:
- Jeg fik disse resultater på min maskine:Dell Precision WorkStation T7400 udstyret med 2 Intel Xeon E5420 CPU'er og 8 GB RAM, der kører 64bit Win7 Enterprise .
- Jeg brugte standardindstillingerne for begge DB'er med forbindelsesstrengen "Data Source=database_file_name".
- Jeg brugte de seneste versioner af både SQL CE 4.0 og SQLite/System.Data.SQLite (fra i dag, 3. juni 2011).
Her er resultaterne for to forskellige prøver:
> =Test CRUD-ydeevne af indlejrede DB'er=> => Samplesize:200> ==Test SQLite==> Oprettelse af tabel:396.0396 ms> Skrivedata:22189.2187 ms> Opdatering af data:23591.3589 ms> Læser data:1.3509 ms> Sletning af data:20963.0961 ms> Drop-tabel:85.0085 ms> ==Test SQL CE 4.0==> Opretter tabel:16.0016 ms> Skriver data:25.0025 ms> Opdaterer data:56.0056 ms:56.0056 ms:8 ms. 2 ms> Læser data:38 ms.> Nedkastningsbord:11.0011 ms
... og en større prøve:
=Test af CRUD -ydeevne af indlejret DBS ==> Prøver Størrelse:1000 ==Testning SQLite ==Oprettelse af tabel:93.0093 MSWRITING Data:116632.6621 MSUpdating Data:104967.4957 MSREADING Data:134.0134 MSDELETINGING Data:107666.765656 MSDROPING:83.00.00 SQL CE 4.0==Opretter tabel:16.0016 msSkrivdata:128.0128 msOpdatering af data:307.0307 msLæsedata:164.0164 msSletning af data:306.0306 msDropping tabel:133 ms.0>Så som du kan se, kræver enhver skriveoperation (opret, opdater, slet) næsten 1000 gange mere tid i SQLite sammenlignet med SQLCE. Det afspejler ikke nødvendigvis den generelle dårlige ydeevne af denne database og kan skyldes følgende:
- Den dataudbyder, jeg bruger til SQLite, er System.Data.SQLite , dvs. en blandet samling, der indeholder både administreret og ikke-administreret kode (SQLite er oprindeligt skrevet fuldstændigt i C, og DLL'en giver kun bindinger). Sandsynligvis spiser P/Invoke og datamarshaling en god del af operationstiden.
- SqlCE 4.0 cacherer højst sandsynligt alle data i hukommelsen som standard, hvorimod SQLite tømmer de fleste dataændringer direkte til disklageret, hver gang ændringen sker. Man kan levere hundredvis af parametre til begge databaser via forbindelsesstreng og justere dem korrekt.
- Jeg brugte en række enkelte forespørgsler til at teste DB. SQLCE understøtter i det mindste bulk-operationer via specielle .Net-klasser, der ville være bedre egnet her. Hvis SQLite også understøtter dem (beklager, jeg er ikke ekspert her, og min hurtige søgning gav intet lovende), ville det også være rart at sammenligne dem.
- Jeg har observeret mange problemer med SQLite på x64-maskiner (ved brug af den samme .net-adapter):fra dataforbindelse, der blev lukket uventet, til databasefilkorruption. Jeg formoder, at der er nogle stabilitetsproblemer enten med dataadapteren eller med selve biblioteket.