2008-09-23 80 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」更新,而且工作隊列操作僅限於消息傳遞線程的插入以及數據處理線程的讀取/更新。