Windows サービス アプリ (Web ではない) のデータ ストアに Sql Server Compact 4.0 を使用することを検討しています。
複数の発信者によるサービスのパフォーマンスが心配だったので、簡単なテストを作成しましたが、結果は予想とは正反対でした。(以下のコードを参照)
このテストでは、最初に Entity Framework 5.0 でコードを使用して作成された単純なデータベースのテーブルに 500 行を追加するだけです。
行を追加する方法は独特です。AddRow メソッドを 500 回呼び出し、AddRow メソッドに任せて DbContext のインスタンスを作成し、行を追加してからコンテキストを破棄します。私がこのようにする理由は、AddRow を 500 回呼び出すループが、クライアントからの 500 回の呼び出しをシミュレートするためです (現実的ではないと言うかもしれませんが、立て続けに)
複数のクライアントをシミュレートするために、LoadData メソッドの作業を行ういくつかのタスクを開始します。
観察のために: 1 つのスレッドが LoadData を呼び出し、最後に AddRow を 500 回呼び出すと、ワークステーションで 500 行を追加するのにかかる時間は ~50 秒です (1 秒あたりわずか 10 行です!)
LoadData を呼び出す 5 つのスレッドでは、各スレッドが最大で 50 秒で実行されると予想していましたが、代わりに 5 つのスレッドのうち 4 つが 3 秒で完了し、最後のスレッドは 10 秒で完了しました。
3 秒後に他の 4 つのスレッドが終了し、最後のスレッドが単独で実行されたため、最後のスレッドの方が長く実行されたと思います。
Sql Compact から Local DB に変更すると、パフォーマンスは期待どおりになることに注意してください。各スレッドはほぼ同じ時間、約 3 秒で実行されます。
これは、新しい Visual Studio 2012 コンソール プロジェクトにカット アンド ペーストできる完全なコードと app.config です。
現時点では、シングル スレッド テストでは接続プールは発生していませんが、他のテストでは何らかの形で接続プールが発生しているのではないでしょうか? これは私には意味がありません。私の質問は、単一の呼び出し元スレッドでテストを複数の呼び出し元スレッドと同じくらい速く実行するにはどうすればよいですか?
using System;
using System.Collections.Generic;
using System.Data.Entity;
using System.Diagnostics;
using System.Globalization;
using System.Linq;
using System.Threading.Tasks;
namespace SqlCompactPerf
{
class Program
{
private const int MaxThreads = 5;
private const int RowsToInsert = 500;
private static void Main()
{
using (var context = new MyContext())
context.Database.Delete();
var tasks = new List<Task>();
for (var threadCount = 1; threadCount <= MaxThreads; threadCount++)
tasks.Add(Task<TimeSpan>.Factory.StartNew(LoadData)
.ContinueWith(x =>
Console.WriteLine("Time: {0}", x.Result)));
Task.WaitAll(tasks.ToArray());
using (var context = new MyContext())
Console.WriteLine("Expecting {0}, Found {1}",
RowsToInsert * MaxThreads,
context.Rows.Count());
}
static TimeSpan LoadData()
{
var watch = new Stopwatch();
watch.Start();
for (var i = 1; i <= RowsToInsert; i++)
{
AddRow(DateTime.Now.ToString(CultureInfo.InvariantCulture));
}
watch.Stop();
return watch.Elapsed;
}
static void AddRow(string data)
{
using (var context = new MyContext())
{
context.Rows.Add(new Row { Id = Guid.NewGuid(), Data = data });
context.SaveChanges();
}
}
}
class MyContext : DbContext
{
public DbSet<Row> Rows { get; set; }
}
internal class Row
{
public Guid Id { get; set; }
public String Data { get; set; }
}
}
App.Config ファイルは次のとおりです。
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<configSections>
<!-- For more information on Entity Framework configuration, visit http://go.microsoft.com/fwlink/?LinkID=237468 -->
<section name="entityFramework" type="System.Data.Entity.Internal.ConfigFile.EntityFrameworkSection, EntityFramework, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" requirePermission="false" />
</configSections>
<startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5" />
</startup>
<entityFramework>
<defaultConnectionFactory type="System.Data.Entity.Infrastructure.SqlCeConnectionFactory, EntityFramework">
<parameters>
<parameter value="System.Data.SqlServerCe.4.0" />
</parameters>
</defaultConnectionFactory>
</entityFramework>
<!--<entityFramework>
<defaultConnectionFactory type="System.Data.Entity.Infrastructure.LocalDbConnectionFactory, EntityFramework">
<parameters>
<parameter value="v11.0" />
</parameters>
</defaultConnectionFactory>
</entityFramework>-->
</configuration>