2008-09-23 98 views
2

我正在开发一个应用程序,该应用程序在收到PayPal即时付款通知时应创建产品(如运输保险单)。不幸的是,PayPal有时会发送重复通知。此外,还有另一个第三方在从PayPal获得更新时同时执行Web服务更新。IsolationLevel.RepeatableRead以防止重复

下面是涉及的数据库表的基本图。

// table "package" 
// columns packageID, policyID, other data... 
// 
// table "insurancepolicy" 
// columns policyID, coverageAmount, other data... 

这里是什么,我想要做一个基本图:

using (SqlConnection conn = new SqlConnection(...)) 
{ 
    sqlTransaction sqlTrans = conn.BeginTransaction(IsolationLevel.RepeatableRead); 

    // Calls a stored procedure that checks if the foreign key in the transaction table has a value. 
    if (PackageDB.HasInsurancePolicy(packageID, conn)) 
    { 
    sqlTrans.Commit(); 
    return false; 
    } 

    // Insert row in foreign table. 
    int policyID = InsurancePolicyDB.Insert(coverageAmount, conn); 
    if (policyID <= 0) 
    { 
    sqlTrans.Rollback(); 
    return false; 
    } 

    // Assign foreign key to parent table. If this fails, roll back everything. 
    bool assigned = PackageDB.AssignPolicyID(packageID, policyID, conn); 
    if (!assigned) 
    { 
    sqlTrans.Rollback(); 
    return false; 
    } 
} 

如果有两个(或更多)的线程(或进程或应用程序)在同一时间做这个,我想第一个线程在没有policyID的情况下锁定“包”行,直到创建策略并将策略ID分配给包表。然后,在policyID分配给包表后,锁将被释放。我希望调用这个相同代码的另一个线程在读取包行时暂停,以确保它没有第一个policyID。当第一个事务的锁被释放时,我希望第二个事务会看到policyID在那里,因此返回时不插入任何行到策略表中。

注意:由于CRUD数据库设计的,每一个存储过程的参与设置读取(选择),创建(插入),或更新。

这是正确使用RepeatableRead事务隔离吗?

谢谢。

+0

什么是包装:政策关系?它是1:1吗? – Constantin 2008-09-23 23:43:37

+0

它是n:1。父表实际上不是“包”,我只是为了说明而称他们为“包”。实际上,这些“包裹”就像是可以组合并一起运输的订单,整个包裹只有一份保险单。 – devlord 2008-09-24 01:14:06

回答

1

这将是更安全,更清洁,如果insert into Policy只是打了一些独特表约束在试图重复插入。提高隔离级别会降低并发性并导致其他恶意问题,如死锁。

另一种方式是始终插入政策行,然后滚回来,如果包已经被安装到已经策略:

begin tran (read committed) 

/* tentatively insert new Policy */ 
insert Policy 

/* attach Package to Policy if it's still free */ 
update Package 
    set Package.policy_id = @policy_id 
    where Package.package_id = @package_id and Package.policy_id is null 

if @@rowcount > 0 
    commit 
else 
    rollback 

这个工作最好当冲突是罕见的,这似乎是你的情况。

1

我相信你实际上想要Serializable隔离级别。问题是两个线程可以通过HasInsurancePolicyCheck(尽管我不知道InsurancePolicyDB.Insert会执行什么操作或为什么会返回0)

您还有许多其他选项。一种是使用消息队列并自己处理这些请求。另一种方法是使用sp_getapplock并锁定该包所特有的某些密钥。这样你就不会再锁定更多的行或表。

+0

谢谢!该公司的其他人也在推荐一个专用队列。 – devlord 2008-09-24 06:56:35

0

我同意aaronjensen回复中的“消息队列”概念。如果您担心同时尝试更新同一行数据的多个并发线程,则应该让线程将其数据插入到工作队列中,然后由单个线程按顺序处理它们。这大大减少了对数据库的争用,因为目标表仅由一个线程而不是“N”更新,而且工作队列操作仅限于消息传递线程的插入以及数据处理线程的读取/更新。