在我看来,将嵌入式数据库(如SQL CE)与服务器端关系型数据库(如除SQLite和Firebird Embedded版本之外的所有其他数据库)进行比较是不正确的。
它们之间的主要区别是,通用的服务器端关系型数据库(如MS SQL,MySQL,Firebird Classic和SuperServer等)作为独立服务安装,并在您的主应用程序范围之外运行。这就是为什么它们可以性能更好的原因,因为它们天生支持多核和多CPU架构,使用操作系统功能(如预缓存,VSS等)来增加强大的数据库操作吞吐量,并且可以申请与您的操作系统提供给单个服务/应用程序一样多的内存。这也意味着它们的性能指标与您的应用程序或多或少独立,但在很大程度上取决于您的硬件。从这个角度来看,我会说任何数据库的服务器版本始终比嵌入式版本更具性能。
SQL CE(以及Firebird Embedded、SQLite、TurboSQL和一些其他嵌入式DB引擎)是嵌入式DB引擎,这意味着整个数据库打包到单个(或最多2个)DLL文件中,这些DLL文件与您的应用程序一起分发。由于明显的大小限制(您是否想一起分发30 MB的DLL和2-3 MB长的应用程序?),它们也直接在您的应用程序上下文中运行,并且数据访问操作的总内存和性能与应用程序的其他部分共享--这涉及可用内存,CPU时间,磁盘吞吐量等。并行运行计算密集型线程和数据访问线程可能导致数据库性能显著降低。
由于这些数据库的不同应用领域,它们具有不同的选项调色板:服务器DB提供广泛的用户和权限管理,支持视图和存储过程,而嵌入式数据库通常缺乏任何用户和权限管理的支持,并且对视图和存储过程的支持有限(后者在服务器端运行时失去了其大部分优势)。数据吞吐量是关系型数据库的一个瓶颈,服务器版本通常安装在条带化RAID卷上,而嵌入式DB通常是面向内存的(尝试将所有实际数据保留在内存中)并且最小化数据存储访问操作。
因此,“可能有意义的是比较不同的嵌入式.NET RDBMS的性能,例如MS SQL CE 4.0,SQLite,Firebird Embedded,TurboSQL”。
-- 更新 --
我要收回我的最后一句话,因为我快速实现显示出非常有趣的结果。
我编写了一个简短的控制台应用程序来测试两个数据提供程序,如果您想自己尝试它们,请看这里的源代码。
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)
{
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; };
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();
}
}
}
必要的免责声明:
- 我在我的机器上获得了这些结果:Dell Precision WorkStation T7400配备有2个Intel Xeon E5420 CPU和8GB RAM,运行64位Win7企业版。
- 我使用“Data Source = database_file_name”连接字符串为两个默认设置的DB。
- 我使用了最新版本的SQL CE 4.0和SQLite/System.Data.SQLite(从今天,2011年6月3日)。
以下是两个不同样本的结果:
> =测试嵌入式数据库的CRUD性能=
> => 样本大小:200
> ==测试SQLite==
> 创建表:396.0396毫秒
> 写入数据:22189.2187毫秒
> 更新数据:23591.3589毫秒
> 读取数据:21.0021毫秒
> 删除数据:20963.0961毫秒
> 删除表:85.0085毫秒
> ==测试SQL CE 4.0==
> 创建表:16.0016毫秒
> 写入数据:25.0025毫秒
> 更新数据:56.0056毫秒
> 读取数据:28.0028毫秒
> 删除数据:53.0053毫秒
> 删除表:11.0011毫秒
...还有一个更大的样本:
=测试嵌入式数据库的CRUD性能=
=> 样本大小:1000
==测试SQLite==
创建表:93.0093毫秒
写入数据:116632.6621毫秒
更新数据:104967.4957毫秒
读取数据:134.0134毫秒
删除数据:107666.7656毫秒
删除表:83.0083毫秒
==测试SQL CE 4.0==
创建表:16.0016毫秒
写入数据:128.0128毫秒
更新数据:307.0307毫秒
读取数据:164.0164毫秒
删除数据:306.0306毫秒
删除表:13.0013毫秒
因此,正如您所看到的,与SQLCE相比,任何编写操作(创建、更新、删除)在SQLite中需要几乎1000倍的时间。这并不一定反映了这个数据库的普遍低性能,可能是由于以下原因:
- 我使用的SQLite数据提供程序是System.Data.SQLite,它是一个混合程序集,包含托管和非托管代码(SQLite最初完全由C编写,DLL仅提供绑定)。可能的P / Invoke和数据转换会消耗大量操作时间。
- SQLCE 4.0很可能默认将所有数据缓存在内存中,而每次发生更改时,SQLite会将大部分数据更改直接刷新到磁盘存储器中。可以通过连接字符串为两个数据库提供数百个参数,并适当调整它们。
- 我使用了一系列单个查询来测试数据库。至少SQLCE支持通过特殊的.NET类进行批量操作,这对此处更加合适。如果SQLite也支持它们(很抱歉,我不是专家,在快速搜索中没有找到有希望的结果),也很好比较它们。
- 在x64机器上使用SQLite时(使用相同的.NET适配器),我观察到许多问题:从数据连接意外关闭到数据库文件损坏。我认为数据适配器或库本身存在某些稳定性问题。