2012-09-02 19 views
7

我一直在下面的圖表中顯示結果。我想知道是否有任何事情可以提高可預測性。我沒有使用SqlBulkCopy,因爲我需要利用EFv5的驗證功能。EF5爲什麼在SaveChanges()次產生這些尖峯?

如果有人能夠驗證/駁斥我的發現,那將是非常棒的。我的目標是擺脫這兩種尖峯。我在下面提供了源代碼,以便讓您快速找到它。所有你需要的是VS中的類庫項目,引用EFv5和NUnit,都可以通過NuGet獲得。只需將此代碼粘貼到Class1中,修改連接字符串並運行即可。您可以使用下面的sql腳本重新創建表。

我使用.Net 4.5,EF 5,NUnit 2.6.1,在Release模式下運行代碼,不附加任何調試器。數據庫是SqlServer 2008 R2。我以64位模式運行NUnit.exe測試,該模式顯示'Net 4.0'爲框架版本。

EFv5 simple entity insert. 1000 batches of 100 entities

X軸是批次號(1000個批次總計),而Y軸是毫秒。你可以看到第一批需要大約30秒,這是因爲dbContext是「冷」的,所以這是預期的。每批次可以節省100個實體。

請注意,此問題正在尋找this answer中缺少的信息,它是EF保存中的抖動

這裏是我使用的代碼:

表:

CREATE TABLE [dbo].[Entity1](
    [Id] [int] IDENTITY(1,1) NOT NULL, 
    [IntField] [int] NOT NULL, 
    [StrField] [nvarchar](50) NOT NULL, 
    [DateField] [datetimeoffset](7) NOT NULL, 
CONSTRAINT [PK_Entity1] PRIMARY KEY CLUSTERED 
(
    [Id] ASC 
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, 
    ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] 
) ON [PRIMARY] 

的類:

using System; 
using System.Collections.Generic; 
using System.Data.Entity; 
using System.Diagnostics; 
using NUnit.Framework; 

namespace ClassLibrary1 
{ 
    public class Entity1 
    { 
     public int Id { get; protected set; } 
     public int IntField { get; set; } 
     public string StrField { get; set; } 
     public DateTimeOffset DateField { get; set; } 
    } 

    public class MyContext : DbContext 
    { 
     public MyContext(string connStr) : base(connStr) { } 
     public virtual DbSet<Entity1> Entities { get { return base.Set<Entity1>(); } } 
    } 

    [TestFixture] 
    public class Class1 
    { 
     [Test] 
     public void EfPerf() 
     { 
      var entities = buildEntities(100000); 
      int batchSize = 100; 
      var batchTimes = new List<Stopwatch>(); 

      for (int i = 0; i < entities.Length; i += batchSize) 
      { 
       var sw = Stopwatch.StartNew(); 
       using (var ctx = buildCtx()) 
       { 
        for (int j = i; j < i + batchSize; j++) 
         ctx.Entities.Add(entities[j]); 
        ctx.SaveChanges(); 
       } 
       sw.Stop(); 
       batchTimes.Add(sw); 
      } 

      batchTimes.ForEach(sw => Console.Out.WriteLine("Elapsed ms: " + 
       sw.ElapsedMilliseconds)); 
     } 

     private MyContext buildCtx() 
     { 
      var cs = "Data Source=your db server;" + 
        "Initial Catalog=your db;" + 
        "Persist Security Info=True;" + 
        "User ID=your user;" + 
        "Password=your pwd"; 
      var ctx = new MyContext(cs); 
      //ctx.Configuration.ProxyCreationEnabled = false; 
      return ctx; 
     } 

     private Entity1[] buildEntities(int count) 
     { 
      var entities = new Entity1[count]; 
      for (int i = 0; i < count; i++) 
       entities[i] = new Entity1 { IntField = i, StrField = "str" + i, 
        DateField = DateTimeOffset.UtcNow }; 
      return entities; 
     } 
    } 
} 
+0

你可以通過增加批量大小來誇大問題嗎?這可能會讓你打破調試器。每批次調用Debugger.Break後8s進入批處理,您也可以啓動計時器。 – usr

+0

也運行SQL事件探查器查看SQL級別是否發生了尖峯。查看持續時間欄。 – usr

+1

我故意不附加調試器,所以我可以確保這不是一個因素。另外,我運行了Sql Profiler,所有插入的持續時間爲0,只有幾十個中的一個比'0'cpu多(在這種情況下只有大約'15')。 – esegura

回答

1

我中有你正在運行到一個問題與死鎖的感覺你的數據庫。 EF將所有saveChanges()調用放入事務中。默認情況下,SQL Server中的事務作爲讀取提交運行,這是高度限制性的。也許你可以嘗試更改隔離級別,如下所示:

using (var scope = new TransactionScope(TransactionScopeOption.Required, new 
2: TransactionOptions { IsolationLevel= IsolationLevel.Snapshot })) 
3: { 
4: // do something with EF here 
5: scope.Complete(); 
6: } 
相關問題