Entity Framework に挿入する最も速い方法を探しています。
私がこれを質問しているのは、アクティブがありTransactionScope
、挿入が非常に大きい (4000 以上) シナリオのためです。潜在的に 10 分以上 (トランザクションのデフォルトのタイムアウト) 続く可能性があり、これによりトランザクションが不完全になります。
ベストアンサー1
あなたの質問に対するコメントへのコメント:
「...SavingChanges (各レコードに対して)...」
それは最悪のことです。SaveChanges()
各レコードを呼び出すと、一括挿入の速度が極端に低下します。パフォーマンスが向上する可能性が高い簡単なテストをいくつか実行します。
- すべての記録の後に 1 回呼び出します
SaveChanges()
。 SaveChanges()
たとえば、100 件のレコードの後に呼び出します。- たとえば、100 件のレコードの後に呼び出して
SaveChanges()
コンテキストを破棄し、新しいコンテキストを作成します。 - 変更検出を無効にする
一括挿入の場合、次のようなパターンで作業および実験を行っています。
using (TransactionScope scope = new TransactionScope())
{
MyDbContext context = null;
try
{
context = new MyDbContext();
context.Configuration.AutoDetectChangesEnabled = false;
int count = 0;
foreach (var entityToInsert in someCollectionOfEntitiesToInsert)
{
++count;
context = AddToContext(context, entityToInsert, count, 100, true);
}
context.SaveChanges();
}
finally
{
if (context != null)
context.Dispose();
}
scope.Complete();
}
private MyDbContext AddToContext(MyDbContext context,
Entity entity, int count, int commitCount, bool recreateContext)
{
context.Set<Entity>().Add(entity);
if (count % commitCount == 0)
{
context.SaveChanges();
if (recreateContext)
{
context.Dispose();
context = new MyDbContext();
context.Configuration.AutoDetectChangesEnabled = false;
}
}
return context;
}
560,000 個のエンティティ (9 つのスカラー プロパティ、ナビゲーション プロパティなし) を DB に挿入するテスト プログラムがあります。このコードを使用すると、3 分以内に動作します。
パフォーマンスのためには、SaveChanges()
多数のレコード (100 または 1000 程度の多数のレコード) の後で呼び出すことが重要です。また、SaveChanges の後でコンテキストを破棄して新しいコンテキストを作成すると、パフォーマンスが向上します。これにより、すべてのエンティティからコンテキストがクリアされますが、SaveChanges
そうではない場合、エンティティは状態のコンテキストにまだ添付されていますUnchanged
。挿入が徐々に遅くなるのは、コンテキストに添付されたエンティティのサイズが大きくなるためです。したがって、しばらくしてからクリアすると便利です。
以下に、560000 個のエンティティの測定値をいくつか示します。
- commitCount = 1、recreateContext = false:数時間(これが現在の手順です)
- commitCount = 100、recreateContext = false: 20分以上
- commitCount = 1000、recreateContext = false: 242 秒
- commitCount = 10000、recreateContext = false: 202 秒
- commitCount = 100000、recreateContext = false: 199 秒
- commitCount = 1000000、recreateContext = false:メモリ不足例外
- commitCount = 1、recreateContext = true: 10分以上
- commitCount = 10、recreateContext = true: 241 秒
- commitCount = 100、recreateContext = true: 164 秒
- commitCount = 1000、recreateContext = true: 191 秒
上記の最初のテストでの動作は、パフォーマンスが非常に非線形であり、時間の経過とともに極端に低下することです。(「何時間も」は推定値であり、このテストは完了せず、20 分後に 50,000 エンティティで停止しました。) この非線形動作は、他のすべてのテストではそれほど重要ではありません。