LINQ2SQL具有事务性能

时间:2009-05-13 04:04:45

标签: c# linq-to-sql

我遇到LINQ2SQL和事务的主要性能问题。我的代码使用IDE生成的LINQ2SQL代码执行以下操作:

对现有记录运行存储过程检查 如果记录不存在,请创建该记录 运行在事务中包装自己的代码的存储过程

当我运行没有事务范围的代码时,我每秒得到20次迭代。一旦我将代码包装在事务范围中,它就会降低到每秒3-4次迭代。我不明白为什么在顶级添加事务会降低性能。请帮帮忙?

Psuedo存储过程与事务:

begin transaction
update some_table_1;
insert into some_table_2;
commit transaction;

select some, return, values

没有事务的伪LINQ代码:

var db = new SomeDbContext();
var exists = db.RecordExists(some arguments);

if (!exists) {
    var record = new SomeRecord 
    {
        // Assign property values
    };

    db.RecordsTable.InsertOnSubmit(record);
    db.SubmitChanges();

    var result = db.SomeStoredProcWithTransactions();
}

带有事务的伪LINQ代码:

var db = new SomeDbContext();
var exists = db.RecordExists(some arguments);

if (!exists) {
    using (var ts = new TransactionScope()) 
    {
        var record = new SomeRecord 
        {
            // Assign property values
        };

        db.RecordsTable.InsertOnSubmit(record);
        db.SubmitChanges();

        var result = db.SomeStoredProcWithTransactions();
        ts.Complete();
    }
}

我知道交易没有升级到DTC,因为我已经禁用了DTC。 SQL事件探查器显示启用事务处理器时,有几个查询需要更长时间,但我不确定原因。涉及的查询非常短暂,我已经使用了已验证的索引。我无法确定添加父事务会导致性能下降的原因。

有什么想法吗?

修改

我已将问题跟踪到最终存储过程中的以下查询:

if exists 
(
    select * from entries where 
        ProfileID = @ProfileID and 
        Created >= @PeriodStart and 
        Created < @PeriodEnd
) set @Exists = 1;

如果我有(nolock)如下所示,问题就会消失。

if exists 
(
    select * from entries with(nolock) where 
        ProfileID = @ProfileID and 
        Created >= @PeriodStart and 
        Created < @PeriodEnd
) set @Exists = 1;

然而,我担心这样做可能会导致问题。有什么建议吗?

3 个答案:

答案 0 :(得分:3)

一旦收到交易,一件大事就会发生变化 - isolation level。您的数据库是否存在争议?如果是这样的话:默认情况下,TransactionScope处于最高“可序列化”隔离级别,包括读锁,键范围锁等。如果它无法立即获取这些锁,它会在阻塞时减速。您可以通过降低事务的隔离级别(通过构造函数)进行调查。例如(但选择你自己的隔离级别):

using(var tran = new TransactionScope(TransactionScopeOption.Required,
    new TransactionOptions { IsolationLevel = IsolationLevel.Snapshot })) {
    // code
    tran.Complete();
}

然而,挑选隔离级别是......棘手的; serializable是最安全的(因此是默认值)。您还可以使用粒度提示(但不能通过LINQ-to-SQL),例如NOLOCKUPDLOCK来帮助控制特定表的锁定。


您还可以调查减速是否是由于尝试与DTC对话造成的。启用DTC并查看它是否加速。 LTM很好,但我看到单个数据库的复合操作在之前升级到DTC ......

答案 1 :(得分:0)

您调用的存储过程是否参与环境(父)事务? - 就是那个问题。

存储过程可能会参与环境事务,这会导致降级。有MSDN article here讨论它们如何相互关联。

来自文章:

“当TransactionScope对象加入现有环境事务时,除非范围中止事务,否则处理范围对象可能不会结束事务。如果环境事务是由根范围创建的,则仅在根范围被处置时是否会在事务中调用Commit。如果事务是手动创建的,则事务在中止或由其创建者提交时结束。“

还有一个关于嵌套交易的认真文件,看起来直接适用于MSDN here的本地化。

注意:

“如果在事务处于活动状态时调用TransProc,则TransProc中的嵌套事务在很大程度上被忽略,并且根据对外部事务采取的最终操作提交或回滚其INSERT语句。”

我认为这解释了性能的差异 - 它本质上是维护父事务的成本。 Kristofer的建议可能有助于减少开销。

答案 2 :(得分:0)

虽然您使用的是单个datacontext,但您的代码示例可能会使用多个连接,并且会将您的事务升级为分布式事务。

尝试使用显式数据库连接初始化datacontext,或在创建datacontext后立即调用db.Connection.Open()。这消除了分布式事务的开销......