繁体   English   中英

PHP Mysql-使用分组依据的交易

[英]PHP Mysql - transactions using group by

我正在尝试在PHP和mysql中创建银行系统。 现在我有一个事务表,其中包含诸如:

id (bigint) | account_id (int) | amount (decimal(10,2) SIGNED) | type (credit/debit) | created_on (timestamp)

因此,要获得总帐户余额,查询将类似于:

select sum(amount) from table where account_id = ? group by account_id order by created_on asc

我们需要有一个事务表来分解系统中的每个事务。 我只是不确定这是否是事实的唯一来源,尤其是考虑到需要进行存入/提取/发送资金的交易时

单个交易表可扩展吗? 提款会发生什么,我们需要获取余额,对照提款金额进行核对,然后插入交易表中,否则将失败?

只拥有两个表,一个用于交易,另一个用于account_balances,这样取款,我们只需要这样做会更容易:

UPDATE account SET balance = balance - ? WHERE id = ? AND balance >= ?

谢谢布莱恩

我只是不确定这是否是事实的唯一来源,

根据定义,关于任何给定事实,只有一个真理来源。

尝试创建第二个真理源意味着您没有真理源,因为这将二者都归结为仅可能产生不一致意见的源头。

用天平保持单独的桌子是微妙的领域。 容易做出天真的假设而忽略了这一点。

尽管如此,例如,有一张带有静态余额的表格可能是有意义的,以便您有一个轻量级的选项来获取客户的余额以显示在每页的顶部...但是必须处理此表格就像它是(或可能成为)不可信任的恶棍。

像这样的表特别容易出现新手错误,从而导致比赛条件和异常,例如将天平读取到变量中,以编程方式调整其值,并将新值写回到数据库中。 那根本不是你怎么做的。 但是,我已经看过它做过这么多次了(特别是对于那些仍然不了解没有一个好的ORM的人,因为ORM是一个本质上有缺陷的概念)。 这样的表应该由触发器维护,而不是由应用程序代码维护,并且更新应该是原子的。

您确实在示例中显示了原子更新...

UPDATE account SET balance = balance - ? WHERE id = ? AND balance >= ?

...尽管您需要格外小心,但在这里,因为如果balance >= ?它将成功更新0行balance >= ? 是不正确的……更糟糕的是,您代码中的第一个占位符值为null的错误, SET balance = balance - NULL会将balance设置为NULL,因为空操作数(正确)会导致大多数操作求和为空值。

如果您希望此表是一个优化表,那么安全的设计将始终通过在进行任何重要的操作(例如提款或转账)之前用交易中的计算结果审核余额来确保一致性,并且如果发现有出入,则公然拒绝继续进行,并解释向用户表明该站点“存在问题”并为您创建内部支持事件...因为这意味着您存在一个错误,该错误使余额表中的值与交易表有所不同,这表明非常不好的事情™。

当然,其中大部分是通过正确使用数据库事务,理解隔离级别以及在关键部分使用锁定读取来进行的。 如果仅通过事务表上的BEFORE INSERT触发器而不是您的代码来修改余额表,那么余额漂移的机会就很少。

进行健全性检查仍然很关键,尽管人们总是绞痛和挥舞手臂,但您还是会从那些坚持认为触发器会对性能造成过大惩罚的人那里得到帮助,因此应该避免……他们绝对不知道自己在说什么。 是的,从技术上讲,汽车油箱中的燃料质量对燃油经济性有负面影响,但这是操作的关键部分。 你什么都不要想。 您不会带着几乎是空的坦克四处行驶。 触发触发器的成本可以忽略不计,尤其是考虑到它们帮助数据库照顾自己的方式。 不要花时间去思考触发器。

但是,此类表的可伸缩性问题在很大程度上由适当的索引解决。 例如,交易表中(account_id,created_on)上的索引表示数据库可以立即找到一个特定帐户的所有交易,而无需按照扫描表的顺序进行排序,该交易已经按照发生的顺序进行了排序。 您应该发现,即使在亿万行中,使用适当的索引也可以快速找到给定帐户的交易。

SELECT sum(amount)表示对交易金额进行了签名,这意味着type列有些多余,尽管您可能希望保留它并使用触发条件来捕获它,该触发条件要求贷项> 0且借项<0。在数据库内部,无论看似多么明显/不必要,当涉及到金钱时,它都不是一个坏主意。

您可能还应该具有BEFORE DELETEBEFORE UPDATE触发器,以拒绝对事务表的任何更改。 交易是不会改变的历史事实。 通过创建第二个抵消交易可以正确撤消不正确的交易。

一个防止在MySQL Server 5.5及更高版本中的给定表上进行所有删除的简单示例看起来像thjs:

mysql> CREATE TRIGGER transaction_bd
       BEFORE DELETE ON transaction
       FOR EACH ROW
       SIGNAL SQLSTATE '45000'
       SET MESSAGE_TEXT = 'the transaction table does not support DELETE.';

(具有非复杂主体的触发器不需要通常的DELIMITER声明。)

我认为最好有两个单独的表分别用于入金(存款)和出金(提款)。 所有正数都输入,负数输出,然后分配所需的任何类型。 但是,这是一个体系结构问题,并且在不了解整个平台要求的情况下很难说出来。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM