2014-05-22 59 views
0

我正在尋找一個解決方案:如何處理併發風險任務?

function foo() 
{ 
    client 1 executes an update/delete 
    // client 2 calls foo() and breaks data integrity 
    client 1 executes an update/delete 
} 

我該如何解決這個與MySQL?我使用的是myisam表,但我對innodb的解決方案也很感興趣。

+0

如果您可以提供一些架構和您擔心的數據完整性的示例,這將會很有幫助。這允許推薦更多更具體的解決方案。 –

+0

查詢1驗證父母身份證,查詢2引用父母身份證在另一行...之間的兩個第二任務刪除父母身份證的行... – sveva

回答

1

注意:這個答案假設你是InnoDB,它允許行級鎖定,而不是需要表鎖的MyISAM。

對於這樣的情況,您可以使用事務和READ/WRITE鎖。您需要的具體細節因案例而異,如果不知道您的模式和您擔心的數據完整性,我無法回答,所以我會給您一個一般的解釋。

可以在您不打算寫入的行上獲取讀取鎖定,但在您的事務處理期間不得更改。可以在您打算在未來的某個時間點更改的行上獲取寫入鎖定。事務是一系列多個操作,以全有或全無的方式應用於數據庫。

因此,作爲一個例子可以假設以下幾點:

  • 你有3個表:表-A,表-B,table_C
  • 操作的客戶端1進行使一個更新到表-A,然後 表-B。
  • 客戶端2可能正在更新表中的任何表。
  • 您需要所有3個表格之間的數據一致性。

你會做這樣的事情:

// This makes it so that each operation is not automatically commited (saved) 
// It implicitly makes all sequences of operations into transactions 
execute("set autocommit=0"); 
// This gets you some data from table_B and also gets a read lock to prevent that data from changing 
result = execute("SELECT * FROM `table_B` WHERE `condition` = 1 LOCK IN SHARE MODE"); 
// This gets some data from table_C and gets a write lock to prevent the data from changing and allowing you to write to it in the future 
result2 = execute("SELECT * FROM `table_C` WHERE `condition` = 1 FOR UPDATE"); 
// This performs your update to table_A 
execute("UPDATE `table_A` SET `value` = 1234 WHERE `condition` = 1"); 
// This performs your update to table_C 
execute("UPDATE `table_C` SET `value` = 4321 WHERE `condition` = 1"); 
// This saves all of the changes that you made during your transaction and releases all locks 
// Note: autocommit is still turned off 
execute("COMMIT"); 

所以讓有涉及購買一些更具體的例子。我意識到這可以通過一個更新語句完成,但我正在通過這種方式來說明如何使用事務。

我的表是:

items (id int not null primary key, user_id int not null, item_type int not null) 
accounts (user_id int not null primary key, balance int not null) 
prices (item_type int not null primary key, price int not null) 
limits (item_type int not null primary key, max_count int not null) 

注意,我要跳過爲簡潔起見輸入環境衛生,不這樣做,真的。 (http://xkcd.com/327/

function purchase(user_id, item_type) { 
    execute("set autocommit=0"); 
    // I am assuming that price and max_count can be changed but they require consistency with each other hence the read locks 
    var price = execute("SELECT `price` FROM `prices` WHERE `item_type` = " + item_type + " LOCK IN SHARE MODE")[0].price; 
    var max_count = execute("SELECT `max_count` FROM `limits` WHERE `item_type` = " + item_type + " LOCK IN SHARE MODE")[0].max_count; 
    // I need the write lock to prevent double spending 
    var account = execute("SELECT * FROM `accounts` WHERE `user_id` = " + user_id + " FOR UPDATE")[0]; 
    // I need to guarantee that the user is not over the limit 
    var count = execute("SELECT count(*) AS `count` FROM `items` WHERE `user_id` = " + user_id + " FOR UPDATE")[0].count; 
    var new_balance = account.balance - price; 
    if(count >= max_count || new_balance < 0) { 
     return false; 
    } 
    execute("INSERT INTO `items` (`user_id`, `item_type`) VALUES (" + user_id + ", " + item_type + ")"); 
    execute("UPDATE `accounts` SET `balance` = " + new_balance + " WHERE `user_id` = " + user_id); 
    execute("COMMIT"); 
    return true; 
} 

還應該注意的是,你現在不用擔心死鎖,但是這是一個完全獨立的主題。

+0

嗨,優秀的介紹!如何鎖定myisam? – sveva

+0

我不使用MyISAM,並且不會很舒服的評論。 –

+0

我的項目經理說innodb幾乎不可能在windows上使用(buggy和crashy),所以我堅持使用myisam :-( – sveva