我有一个简单的电力系统,用户可以相互借用:
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。这意味着用户将被授予几乎无限的权力(信用)。
首先,不要担心这个:
在运行上述一组查询之前,我通过SELECT查询检查发件人是否有足够的信用可以转账。
这会让你面临一种无论如何都必须应对的种族状况。相反,在数据库中设置约束,使其不可能进入无效状态。
第2期:电源为
unsigned int
。
也不要那样做,没有必要。一个4字节的带符号整数在正侧应该有足够的空间;如果没有,您可以切换到有符号的8字节整数。你想要一个有符号的值的原因是它使完整性检查变得相当容易:如果余额降到零以下,那么就有问题了。如果使用无符号值,则必须保留4294967295-n
(对于某些n
)来检测下溢和上溢,而不是简单的< 0
。
就约束你的数据而言,通常你会使用这样的CHECK约束:
check (power >= 0)
但是MySQL不支持CHECK约束。但是,您可以编写一个BEFORE INSERT或UPDATE触发器,该触发器可以检查new.power >= 0
,如果不是,则引发异常。
现在您有了一个users
表,它不允许无效的power
值,所以我们完成了问题2。
关于问题1:交易。是的,您确实希望使用交易进行转账。你会想要这样的序列:
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约束(作为触发器实现)确保没有人可以放弃比他们拥有的更多的权力。