我正在寻找解决方案:
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 :(得分:1)
注意:这个答案假设您是InnoDB,它允许行级锁定而不是需要表锁的MyISAM。
对于这样的情况,您将使用事务和READ / WRITE锁。您需要的确切详细信息因具体情况而异,如果不了解您的架构以及您担心的数据完整性,我无法回答这些问题,因此我将向您提供一般性解释。
可以在您不打算写入的行上获取读锁定,但在交易期间不得更改。可以在将来某个时候要更改的行上获取写锁定。事务是一系列多个操作,以全有或全无的方式应用于数据库。
举个例子,我们假设如下:
你会做这样的事情:
// 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;
}
还应该注意的是,您现在必须担心死锁,但这是一个完全独立的主题。