用于在用户之间转移信用的Mysql交易


Mysql transaction for transferring credit between users

我有一个简单的电力系统,用户可以相互借用:

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:交易。是的,您确实希望使用交易进行转账。你会想要这样的序列:

  1. start transaction
  2. INSERT INTO power (sender, receiver, amount) VALUES ('$sender', '$receiver', '$amount')
  3. UPDATE users SET power=power-$amount WHERE user_id='$sender'
  4. UPDATE users SET power=power+$amount WHERE user_id='$receiver'
  5. "检查触发器"是否有任何错误?
    • 如果有,则向数据库发送rollback,并告诉用户出了什么问题
    • 如果还没有,则向数据库发送commit

该事务将确保所有三个操作作为一个单元要么成功要么失败,CHECK约束(作为触发器实现)确保没有人可以放弃比他们拥有的更多的权力。