2015-03-25 62 views
2

我有一箇中央數據庫用於處理用戶信用與多個服務器讀取和寫入它。該應用程序坐落在這些服務器之上做每個請求的下列服務用戶請求:樂觀鎖定用戶信用管理

 
1. check if user has enough credit for the task by reading from db. 
2. perform the time consuming request 
3. deduct a credit from user account, save the new credit count back to db. 

應用程序使用的數據庫的樂觀鎖定。所以,以下可能發生

 
1. request a comes in, see that user x has enough credit, 
2. request b comes in, see that user x has enough credit, 
3. a performs work 
4. a saves the new credit count back to db 
5. b performs work 
6. b tries to save the new credit count back to db, application gets an exception and fails to account for this credit deduction. 

有了悲觀鎖定,應用程序將需要顯式地獲取鎖定的用戶帳戶,以保證獨佔訪問,但由於該系統有許多併發請求該殺死的性能。
那麼這個信用系統會是一個很好的新設計?

+0

「執行耗時的請求」涉及到數據庫活動嗎?如果是這樣,是否需要將需要撤消的事情更改爲數據庫? – 2015-03-30 04:16:28

+0

@Rick James:不,「執行耗時的請求不執行數據庫活動」 – user121196 2015-03-31 01:56:47

回答

1

這裏有兩個「鎖定」使用InnoDB的鎖定機制的兩個原因之一,在避免機制:

  • 那需要更長的時間應該比你在InnoDB中的BEGIN...COMMIT花的任務。
  • ,在不同的程序(或不同的網頁),比它在開始結束任務。

計劃A.(這是假設的競爭條件是罕見的,浪費了第2步的時間)

  1. (同樣)通過從db中讀取來檢查用戶是否有足夠的功勞。
  2. (相同)執行耗時請求
  3. (添加)START TRANSACTION;
  4. (添加)再次檢查用戶是否具有足夠的信用額度。 (ROLLABCK,如果沒有,則中止)
  5. (與舊#3相同)從用戶帳戶扣除信用額度,將新的信用計數保存回db。
  6. (添加)COMMIT;

START..COMMIT是InnoDB的交易的東西。如果比賽條件引起的「X」由第4步,沒有信用,你會ROLLBACK,而不是執行步驟4和5

B計劃。(這是比較複雜的,但你可能會喜歡它。)

  1. 有一個表Locks鎖定。它包含user_id和一個時間戳。
  2. START TRANSACTION;
  3. 如果user_id在Locks中,則中止(ROLLBACK並退出)。
  4. INSERT INTO LocksLocks中的user_id和current_timestamp(從而「鎖定」'x')。
  5. COMMIT;
  6. 執行的處理(步驟原1,2,3)
  7. DELETE FROM Locks WHERE user_id = 'x';autocommit=1足夠這裏。)

一個潛在的問題:如果處理在步驟6中死去,沒有開始釋放鎖,該用戶將永遠被鎖定。 '解決方案'是定期檢查Locks是否有'非常'的時間戳。如果找到任何內容,則假定處理已終止,並刪除行。

+1

計劃A與OP的原始流程沒有區別,因爲您應該像在步驟(6)中那樣在步驟(4)中引發相同的異常。 – saaj 2015-03-31 13:00:46

1

你沒有明確說明你想達到什麼,所以我假設你不想執行這項工作只是意識到由於信用不足而一直徒勞無功。

無鎖

落實步驟(1)信用凍結和工作(2),並扣除(3)保持關聯。這種方式低信用戶不會通過步驟(1)。

樂觀鎖

如樂觀鎖定後呈文檢測到衝突,我不認爲它適合的假設。

悲觀鎖定

這是不可能肯定的告訴不知道的模式,但我認爲這是關於查殺性能誇張。您可以更精細地整合MySQL InnoDB transaction isolation levelslocking reads,而不是完全鎖定用戶帳戶。例如,使用SELECT ... LOCK IN SHARE MODE設置共享鎖並允許讀取其他事務。

瑞克對任務的謹慎需要更長時間,MySQL將等待(innodb_lock_wait_timeout)適用於此處。

1

你想要The Escrow Transactional Method

您在每次更新過程中都發放一些信用卡,並將信用卡分發給(即,託管)它們。一個流程會重試,直到成功完成一項交易,增加信貸因需求而減少,並減少所需信用;只有這樣才能使信用保持非負面的成功。然後它做了長時間的計算。無論計算成功與否,它都會應用一項減少信用額度的交易。但是,如果成功,它也會增加資產,而在失敗時會增加剩餘的資金。

+0

請記住,您必須處理服務器在信用卡託管期間死亡的情況。也就是說,它無法將其從託管中解除。 – 2015-04-02 05:28:45

1

使用timestamp/rowversion方法,您可以在除MySQL之外的所有實際數據庫引擎中找到該方法。

你可以用這種方式模仿它們。有一個TIMESTAMP列(更新),每當更新行時都會更新。選擇該列以及所需的其他數據。使用返回的時間戳記作爲WHERE子句中的條件,以便只在時間戳記與讀取行時相同時才更新該行。

UPDATE table SET col1 = value WHERE id = 1 AND updated = timestamp_value_read 

現在,當您運行更新和時間戳不匹配時,將不會執行更新。你可以通過使用受影響的行來測試,如果更新了零行,那麼你知道行在讀和寫之間被修改了。處理您的代碼中的條件,無論哪種方式最適合您的應用程序和用戶。

Timestamp tutorial

+0

由於saaj對另一個答案進行了評論,這與OP的原始流程沒有什麼不同,因爲當另一個流程修改相同的信用額時,您會浪費中間工作。 – philipxy 2015-04-05 01:32:51

+0

不,你沒有,因爲沒有其他進程可以改變信用:這就是讀/寫鎖的功能。事實上,所有其他的賽事都必須等待鎖定才能讀取數值,以免發生比賽情況。 – 2015-04-05 09:33:25