电商对账系统

712 阅读3分钟

一、功能

账户系统主要负责记录和管理用户账户的钱。

二、 为什么总是对不上账?

“对不上账”的本质原因是“冗余数据的一致性问题”。
这里的冗余数据并不是多余或者重复的数据,而且多份还有相同信息的数据。例如,我们完全可以通过用户的每一笔充值交易数据、消费的数据流水,来计算出用户当前账户的余额,此时,账户余额数据和这些账户相关的交易记录,都含有账户余额这个数据,它们彼此互为冗余数据。
理论上来说冗余数据不仅浪费空间、还存在一致性问题,那么为什么这样设计呢?
因为余额这个数据在交易过程中频繁被使用,每次交易之前都通过所有的历史流水计算这个余额并不实际,所以账户系统保存了每个账户的余额,这个是典型的用存储空间换计算时间的设计。
在记录流水时️,有两个原则需要遵循:

  1. 流水记录只能新增,一旦记录成功不容许修改和删除。就算是正当原因要取消一笔已经完成的交易,也不应该删除流水,而是新增一笔“取消交易”的流水;
  2. 流水号必修是递增的,因为需要用流水号确定交易的先后顺序。
在对账时,一旦出现了流水和余额不一致的场景,并且无法通过业务员手段来确定到底是哪个流程出错了,一般的处理原则以交易流水为准来矫正数据,这样才能保证后续的交易能“对上账”。

三、数据库事务来保证数据一致性

Transaction在英文中是事务和交易的意思。
我们在设计对外接口时,不能单独提供更新余额或者增加流水的接口,而且提供一个统一的交易功能接口。这个交易功能接口实现中,统计记录流水并且修改余额,要保证在任何情况下,这两个操作要么都成功、要么都失败。
ACID: 原子性Atomic、一致性Consistency、隔离性Isolation、持久性Duration

隔离级别

  • READ-UNCOMMITTED:读未提交
    缺点:脏读;不可重复读;幻读;
  • READ-COMMITTED:读已提交
    缺点:不可重复读;幻读;
  • REPEATABLE-READ:可重复读
    缺点:幻读;
  • SERIALIZABLE:幻读

四、业务流程

  1. 在账户余额表增加一个log_id字段,记录最后一笔交易的流水号;
  2. 首先开始事务,查询并记录当前账户的余额和最后一笔交易的流水号;
  3. 然后写入流水记录;
  4. 再更新账户余额,需要在更新语句的WHERE条件中限定,只有流水号等于之前查出来的流水号才更新;
  5. 然后检查更新余额的返回值,如果更新成功就提交事务,否则回滚事务。

注意点:更新账户余额后,不能只检查更新语句是不是执行成功了,还需要检查返回值中变更的行数是不是1。因为即使流水号不相等,余额没有更新,这条更新语句仍然是成功的,只不过更新了0条记录。