2011-05-01 61 views
6

歷史如何加快LINQ插入與SQL CE?

我的「記錄」(3,500),我保存爲XML和壓縮的程序的退出名單。因爲:

  • 的記錄數增加
  • 只有約50記錄需要退出時進行更新
  • 儲蓄持續3秒鐘

我需要另一種解決方案 - 嵌入式數據庫。我選擇SQL CE,因爲它與VS工作沒有任何問題,並且許可證是OK,我(我將它比作火鳥SQLite的EffiProzdb4o的的BerkeleyDB)。

數據

的記錄結構:11個字段,其中2使主鍵(nvarchar的+字節)。其他記錄是字節,數據時間,雙精度和整數。

我不使用任何關係,連接,索引(主鍵除外),觸發器,視圖等。它實際上是扁平的字典 - 對鍵+值。我修改其中的一些,然後我必須在數據庫中更新它們。我不時添加一些新的「記錄」,我需要存儲(插入)它們。就這樣。

LINQ方法

我有空白的數據庫(文件),所以我做3500箇中插入一個循環(一個接一個)。我甚至不檢查記錄是否已經存在,因爲db是空白的。

執行時間? 4分52秒。我暈倒了(介意你:XML +壓縮= 3秒)。

SQL CE原始方法

我GOOGLE了一下,儘管這種要求如下: LINQ to SQL (CE) speed versus SqlCe 說明它是SQL CE本身的錯,我給它一個嘗試。

相同的循環,但這次插入是用SqlCeResultSet(DirectTable模式,請參閱:Bulk Insert In SQL Server CE)和SqlCeUpdatableRecord。

結果呢?你坐得舒服嗎?那麼...... 0.3秒(是的,秒數的一小部分!)。

問題

LINQ是非常可讀性和原始操作完全相反。我可以編寫一個將所有列索引轉換爲有意義名稱的映射器,但似乎重新發明了輪子 - 畢竟它已經在...... LINQ中完成了。

所以也許這是一種告訴LINQ加快速度的方法嗎? 問題 - 怎麼做?

代碼

LINQ

foreach (var entry in dict.Entries.Where(it => it.AlteredByLearning)) 
{ 
    PrimLibrary.Database.Progress record = null; 

     record = new PrimLibrary.Database.Progress(); 
     record.Text = entry.Text; 
     record.Direction = (byte)entry.dir; 
     db.Progress.InsertOnSubmit(record); 

    record.Status = (byte)entry.LastLearningInfo.status.Value; 
    // ... and so on 

    db.SubmitChanges(); 
} 

原始操作

SqlCeCommand CMD = conn.CreateCommand();

cmd.CommandText =「Progress」; cmd.CommandType = System.Data.CommandType.TableDirect; SqlCeResultSet rs = cmd.ExecuteResultSet(ResultSetOptions.Updatable);

foreach (var entry in dict.Entries.Where(it => it.AlteredByLearning)) 
{ 
    SqlCeUpdatableRecord record = null; 

    record = rs.CreateRecord(); 

    int col = 0; 
    record.SetString(col++, entry.Text); 
    record.SetByte(col++,(byte)entry.dir); 
    record.SetByte(col++,(byte)entry.LastLearningInfo.status.Value); 
    // ... and so on 

    rs.Insert(record); 
} 
+0

你能展示一些代碼示例嗎? – 2011-05-01 18:07:41

+0

@Kevin Pullin,當然,我添加了必要的部分來理解我在兩種情況下如何插入。 – greenoldman 2011-05-01 19:06:47

+0

你有沒有試過['BinaryFormatter'](http://msdn.microsoft.com/en-us/library/system.runtime.serialization.formatters.binary.binaryformatter.aspx)? – svick 2011-05-01 20:03:20

回答

8

每次交易做更多工作。

承諾一般是非常昂貴的操作對於一個典型的關係型數據庫作爲數據庫必須等待磁盤刷新,以確保數據不丟失(ACID guarantees和所有)。沒有專業控制器的傳統HDD磁盤IO是非常緩慢在這種操作:數據必須被刷新到物理磁盤 - 也許只有30-60提交可能發生一秒之間IO同步!

請參閱SQLite常見問題:INSERT is really slow - I can only do few dozen INSERTs per second。忽略不同的數據庫引擎,這是完全相同的問題。

通常情況下,LINQ2SQL在SubmitChanges中創建一個新的隱式事務。爲了避免這種隱性事務/提交(提交是昂貴的操作)之一:

  1. 呼叫SubmitChanges少(比如說,一旦循環);或

  2. 設置明確的交易範圍(請參閱TransactionScope)。使用較大的事務上下文

一個例子是:

using (var ts = new TransactionScope()) { 
    // LINQ2SQL will automatically enlist in the transaction scope. 
    // SubmitChanges now will NOT create a new transaction/commit each time. 
    DoImportStuffThatRunsWithinASingleTransaction(); 
    // Important: Make sure to COMMIT the transaction. 
    // (The transaction used for SubmitChanges is committed to the DB.) 
    // This is when the disk sync actually has to happen, 
    // but it only happens once, not 3500 times! 
    ts.Complete(); 
} 

然而,這種方法的使用單個事務或的SubmitChanges單個呼叫語義是除上述調用代碼的不同提交更改3500次並創建3500個不同的隱式事務。特別是,原子操作的大小(相對於數據庫)是不同的,可能不適用於所有任務。

對於LINQ2SQL更新,更改開放式併發模型(禁用它或僅使用時間戳字段)可能會導致性能改進很小。然而,最大的改進將來自於減少必須執行的提交次數。

快樂編碼。

+0

感謝您的充實。我對linq2sql沒什麼了不起。 +1 – 2011-05-01 20:45:32

+0

儘管它在我眼前是正確的,但我忽略了這一點,認爲在桌面環境中這是微不足道的,因爲有兩個因素--SQL CE引擎每次都不重新加載,沒有連接處罰。哦,男孩,我真的太離譜了 - 有一次振奮人心,而我差不多5分鐘就差不多5秒鐘了(這比DirectTable慢了一點,但對我來說很好)。謝謝你的冷水淋浴:-)。 – greenoldman 2011-05-02 17:29:19

5

我對此並不積極,但似乎db.SubmitChanges()調用應在循環之外進行。也許這會加快速度?

+0

你的想法非常正確! :-) – greenoldman 2011-05-02 17:36:40