简体   繁体   English

使用SQL dB列作为Entity Framework中并发操作的锁

[英]Using SQL dB column as a lock for concurrent operations in Entity Framework

We have a long running user operation that is handled by a pool of worker processes. 我们有一个运行时间较长的用户操作,它由一组工作进程处理。 Data input and output is from Azure SQL. 数据输入和输出来自Azure SQL。

The master Azure SQL table structure columns are approximated to 主要的Azure SQL表结构列近似为

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

beingProcessed is boolean and lastTimeProcessed is DateTime. beingProcessed为布尔值, lastTimeProcessed为DateTime。 The logic in every worker role is as shown below and with multiple workers processing (each with their own Entity Framework layer), in essence beingProcessed is being used a lock for MutEx purposes 每个工作角色中的逻辑如下所示,并且有多个工作进程处理(每个工作进程都有自己的实体框架层),实质上,正在处理中的beingProcessed被用作MutEx目的的锁

Question : How can I deal with concurrency issues on the beingProcessed "lock" itself based on the above load? :我该如何处理的并发问题beingProcessed基于上述负荷“锁”本身? I think read-modify-write operation on the beingProcessed needs to be atomic but I'm open to other strategies. 我认为对beingProcessed read-modify-write操作需要是原子的,但我愿意接受其他策略。 Open to other code refinements too. 也可以进行其他代码改进。

[Update]: I wonder if TransactionScope is what's needed here ... http://msdn.microsoft.com/en-US/library/system.transactions.transactionscope(v=vs.110).aspx [更新]:我想知道这里是否需要TransactionScope ... http://msdn.microsoft.com/zh-CN/library/system.transactions.transactionscope(v=vs.110).aspx

Code: 码:

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 ()
}

What we usually do is this: 我们通常这样做的是:

At the beginning of a long operation we start a transaction: 在长时间操作的开始,我们开始交易:

BEGIN TRANSACTION

Then we select a row from the table we would like to update/delete using these hints: 然后,我们使用以下提示从要更新/删除的表中选择一行:

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

Then we check that we have the row. 然后,我们检查是否有该行。 If the row is locked by another process there will be an SQL Error. 如果该行被另一个进程锁定,则将出现SQL错误。 In this case we rollback the transaction and advise the user. 在这种情况下,我们回滚交易并建议用户。 If the record is locked we process the record, and do the required updates, using the same transaction object we used to lock the record: 如果记录被锁定,我们将使用与锁定记录相同的事务对象来处理记录并进行所需的更新:

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

Then we COMMIT the transaction. 然后我们提交交易。

COMMIT;

This is just the Pseudo-code of the process. 这只是该过程的伪代码。 You will have to implement it in your program. 您将必须在程序中实现它。

One small note regarding the above process. 关于上述过程的一小记。 When you lock the record in SQL Server (or Azure), use the primary key in your WHERE Clause, otherwise the SQL Server will decide to use a Page lock, or Table lock 当您在SQL Server(或Azure)中锁定记录时,请在WHERE子句中使用主键,否则SQL Server将决定使用页锁或表锁

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM