web-dev-qa-db-ja.com

SQL Server CE 4.0のパフォーマンス比較

SQL Server CE 4(SQL Server Compact Edition 4.0)はまだニュースではありません(もしそうであれば、 この記事を読む

しかし、SQL Server CE 4のパフォーマンスを他のデータベースと比較することは非常に興味深いことです。

特に:

  • SQLite
  • SQLサーバー(1)
  • SQL Server Express *
  • 多分ファイアバード

(1)機能が同等であるアプリケーション用。

残念ながら、Googleが現在提供している件名に関するリンクはそれほど多くありません。実際、私は何も見つけることができませんでした(適切なSQL CEバージョンの場合)。

そのような情報を見つけたり共有したりできる場合は、将来の人類のためにここで収集してみましょう。

37
MajesticRa

私の意見では、組み込みデータベース(SQL CEなど)とサーバー側のリレーショナルデータベース(SQLiteとFirebirdの組み込みバージョンを除くすべてのデータベース)を比較するのは正しくありません。

それらの間の主な違いは、--- MS-SQL、MySQL、Firebird Classic、SuperServerなどの汎用サーバー側リレーショナルデータベースが独立したサービスとしてインストールされ、-外部で実行されることです。メインアプリケーションの範囲。そのため、マルチコアおよびマルチCPUアーキテクチャの本質的なサポートにより、プリキャッシュ、VSSなどのOS機能を使用して、データベース操作が集中した場合のスループットを向上させ、できるだけ多くのメモリを要求できるため、パフォーマンスが大幅に向上します。 OSは単一のサービス/アプリケーションを提供できます。また、それらのパフォーマンスインジケーターはアプリケーションからほぼ独立していますが、ハードウェアに大きく依存しています。この点で、データベースのサーバーバージョンは、埋め込みデータベースに比べて常にパフォーマンスが高いと言えます。

SQL CEは(Firebird Embedded、SQLite、TurboSQLおよびその他のいくつかとともに)組み込みDBエンジンです。これは、データベース全体が単一(または最大2つ)のDLLファイルにパックされ、一緒に配布されることを意味します。あなたの申請。明らかなサイズ制限があるため(30 MBを配布する必要がありますかDLL 2-3 MBの長いアプリケーションと一緒に?)それらものコンテキストで直接実行しますアプリケーションと合計データアクセス操作のメモリとパフォーマンスは、アプリケーションの他の部分と共有されます-これは、利用可能なメモリ、CPU時間、ディスクスループットなどの両方に関係します。データアクセススレッドと並行して実行されるスレッドは、データベースのパフォーマンスを劇的に低下させる可能性があります。

アプリケーションの領域が異なるため、これらのデータベースにはさまざまなオプションパレットがあります。server-dbは、広範なユーザーと権限の管理、ビューとストアドプロシージャのサポートを提供しますが、組み込みデータベースは通常、ユーザーと権限の管理をサポートせず、ビューのサポートが制限されます。とストアドプロシージャ(後半のものは、サーバー側で実行することの利点の大部分を失います)。データスループットはRDBMSの通常のボトルネックです。サーバーバージョンは通常、ストライプRAIDボリュームにインストールされますが、組み込みDBはメモリ指向であることが多く(実際のデータをすべてメモリに保持するようにしてください)、データストレージアクセス操作を最小限に抑えます。

したがって、おそらく意味があるのは、MS SQL CE 4.0、SQLite、Firebird Embedded、TurboSQLなど、パフォーマンスについて.Netのさまざまな埋め込みRDBMSを比較することです。 通常の非ピーク操作中に大幅な違いが発生することはないと思いますが、一部のデータベースでは、OSとの統合が改善されているため、大規模なBLOBのサポートが向上する場合があります。

-更新-

私の迅速な実装が非常に興味深い結果を示しているため、私は最後の言葉を取り戻す必要があります。

両方のデータプロバイダーをテストするための短いコンソールアプリケーションを作成しました。自分で試してみたい場合のソースコードを次に示します。

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. 私のマシンでこれらの結果を得ました:2つのIntel Xeon E5420 CPUと8 GBのRAMを搭載し、64ビットWin7 Enterpriseを実行するDell Precision WorkStation T74
  2. 両方のDBのデフォルト設定を接続文字列 "Data Source = database_file_name"と共に使用しました。
  3. SQL CE 4.0とSQLite/System.Data.SQLiteの両方の最新バージョンを使用しました(今日、2011年6月3日から)。

2つの異なるサンプルの結果は次のとおりです。

> =埋め込みDBのCRUDパフォーマンスのテスト= 
> =>サンプルサイズ:200 
> == SQLiteのテスト== 
>テーブルの作成:396.0396 ms 
>データの書き込み:22189.2187 ms 
>データの更新:23591.3589 ms 
>データの読み取り:21.0021 ms 
>データの削除:20963.0961 ms 
>テーブルの削除:85.0085 ms 
 
> == SQL CE 4.0のテスト== 
>テーブルの作成:16.0016 ms 
>データの書き込み:25.0025 ms 
>データの更新:56.0056ミリ秒
>データの読み取り:28.0028ミリ秒
>データの削除:53.0053ミリ秒
>テーブルの削除:11.0011ミリ秒

...そしてより大きなサンプル:

 =埋め込みDBのCRUDパフォーマンスのテスト= 
 =>サンプルサイズ:1000 
 == SQLiteのテスト== 
作成テーブル:93.0093 ms 
書き込みデータ:116632.6621 ms 
データの更新:104967.4957 ms 
データの読み取り:134.0134 ms 
データの削除:107666.7656 ms 
ドロップテーブル:83.0083 ms 
 
 == SQL CE 4.0のテスト== 
テーブルの作成:16.0016 ms 
書き込みデータ:128.0128 ms 
更新データ:307.0307 ms 
データの読み取り: 164.0164 ms 
データの削除:306.0306 ms 
ドロップテーブル:13.0013 ms 

ご覧のように、書き込み操作(作成、更新、削除)には、SQLCEに比べてSQLiteで約1000倍の時間が必要です。これは、必ずしもこのデータベースの一般的な悪いパフォーマンスを反映しているわけではなく、次の原因が考えられます。

  1. 私がSQLiteに使用するデータプロバイダーは System.Data.SQLite です。これは、マネージコードとアンマネージコードの両方を含む混合アセンブリです(SQLiteは元々完全にCで記述されており、DLLバインディングのみを提供します。おそらくP/Invokeとデータマーシャリングは、操作時間のかなりの部分を消費します。
  2. ほとんどの場合、SQLCE 4.0はデフォルトですべてのデータをメモリにキャッシュしますが、SQLiteはほとんどのデータ変更を、変更が発生するたびにディスクストレージに直接フラッシュします。接続文字列を介して両方のデータベースに何百ものパラメータを提供し、それらを適切に調整できます。
  3. 一連の単一クエリを使用してDBをテストしました。少なくともSQLCEは、ここでより適している特別な.Netクラスを介して一括操作をサポートします。 SQLiteもそれらをサポートしている場合(申し訳ありませんが、私はここの専門家ではありませんが、私の迅速な検索で何も期待できませんでした)、それらを比較することもできます。
  4. X64マシン上のSQLiteで(同じ.netアダプターを使用して)多くの問題を観察しました:データ接続が予期せず閉じられることから、データベースファイルの破損まで。データアダプターまたはライブラリ自体のいずれかにいくつかの安定性の問題があると思います。
16

これは、CodeProjectウェブページのベンチマークについての焼きたての記事です。

。Net用の埋め込みDBのパフォーマンスのベンチマーク:SQL CE 4.0とSQLite

(記事のステータスは保留中です。コンテンツにアクセスするには、CodeProjectにログインする必要があります)

追伸:以前の回答を誤ってコミュニティWikiエントリとしてマークしたため、評価が得られません。これにより、このトピックについて Code Project の記事を書くように促されました。コードはやや最適化されており、埋め込みDBと結果の統計分析に関する追加情報が含まれています。だから、記事と私の2番目の回答が気に入ったら、この回答に投票してください。

14

Alaudoのテスト、テスト結果、そして最終的には彼の結論に本当に苦労しているので、私は先に進んで彼のプログラムを少し遊んで、修正バージョンを思いつきました。

次の各10回をテストし、平均時間を出力します。

  • トランザクションなしのsqlite、デフォルトのjounal_modeを使用
  • デフォルトのjournal_modeを使用したトランザクション付きのsqlite
  • トランザクションなしの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秒はまだ少し高いように見えるかもしれませんが、少なくとも23秒よりは妥当です。逆に、SqlCeが同じ200の挿入または更新を完了するのに少し時間がかかるのではないかと心配するかもしれません。特に、個々のトランザクションで、または1つのトランザクションで一緒に各SQLクエリを実行することで、実際の速度の違いはないようです。 SqlCeについてこれを説明するには十分な知識がありませんが、心配です。 .Commit()が戻ったときに、変更が実際にディスクに書き込まれていることが保証されないということですか?

10
user610650

最近、SQL CE 4とNHibernateを使用するプロジェクトに取り組み、パフォーマンスが非常に優れていることがわかりました。 SQL CE 4では、1秒間に8000レコードを挿入することができました。ネットワークを介してOracleを使用した場合、バッチサイズとseqhiloのアプローチを使用しても、1秒あたり100レコードしか挿入できませんでした。

私はそれをテストしませんでしたが、.NETのNoSQL製品のパフォーマンスレポートのいくつかを見ると、SQL CE 4はスタンドアロンアプリケーションの最良のソリューションの1つであるようです。

Identity列の使用を避けるだけで、使用しない場合はパフォーマンスが40倍向上することがわかりました。 Identity列をPKとして使用した場合、同じ8000レコードの挿入に40秒かかりました。

5
ealbert