我有一个简单的电力系统,因为用户可以互相借用:
mysql_query("INSERT INTO power (sender, receiver, amount)
VALUES ('$sender', '$receiver', '$amount')");
mysql_query("UPDATE users SET power=power-$amount WHERE user_id='$sender'");
mysql_query("UPDATE users SET power=power+$amount WHERE user_id='$receiver'");
在运行上述查询集之前,我检查发件人是否有足够的信用额度通过SELECT
查询进行转移。
问题1:我认为,最好将所有这四个查询放入Transaction
;因为InnoDB ACID将保证更安全的性能。这样做的最佳交易是什么?
问题2:权力为unsigned int
。如果有任何机会(甚至不太可能),用户没有足够的信用power-$amount
将不会设置0;相反,它将循环通过int()的最大值,即4294967295.这意味着用户将被授予几乎无限的权力(信用)。
答案 0 :(得分:3)
首先,不要为此烦恼:
在运行上述查询集之前,我检查发件人是否有足够的信用来通过SELECT查询进行传输。
这让你对竞争条件持开放态度,无论如何你都要处理。相反,在数据库中设置约束以使其无法进入无效状态。
第2期:权力为
unsigned int
。
也不要这样做,没有必要。 4字节有符号整数应该在正侧有足够的空间;如果没有,您可以切换到带符号的8字节整数。您需要签名值的原因是它使完整性检查相当容易:如果余额低于零,则出现问题。如果您使用无符号值,则必须保留4294967295-n
(对于某些n
)以检测下溢和溢出而不是简单的< 0
。
就限制数据而言,通常你会使用像这样的CHECK约束:
check (power >= 0)
但MySQL不支持CHECK约束。但是,您可以编写一个BEFORE INSERT或UPDATE触发器,可以检查new.power >= 0
和raise an exception(如果不是)。
现在您有一个users
表格,不允许使用无效的power
值,因此我们已完成问题2.
开始问题1:交易。是的,您绝对希望使用transaction进行转移。你需要一个像这样的序列:
start transaction
INSERT INTO power (sender, receiver, amount) VALUES ('$sender', '$receiver', '$amount')
UPDATE users SET power=power-$amount WHERE user_id='$sender'
UPDATE users SET power=power+$amount WHERE user_id='$receiver'
rollback
并告诉用户出了什么问题。commit
发送到数据库。事务将确保所有三个操作作为单个单元成功或失败,并且CHECK约束(实现为触发器)确保没有人可以提供比他们更多的权力。