2012-12-14 13 views
1

假設我有一盤棋存儲在SQL數據庫的東西,如下面的模式:如何防止多個同時SQL更新一盤棋

CREATE TABLE chessgames(
    game_id INTEGER, 
    move_id INTEGER, 
    move char(4) 
}; 

因此,如果正在進行的遊戲,用game_id 0,具有e4 e5的移動,那麼該表將具有元組(0,1,「e4」)和(0,2,「e5」)。

現在假設客戶端試圖通過同時發送移動d4和Nf3來破壞數據庫。試圖獲得兩個移動處理並有效地嘗試插入元組(0,3,「d4」)和(0,3,「Nf3」),兩者都具有相同的move_id,從而打破了move_id的唯一性。

什麼是確保唯一性的最佳慣用方法?發生在我身上的一種可能性是讓我的C++代碼包含一個互斥列表,每個遊戲一個互斥量。當一招如D4到達時,C++代碼鎖定了相應的遊戲互斥,運行下面的SQL查詢

SELECT move_id, move FROM chessgames WHERE game_id = 0 

獲取所有的遊戲動作(在這個例子中我給了這將是E4和e5),C++代碼將執行這些移動並檢查沒有行已經有move_id = 3,然後播放移動以構建當前位置,以便它可以檢查移動d4是否有效。如果它是有效的,則運行

INSERT INTO chessgames VALUES (0, 3, "d4") 

將移動存儲在數據庫中,然後釋放該互斥體。

這樣,如果Nf3移動到達的同時它的d4移動處理將被鎖定的互斥鎖阻擋,並且當最終處理Nf3時,它將看到move_id = 3的行已經存在,它會被忽略。

有沒有更好的方法來做到這一點?我的數據庫模式對於我想要做的事情是否合理?

+0

如果客戶端總是按順序發送移動,即(0,1,「e4」),(0,2,「e3」),(0,3,「b4」),那麼對你來說不是更好控制'move_id',只允許'Game_id'和'Move'作爲客戶端的輸入。這樣,你可以在移動時自動增加'move_id'。 – DMK

+0

對不起,如果我不清楚,隱含我的意思是客戶端不會發送'move_id',只發送'Game_id'和'Move' 。但是我沒有看到,如果服務器應該運行多線程並且可能同時處理多個輸入,那麼解決方法是如何正確地增加'move_id'。 – user782220

回答

0

我知道我需要的是樂觀鎖定,就像這個問題的答案很像 question

2

首先,您應該在列game_id和move_id上​​創建一個UNIQUE索引。 這樣,您可以確保數據庫級別上每個遊戲的move_id唯一性。

CREATE UNIQUE INDEX U_game_move 
ON chessgames 
(game_id, move_id); 

然後,一個互斥體的方法是好的,但你也可以考慮創建一個處理move_id增量的觸發器。

+0

據我所知,每次需要閱讀整個遊戲以創建當前位置,並驗證正在處理的移動是爲了當前側移動並且移動是有效的。如果沒有互斥鎖,我該怎麼做,而只是觸發器?你能提供具體的細節嗎? – user782220