Meiner Meinung nach ist es falsch, die eingebettete Datenbank (wie SQL CE) mit der serverseitigen relationalen Datenbank (wie allen anderen außer SQLite und der eingebetteten Version von Firebird) zu vergleichen.
Der Hauptunterschied zwischen ihnen besteht darin, dass die allgemeinen serverseitigen relationalen Datenbanken (wie MS SQL, MySQL, Firebird Classic und SuperServer etc.) werden als eigenständiger Dienst installiert und außerhalb des Umfangs Ihrer Hauptanwendung ausgeführt . Aus diesem Grund können sie aufgrund der intrinsischen Unterstützung für Multi-Core- und Multi-CPU-Architekturen eine viel bessere Leistung erbringen, indem sie Betriebssystemfunktionen wie Pre-Caching, VSS usw. verwenden, um den Durchsatz bei intensivem Datenbankbetrieb zu erhöhen, und können so viel Speicher beanspruchen wie Ihr Betriebssystem kann einen einzelnen Dienst/eine einzelne Anwendung bereitstellen. Dies bedeutet auch, dass die Leistungsindikatoren für sie mehr oder weniger unabhängig von Ihrer Anwendung sind, aber weitgehend von Ihrer Hardware abhängen. Insofern würde ich sagen, dass die Server-Versionen jeder Datenbank immer performanter sind als die Embedded-Versionen.
SQL CE (zusammen mit Firebird Embedded, SQLite, TurboSQL und einigen anderen) sind eingebettete DB-Engines , was bedeutet, dass die komplette Datenbank in eine einzige (oder maximal 2) DLL-Datei gepackt ist, die zusammen mit Ihrer Anwendung verteilt wird. Aufgrund der offensichtlichen Größenbeschränkungen (möchten Sie eine 30-MB-DLL zusammen mit Ihrer 2-3 MB langen Anwendung verteilen?) laufen sie auch direkt im Kontext Ihrer Anwendung und der gesamte Speicher und die Leistung für Datenzugriffsvorgänge werden mit anderen Teilen Ihrer Anwendung geteilt -- das betrifft sowohl den verfügbaren Speicher, die CPU-Zeit, den Plattendurchsatz usw. Wenn rechenintensive Threads parallel zu Ihrem Datenzugriffs-Thread laufen, kann dies zu einer dramatischen Verringerung Ihrer Datenbankleistung führen.
Aufgrund der unterschiedlichen Anwendungsbereiche haben diese Datenbanken eine unterschiedliche Palette von Optionen:Server-DB bieten umfangreiche Benutzer- und Rechteverwaltung, Unterstützung für Ansichten und gespeicherte Prozeduren, während eingebettete Datenbanken normalerweise keine Unterstützung für Benutzer- und Rechteverwaltung haben und nur begrenzte Unterstützung für Ansichten haben und gespeicherte Prozeduren (letztere verlieren den Großteil ihrer Vorteile, wenn sie auf der Serverseite ausgeführt werden). Der Datendurchsatz ist ein üblicher Engpass von RDBMS, Serverversionen werden normalerweise auf Striped-RAID-Volumes installiert, während eingebettete DBs oft speicherorientiert sind (versuchen, alle tatsächlichen Daten im Speicher zu halten) und die Datenspeicherzugriffsvorgänge minimieren.
Was wahrscheinlich Sinn machen würde, wäre, verschiedene eingebettete RDBMS für .Net auf ihre Leistung zu vergleichen, wie MS SQL CE 4.0, SQLite, Firebird Embedded, TurboSQL . Ich würde keine drastischen Unterschiede während des normalen Betriebs außerhalb der Spitzenzeiten erwarten, während einige Datenbanken aufgrund der besseren Integration mit dem Betriebssystem möglicherweise eine bessere Unterstützung für große BLOBs bieten.
-- aktualisieren --
Ich muss meine letzten Worte zurücknehmen, denn meine schnelle Umsetzung zeigt sehr interessante Ergebnisse.
Ich habe eine kurze Konsolenanwendung geschrieben, um beide Datenanbieter zu testen. Hier ist der Quellcode für Sie, wenn Sie selbst damit experimentieren möchten.
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();
}
}
}
Notwendiger Haftungsausschluss:
- Ich habe diese Ergebnisse auf meinem Computer erhalten:Dell Precision WorkStation T7400, ausgestattet mit 2 Intel Xeon E5420-CPUs und 8 GB RAM, mit 64-Bit-Win7 Enterprise .
- Ich habe die Standardeinstellungen für beide DBs verwendet mit Verbindungszeichenfolge "Data Source=database_file_name".
- Ich habe die neuesten Versionen von SQL CE 4.0 und SQLite/System.Data.SQLite verwendet (ab heute, 3. Juni 2011).
Hier sind die Ergebnisse für zwei verschiedene Proben:
> =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
... und ein größeres Beispiel:
=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
Wie Sie sehen können, benötigen alle Schreibvorgänge (Erstellen, Aktualisieren, Löschen) in SQLite fast 1000-mal mehr Zeit im Vergleich zu SQLCE. Dies spiegelt nicht unbedingt die allgemein schlechte Leistung dieser Datenbank wider und kann folgende Ursachen haben:
- Der Datenanbieter, den ich für SQLite verwende, ist System.Data.SQLite , das ist eine gemischte Assembly, die sowohl verwalteten als auch nicht verwalteten Code enthält (SQLite ist ursprünglich vollständig in C geschrieben und die DLL stellt nur Bindungen bereit). Wahrscheinlich verschlingen P/Invoke und Datenmarshaling einen guten Teil der Betriebszeit.
- Wahrscheinlich speichert SQLCE 4.0 standardmäßig alle Daten im Arbeitsspeicher, während SQLite die meisten Datenänderungen jedes Mal, wenn die Änderung eintritt, direkt in den Plattenspeicher schreibt. Man kann Hunderte von Parametern für beide Datenbanken per Verbindungszeichenfolge bereitstellen und sie entsprechend anpassen.
- Ich habe eine Reihe von Einzelabfragen verwendet, um die Datenbank zu testen. Immerhin unterstützt SQLCE Massenoperationen über spezielle .Net-Klassen, die hier besser geeignet wären. Wenn SQLite sie auch unterstützt (sorry, ich bin hier kein Experte und meine schnelle Suche ergab nichts vielversprechendes), wäre es schön, sie auch zu vergleichen.
- Ich habe viele Probleme mit SQLite auf x64-Rechnern (bei Verwendung desselben .net-Adapters) beobachtet:vom unerwarteten Schließen der Datenverbindung bis zur Beschädigung der Datenbankdatei. Ich nehme an, es gibt einige Stabilitätsprobleme entweder mit dem Datenadapter oder mit der Bibliothek selbst.