SQL Server CE 4.0性能比较

SQL Server CE 4(SQL Server Compact Edition 4.0)已不是新闻(如果是,可以阅读本文 )

但是看到SQL Server CE 4与其他数据库的性能比较是非常有趣的。

特别是:

  • SQLite的
  • SQL Server(1)
  • SQL Server Express *
  • 也许火鸟

(1)function相当的应用程序。

不幸的是,谷歌现在提供的主题并不多。 其实我无法find任何(适当的SQL CE版本)。

如果可以find或分享这些信息,可以在这里收集,供未来人类使用。

在我看来,将embedded式数据库(如SQL CE)与服务器端关系数据库(除了SQLite和embedded式版本的Firebird之外)进行比较是不正确的。

它们之间的主要区别在于, 通用服务器端关系数据库 (如MS SQL,MySQL,Firebird Classic和SuperServer等)作为独立服务安装,并且在主应用程序的范围之外运行 。 这就是为什么他们可以执行得更好,这是因为对多核和多CPU架构的内在支持,使用诸如预caching,VSS等操作系统特性来提高数据库操作密集度时的吞吐量,并且可以占用尽可能多的内存你的操作系统可以提供一个服务/应用程序。 这也意味着它们的性能指标或多或less与您的应用程序相互独立,但很大程度上取决于您的硬件。 在这方面,我会说任何数据库的服务器版本总是比embedded式的更高性能。

SQL CE(以及Firebird Embedded,SQLite,TurboSQL和其他) embedded了数据库引擎 ,这意味着完整的数据库被打包成一个(或最多2个)DLL文件,与应用程序一起分发。 由于明显的大小限制(您是否需要将一个30 MB的DLL与2-3 MB的应用程序一起分发?),它们也直接运行在应用程序的上下文中 ,数据访问操作的总内存和性能是与可用内存,CPU时间,磁盘吞吐量等相关的应用程序的其他部分共享 。计算密集型线程与数据访问线程并行运行可能会导致数据库性能急剧下降。

由于不同的应用领域,这些数据库有不同的选项:server-db提供广泛的用户和权限pipe理,支持视图和存储过程,而embedded式数据库通常缺less对用户和权限pipe理的支持,并且对视图的支持有限和存储过程(后者在服务器端失去了大部分的好处)。 数据吞吐量是RDBMS常见的瓶颈,服务器版本通常安装在条带化的RAID卷上,而embedded式数据库通常是面向内存的(尽量保留内存中的所有实际数据),并尽量减less数据存储访问操作。

所以, 有意义的可能是比较不同的embedded式RDBMS for .Net的性能,如MS SQL CE 4.0,SQLite,Firebird Embedded,TurboSQL在通常的非高峰操作期间,我不会期望有很大的差异,但是由于与操作系统更好的集成,某些数据库可能会提供更好的对大BLOB的支持。

– 更新 –

我必须收回我的遗言,因为我的快速实施显​​示了非常有趣的结果。

我编写了一个简短的控制台应用程序来testing两个数据提供者,如果你想自己尝试一下,那么这里是你的源代码。

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(); } } } 

必要的免责声明:

  1. 我在机器上得到了这些结果: 戴尔Precision WorkStation T7400配备了2个Intel Xeon E5420 CPU和8GB内存,运行64位Win7 Enterprise
  2. 我使用连接string“Data Source = database_file_name”的两个DB默认设置
  3. 我使用了最新版本的SQL CE 4.0和SQLite / System.Data.SQLite(从今天起,2011年6月3日)。

以下是两个不同样本的结果:

 > =testingembedded式数据库的CRUD性能=  
 > =>样本大小:200
 > ==testingSQLite == 
 >创build表格:396.0396毫秒 
 >写入数据:22189.2187 ms 
 >更新数据:23591.3589 ms
 >读数据:21.0021毫秒 
 >删除数据:20963.0961毫秒 
 >滴桌:85.0085毫秒

 > ==testingSQL CE 4.0 == 
 >创build表格:16.0016毫秒 
 >写入数据:25.0025毫秒 
 >更新数据:56.0056毫秒 
 >读数据:28.0028毫秒 
 >删除数据:53.0053毫秒 
 >滴桌:11.0011毫秒

…和一个更大的样本:

 =testingembedded式数据库的CRUD性能=
  =>样本大小:1000
 ==testingSQLite ==
创build表格:93.0093毫秒
写入数据:116632.6621毫秒
更新数据:104967.4957毫秒
读数据:134.0134毫秒
删除数据:107666.7656毫秒
滴桌:83.0083毫秒

 ==testingSQL CE 4.0 ==
创build表格:16.0016毫秒
写入数据:128.0128 ms
更新数据:307.0307毫秒
读数据:164.0164毫秒
删除数据:306.0306毫秒
滴桌:13.0013毫秒

所以,正如您所看到的,与SQLCE相比,任何写入操作(创build,更新,删除)在SQLite中所需的时间都将多出1000倍。 这并不一定反映这个数据库的一般性能不佳,可能是由于以下原因:

  1. 我用于SQLite的数据提供者是System.Data.SQLite ,它是一个包含托pipe代码和非托pipe代码的混合程序集(SQLite最初是用C完全编写的,DLL只提供绑定)。 可能是P / Invoke和数据编组,占用了一大段运行时间。
  2. 很可能,SQLCE 4.0默认情况下将所有数据caching在内存中,而每当发生更改时,SQLite会将大部分数据更改直接刷新到磁盘存储。 可以通过连接string为两个数据库提供数百个参数,并适当调整它们。
  3. 我使用了一系列单个查询来testing数据库。 至lessSQLCE支持通过特殊的.Net类进行批量操作,在这里更适合。 如果SQLite支持它们(对不起,我不是这里的专家,我的快速search没有什么前途),这将是很好的比较它们。
  4. 我已经观察到x64机器(使用相同的.net适配器)上的SQLite的许多问题:从意外closures数据连接到数据库文件损坏。 我认为有数据适配器或库本身存在一些稳定性问题。

这里是我刚刚出炉的关于CodeProject网页基准testing的文章:

基准testing.Net的embedded式数据库的性能:SQL CE 4.0与SQLite

(该文章现在有一个挂起状态,你需要在CodeProject上login才能访问它的内容)

PS:我错误地将我以前的答案标记为社区wiki条目,并且不会获得任何声誉。 这鼓励我为这个主题编写代码项目的文章,有一些优化的代码,更多关于embedded式数据库的附加信息和结果的统计分析。 所以,如果你喜欢这篇文章,我的第二个答案在这里,请投票回答。

因为我在Alaudo的testing,testing结果以及最后的结论中遇到了一个很难的事情,所以我继续前进,并且在他的程序中玩了一下,并提出了一个修改的版本。

它testing以下每个10次,并输出平均次数:

  • 没有事务的sqlite,使用默认的jounal_mode
  • SQLite与交易,使用默认journal_mode
  • 没有事务的sqlite,使用WAL jounal_mode
  • SQLite与交易,使用WAL journal_mode
  • 没有事务的sqlce
  • sqlce与交易

这里是程序(实际上是一个类):

 using System; using System.Collections.Generic; using System.Data.Common; using System.Data.SqlServerCe; using System.Data.SQLite; using System.Diagnostics; using System.IO; using System.Linq; class SqliteAndSqlceSpeedTesting { class Results { public string test_details; public long create_table_time, insert_time, update_time, select_time, delete_time, drop_table_time; } enum DbType { Sqlite, Sqlce }; const int NUMBER_OF_TESTS = 200; const string create_table_sqlite = "CREATE TABLE Test (id integer not null primary key, textdata nvarchar(500));"; const string create_table_sqlce = "CREATE TABLE Test (id integer not null identity primary key, textdata nvarchar(500));"; const string drop_table = "DROP TABLE Test"; const string insert_data = "INSERT INTO Test (textdata) VALUES ('{0}');"; const string read_data = "SELECT textdata FROM Test WHERE id = {0}"; const string update_data = "UPDATE Test SET textdata = '{1}' WHERE id = {0}"; const string delete_data = "DELETE FROM Test WHERE id = {0}"; public static void RunTests() { List<Results> results_list = new List<Results>(); for (int i = 0; i < 10; i++) { results_list.Add(RunTest(DbType.Sqlite, false, false)); results_list.Add(RunTest(DbType.Sqlite, false, true)); results_list.Add(RunTest(DbType.Sqlite, true, false)); results_list.Add(RunTest(DbType.Sqlite, true, true)); results_list.Add(RunTest(DbType.Sqlce, false)); results_list.Add(RunTest(DbType.Sqlce, true)); } foreach (var test_detail in results_list.GroupBy(r => r.test_details)) { Console.WriteLine(test_detail.Key); Console.WriteLine("Creating table: {0} ms", test_detail.Average(r => r.create_table_time)); Console.WriteLine("Inserting data: {0} ms", test_detail.Average(r => r.insert_time)); Console.WriteLine("Updating data: {0} ms", test_detail.Average(r => r.update_time)); Console.WriteLine("Selecting data: {0} ms", test_detail.Average(r => r.select_time)); Console.WriteLine("Deleting data: {0} ms", test_detail.Average(r => r.delete_time)); Console.WriteLine("Dropping table: {0} ms", test_detail.Average(r => r.drop_table_time)); Console.WriteLine(); } } static Results RunTest(DbType db_type, bool use_trx, bool use_wal = false) { DbConnection conn = null; if (db_type == DbType.Sqlite) conn = GetConnectionSqlite(use_wal); else conn = GetConnectionSqlce(); Results results = new Results(); results.test_details = string.Format("Testing: {0}, transactions: {1}, WAL: {2}", db_type, use_trx, use_wal); results.create_table_time = CreateTable(conn, db_type); results.insert_time = InsertTime(conn, use_trx); results.update_time = UpdateTime(conn, use_trx); results.select_time = SelectTime(conn, use_trx); results.delete_time = DeleteTime(conn, use_trx); results.drop_table_time = DropTableTime(conn); conn.Close(); return results; } static DbConnection GetConnectionSqlite(bool use_wal) { SQLiteConnection conn = new SQLiteConnection("Data Source=sqlite.db"); if (!File.Exists(conn.Database)) SQLiteConnection.CreateFile("sqlite.db"); conn.Open(); if (use_wal) { var command = conn.CreateCommand(); command.CommandText = "PRAGMA journal_mode=WAL"; command.ExecuteNonQuery(); } return conn; } static DbConnection GetConnectionSqlce() { SqlCeConnection conn = new SqlCeConnection("Data Source=sqlce.sdf"); if (!File.Exists(conn.Database)) using (var sqlCeEngine = new SqlCeEngine("Data Source=sqlce.sdf")) sqlCeEngine.CreateDatabase(); conn.Open(); return conn; } static long CreateTable(DbConnection con, DbType db_type) { Stopwatch sw = Stopwatch.StartNew(); var sqlcmd = con.CreateCommand(); if (db_type == DbType.Sqlite) sqlcmd.CommandText = create_table_sqlite; else sqlcmd.CommandText = create_table_sqlce; sqlcmd.ExecuteNonQuery(); return sw.ElapsedMilliseconds; } static long DropTableTime(DbConnection con) { Stopwatch sw = Stopwatch.StartNew(); var sqlcmd = con.CreateCommand(); sqlcmd.CommandText = drop_table; sqlcmd.ExecuteNonQuery(); return sw.ElapsedMilliseconds; } static long InsertTime(DbConnection con, bool use_trx) { Stopwatch sw = Stopwatch.StartNew(); var sqlcmd = con.CreateCommand(); DbTransaction trx = null; if (use_trx) { trx = con.BeginTransaction(); sqlcmd.Transaction = trx; } for (int i = 0; i < NUMBER_OF_TESTS; i++) { sqlcmd.CommandText = string.Format(insert_data, Guid.NewGuid().ToString()); sqlcmd.ExecuteNonQuery(); } if (trx != null) trx.Commit(); return sw.ElapsedMilliseconds; } static long SelectTime(DbConnection con, bool use_trx) { Stopwatch sw = Stopwatch.StartNew(); var sqlcmd = con.CreateCommand(); DbTransaction trx = null; if (use_trx) { trx = con.BeginTransaction(); sqlcmd.Transaction = trx; } Random rnd = new Random(DateTime.Now.Millisecond); for (var max = NUMBER_OF_TESTS; max-- > 0; ) { sqlcmd.CommandText = string.Format(read_data, rnd.Next(1, NUMBER_OF_TESTS - 1)); sqlcmd.ExecuteNonQuery(); } if (trx != null) trx.Commit(); return sw.ElapsedMilliseconds; } static long UpdateTime(DbConnection con, bool use_trx) { Stopwatch sw = Stopwatch.StartNew(); var sqlcmd = con.CreateCommand(); DbTransaction trx = null; if (use_trx) { trx = con.BeginTransaction(); sqlcmd.Transaction = trx; } Random rnd = new Random(DateTime.Now.Millisecond); for (var max = NUMBER_OF_TESTS; max-- > 0; ) { sqlcmd.CommandText = string.Format(update_data, rnd.Next(1, NUMBER_OF_TESTS - 1), Guid.NewGuid().ToString()); sqlcmd.ExecuteNonQuery(); } if (trx != null) trx.Commit(); return sw.ElapsedMilliseconds; } static long DeleteTime(DbConnection con, bool use_trx) { Stopwatch sw = Stopwatch.StartNew(); Random rnd = new Random(DateTime.Now.Millisecond); var order = Enumerable.Range(1, NUMBER_OF_TESTS).ToArray<int>(); Action<int[], int, int> swap = (arr, a, b) => { int c = arr[a]; arr[a] = arr[b]; arr[b] = c; }; var sqlcmd = con.CreateCommand(); DbTransaction trx = null; if (use_trx) { trx = con.BeginTransaction(); sqlcmd.Transaction = trx; } // shuffling the array for (var max = NUMBER_OF_TESTS; max-- > 0; ) swap(order, rnd.Next(0, NUMBER_OF_TESTS - 1), rnd.Next(0, NUMBER_OF_TESTS - 1)); foreach (int index in order) { sqlcmd.CommandText = string.Format(delete_data, index); sqlcmd.ExecuteNonQuery(); } if (trx != null) trx.Commit(); return sw.ElapsedMilliseconds; } } 

这里是我得到的数字:

 Testing: Sqlite, transactions: False, WAL: False Creating table: 24.4 ms Inserting data: 3084.7 ms Updating data: 3147.8 ms Selecting data: 30 ms Deleting data: 3182.6 ms Dropping table: 14.5 ms Testing: Sqlite, transactions: False, WAL: True Creating table: 2.3 ms Inserting data: 14 ms Updating data: 12.2 ms Selecting data: 6.8 ms Deleting data: 11.7 ms Dropping table: 0 ms Testing: Sqlite, transactions: True, WAL: False Creating table: 13.5 ms Inserting data: 20.3 ms Updating data: 24.5 ms Selecting data: 7.8 ms Deleting data: 22.3 ms Dropping table: 16.7 ms Testing: Sqlite, transactions: True, WAL: True Creating table: 3.2 ms Inserting data: 5.8 ms Updating data: 4.9 ms Selecting data: 4.4 ms Deleting data: 3.8 ms Dropping table: 0 ms Testing: Sqlce, transactions: False, WAL: False Creating table: 2.8 ms Inserting data: 24.4 ms Updating data: 42.8 ms Selecting data: 30.4 ms Deleting data: 38.3 ms Dropping table: 3.3 ms Testing: Sqlce, transactions: True, WAL: False Creating table: 2.1 ms Inserting data: 24.6 ms Updating data: 44.2 ms Selecting data: 32 ms Deleting data: 37.8 ms Dropping table: 3.2 ms 

对于使用sqlite的200次插入或更新,大约3秒钟可能仍然有点高,但至less比23秒更合理。 相反,人们可能会担心SqlCe如何花费太less的时间来完成相同的200次插入或更新,特别是因为在单个事务中进行每个SQL查询或在一次事务中进行每个SQL查询似乎没有真正的速度差异。 我不太了解SqlCe来解释这一点,但它令我担心。 这是否意味着当.Commit()返回时,您不确定这些更改是否实际写入磁盘?

我最近在使用SQL CE 4和NHibernate的项目上工作,我发现性能是非常好的。 使用SQL CE 4,我们能够在一秒钟内插入8000条logging。 通过networking上的Oracle,我们只能够每秒插入100条logging,甚至使用批处理和seqhilo方法。

我没有对它进行testing,但是看看用于.NET的NoSQL产品的一些性能报告,SQL CE 4似乎是独立应用程序的最佳解决scheme之一。

只要避免使用标识列,我们注意到,如果不使用,性能会提高40倍。 同样的8000条logging在身份列用作PK时需要40秒才能插入。