如何处理并发风险任务?

时间:2014-05-22 15:15:38

标签: mysql concurrency innodb myisam

我正在寻找解决方案:

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解决方案感兴趣

1 个答案:

答案 0 :(得分:1)

注意:这个答案假设您是InnoDB,它允许行级锁定而不是需要表锁的MyISAM。

对于这样的情况,您将使用事务和READ / WRITE锁。您需要的确切详细信息因具体情况而异,如果不了解您的架构以及您担心的数据完整性,我无法回答这些问题,因此我将向您提供一般性解释。

可以在您不打算写入的行上获取读锁定,但在交易期间不得更改。可以在将来某个时候要更改的行上获取写锁定。事务是一系列多个操作,以全有或全无的方式应用于数据库。

举个例子,我们假设如下:

  • 你有3个表:table_A,table_B,table_C
  • 客户端1正在执行的操作对table_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;
}

还应该注意的是,您现在必须担心死锁,但这是一个完全独立的主题。