如何使用SQL CE加速LINQ插入?

时间:2011-05-01 16:48:25

标签: c# .net linq optimization sql-server-ce

记录

我有一个“记录”列表(3,500),我保存到XML并在退出程序时压缩。时间:

  • 记录数增加
  • 退出时只需更新约50条记录
  • 保存大约需要3秒

我需要另一个解决方案 - 嵌入式数据库。我之所以选择SQL CE,是因为它与VS一起使用没有任何问题,许可证对我来说是好的(我把它比作 Firebird SQLite EffiProz db4o BerkeleyDB )。

数据

记录结构:11个字段,其中2个构成主键(nvarchar + byte)。其他记录是字节,数据时间,双精度和整数。

我不使用任何关系,连接,索引(主键除外),触发器,视图等。它实际上是平的字典 - 对键+值。我修改了其中一些,然后我必须在数据库中更新它们。我不时添加一些新的“记录”,我需要存储(插入)它们。就是这样。

LINQ方法

我有空白数据库(文件),所以我在循环中逐个插入3500(逐个)。我甚至不检查记录是否已经存在,因为db是空白的。

执行时间? 4分52秒我晕倒了(记住你:XML + compress = 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 =“进度”; 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);
}

2 个答案:

答案 0 :(得分: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)。

  3. 使用更大的事务上下文的一个示例是:

    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的方法的语义不同于上面调用SubmitChanges 3500次并创建3500个不同隐式事务的代码的语义。特别是,原子操作的大小(相对于数据库)是不同的,可能不适合所有任务。

    对于LINQ2SQL更新,更改乐观并发模型(例如,禁用它或仅使用时间戳字段)可能会导致性能提升很小。然而,最大的改进将来自减少必须执行的提交数量。

    快乐的编码。

答案 1 :(得分:5)

我对此并不乐观,但似乎db.SubmitChanges()调用应该在循环之外进行。也许这会加快速度?