需求是: 用户参与活动报名,缴报名费,每次有人报名后,活动总共收到的报名费就要统计出来。 方法 1: update table set fee = fee + 10
方法 2: $fee = "select sum(fee) from pay_log where ..." update table set fee = $fee
大家都用什么方法保证不出错? 方法 1 和方法 2 是不是都必须加上事务?
![]() | 1 libook 2020-04-22 16:25:51 +08:00 最精确的肯定是每次从数据库里求和,但要看你量级多大,会不会导致性能问题。 还有就是总额是展示给用户看的还是展示给老板看的,一般展示给用户看都不是真实的数量或金额。 |
![]() | 2 keepeye 2020-04-22 16:40:01 +08:00 用方法 1 ,和 pay_log 插入语句放到一个事务里 方法 2 怕并发 |
![]() | 3 baiyuxiong OP @keepeye 方法 1 总感觉可靠性低,担心算错。 |
![]() | 4 baiyuxiong OP @libook 如果量级大就选择方法 1 ? |
![]() | 5 keepeye 2020-04-22 17:02:56 +08:00 为什么担心算错? |
![]() | 6 hua123s 2020-04-22 17:03:01 +08:00 via iPhone 在可重复读隔离级别下,update 是当前读,所以 1 和 2 是等价。我刚刚百度的,不知道对不对 |
![]() | 7 |
8 mwiker 2020-04-22 17:09:54 +08:00 没缴纳时间吗? |
![]() | 9 libook 2020-04-22 18:11:37 +08:00 @baiyuxiong 并不是,只是说量大的话方案 2 性能会差,届时可能需要一些更复杂的方案来保障较强一致性的同时也能保障较高的可用性。 举个例子: 可以做两层,上层是快照层,用 Redis 等内存数据库来存当前的总额,又增加则在这个数字上直接累加;下层是持久层,记录每一条交易。 服务读取总额的时候只从快照层读取当前总额,然后可以设置一个对持久层性能影响较小的刷新周期,比如 10 秒,每个刷新周期从持久层计算总额然后覆盖到快照层。当然这个方案还要考虑业务级别的事务,比如对两层进行写操作需要有原子性,要么都写成功,要么都失败。 还可以更复杂,当然一切都是看实际情况,如果你量不大,没必要搞这么复杂,就每次求和就行了。 |
![]() | 10 wxF2BByt634kiAN1 2020-04-22 22:55:10 +08:00 via Android 这种东西用户也不知道,有必要实时更新么。。 |
![]() | 11 CStarter 2020-04-22 22:55:19 +08:00 update table set fee = fee + 10 where fee = 原值 一定要在更新 fee 时,校验 fee 是否为原值,否则可能出现并发问题。 |
![]() | 12 baiyuxiong OP @libook 666 这个方案感觉不错 |