当我收到PayPal即时付款通知时,我正在开发一个应该创建产品(如运输保险单)的应用程序。不幸的是,PayPal有时会发送重复的通知。此外,还有另一个第三方在从PayPal获取更新时同时执行Web服务更新。
这是所涉及的数据库表的基本图表。
// 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的情况下锁定“package”行,直到创建策略并且policyID已分配给包表。然后在将policyID分配给包表后释放锁。我希望调用相同代码的另一个线程在读取包行时会暂停,以确保它首先没有policyID。当第一个事务的锁被释放时,我希望第二个事务会看到policyID存在,因此返回时不会在策略表中插入任何行。
注意:由于CRUD数据库设计,每个存储过程都涉及读取(选择),创建(插入)或更新。
这是否正确使用了RepeatableRead事务隔离?
感谢。
答案 0 :(得分:1)
我相信你真的想要Serializable隔离级别。问题是两个线程可以通过HasInsurancePolicyCheck(虽然我不知道InsurancePolicyDB.Insert会做什么或为什么它会返回0)
您还有许多其他选择。一个是使用消息队列并自行连续处理这些请求。另一种方法是使用sp_getapplock并锁定该包独有的一些密钥。这样你就不会再锁定任何行或表了。
答案 1 :(得分:1)
如果insert into Policy
在尝试插入重复时遇到一些唯一性表约束,那将更安全,更清晰。提高隔离级别可以降低并发性并导致其他令人讨厌的问题,如死锁。
另一种方法是始终插入Policy行,如果Package已经附加到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
这种情况在冲突很少发生时效果最好,这似乎是你的情况。
答案 2 :(得分:0)
我同意aaronjensen的回应中的“消息队列”的想法。如果您担心多个并发线程试图同时更新同一行数据,则应该让线程将其数据插入到工作队列中,然后由单个线程按顺序处理。这大大减少了对数据库的争用,因为目标表只由一个线程而不是“N”更新,工作队列操作仅限于消息传递线程的插入,以及数据处理线程的读取/更新。 / p>