【问题标题】:Using SQL dB column as a lock for concurrent operations in Entity Framework在实体框架中使用 SQL dB 列作为并发操作的锁
【发布时间】:2012-12-12 23:01:28
【问题描述】:

我们有一个长期运行的用户操作,由工作进程池处理。数据输入和输出来自 Azure SQL。

主 Azure SQL 表结构列近似为

[UserId, col1, col2, ... , col N, beingProcessed, lastTimeProcessed ] 

beingProcessed 是布尔值,lastTimeProcessed 是日期时间。每个工人角色的逻辑如下所示,并且有多个工人处理(每个工人都有自己的实体框架层),本质上beingProcessed 被用作互斥目的的锁

问题:如何根据上述负载处理beingProcessed“锁”本身的并发问题?我认为beingProcessed 上的read-modify-write 操作需要是原子的,但我对其他策略持开放态度。也对其他代码改进开放。

[更新]:不知道TransactionScope是不是这里需要的...http://msdn.microsoft.com/en-US/library/system.transactions.transactionscope(v=vs.110).aspx

代码:

public void WorkerRoleMain()
{
    while(true)
    {
        try
        {
            dbContext db = new dbContext();

            // Read
            foreach (UserProfile user in db.UserProfile
                    .Where(u => DateTime.UtcNow.Subtract(u.lastTimeProcessed) 
                            > TimeSpan.FromHours(24) & 
                            u.beingProcessed == false))
            {
                user.beingProcessed = true; // Modify
                db.SaveChanges();           // Write
                // Do some long drawn processing here
                ...
                ...
                ...
                user.lastTimeProcessed = DateTime.UtcNow;
                user.beingProcessed = false;
                db.SaveChanges();
            }
        }
        catch(Exception ex)
        {
            LogException(ex);
            Sleep(TimeSpan.FromMinutes(5));
        }
    } // while ()
}

【问题讨论】:

    标签: sql entity-framework locking azure-sql-database mutual-exclusion


    【解决方案1】:

    我们通常做的是这样的:

    在一个长操作的开始我们开始一个事务:

    BEGIN TRANSACTION
    

    然后我们使用这些提示从表中选择要更新/删除的行:

    SELECT * FROM Table WITH (ROWLOCK, NOWAIT) Where ID = 123;
    

    然后我们检查我们是否有该行。如果该行被另一个进程锁定,则会出现 SQL 错误。在这种情况下,我们回滚事务并通知用户。 如果记录被锁定,我们会处理记录,并使用我们用来锁定记录的相同事务对象进行所需的更新:

    UPDATE Table SET Col1='value' WHERE ID = 123;
    

    然后我们提交事务。

    COMMIT;
    

    这只是该过程的伪代码。你必须在你的程序中实现它。

    关于上述过程的一点小提示。在 SQL Server(或 Azure)中锁定记录时,请在 WHERE 子句中使用主键,否则 SQL Server 将决定使用页锁或表锁

    【讨论】:

    • 谢谢,但这需要我们将上述内容作为存储过程,然后从 EF 调用存储过程(在数据库首先,它也要求读取存储过程)。是否可以留在实体框架中以实现读取-修改-写入的原子性而不诉诸存储过程?
    • 简单的选择不是问题 (.where(...)) - 问题的核心是原子性。
    • 如果您将更新保持在 BEGIN ... COMMIT TRANSACTION 中,这个过程几乎是原子的
    • 你错过了这个问题,我正在 EF 中尝试这个 - 不是在 SQL 脚本中,也不是在 SQL 存储过程中。尽管 EF 最终可能会使用相同的机制,但我需要在 EF 本身中构建它们。
    • 我没有错过这个问题。上面的过程只是我们在各种系统(PowerBuilder、MFC、WinApp)中使用的伪代码。我们创建了包装类来支持最终将执行这些步骤的过程。重要的是从表中选择 WITH (ROWLOCK, NOWAIT) 来检查记录是否被锁定,并在需要时锁定它。如果你熟悉的话,它相当于 Oracle 中的 FOR UPDATE NOWAIT。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-10-31
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多