你有没有想过为什么交易和退款要拆开不同的表

响应式编程
• 阅读 3320
前言

近期做新项目,在设计表结构的时候,突然想起来之前面试的时候遇到的一个问题,那时候也是初出茅庐,对很多东西一知半解(当然现在也是),当时那个小哥哥问我为什么交易和退款要拆成两个表?是基于什么考虑?有什么好处和优点么?

背景

那是一个风和日丽的下午,当然,风和日丽的下午应该配点其他的形容词,实在是我才疏学浅,只能用这个词充当了下开头…… (此处省略小五千字)

赶紧进入正文!

因为之前一直做聚合支付,而在使用过程中,也是支付和退款表拆开的,一直这么用,并没有觉得不妥。

比如一个交易表基本就是这样的:

字段类型含义
idbigint主键 id
trans_idvarchar交易订单号
trans_amountbigint订单金额
trans_statustinyint交易状态
………………
create_timedatetime创建时间
update_timedatetime更新时间

退款表也差不多就是这样:

字段类型含义
idbigint主键 id
refund_idvarchar退款订单号
origin_trans_idvarchar原始交易订单号
refund_statustinyint退款状态
refund_amountbigint退款金额
………………
create_timedatetime创建时间
update_timedatetime更新时间

大概两个表就是这样子的吧!像一些其他字段就先省略了,平常用着也觉得没什么。

但是恰好那次那个小哥哥就问了这个问题,支付和退款为什么要分开记录?

当时也是确实是实力不允许,我只是说了就是这么用的,把正向流程和逆向流程拆开,分开实现逻辑,比较方便。

个人见解

这里说的不仅仅是交易和退款,同时泛指正向交易和逆向交易,比如充值和消费,借款和贷款,账户出账入账等等,下面仅说说个人见解,只做讨论,如果小伙伴有更好的说法,希望可以留言指出,共同学习。

对账需要

对账户而言,出款表和入款表最后两方的金额是能对的上的,也就是说收支平衡

当然这个记在一个表里也是完全可以的。毕竟对出入账只是流水没有状态变化,比如出账中,入账中,等等,流水表完全可以记在一个里面,然后用字段进行标识是出账还是入账。

拆表需要

在网上看资料经常会说分库分表,而像订单这种(交易/退款)完全两种业务,使用两张表相对而言比较合适,毕竟交易的订单相比退款订单要多的多。

字段设计

交易和退款是完全不同的两种业务,不像账户流水就是资金记录。

交易除了订单状态还有一些交易信息比如商户号、优惠金额、实付金额、交易渠道、商品 id 名称、备注等各种信息。

退款则是根据原单进行退款,需要记录原始订单号、退款金额(部分退款)、退款信息等。

虽然交易和退款总体上都包含 订单号、状态、金额等,但是如果强行放在一个表,就会导致以下问题:

  1. 很多字段为空的情况,比如交易不需要原始订单号,退款需要存储原始订单号。本来可以设置索引来提高查询效率的字段也不太合适设置了。
  2. 状态也不一定可以完全兼容,像交易状态和退款状态就很难互相兼容。

开发效率

交易和退款分开之后,两个人负责不同的业务进行开发,包括业务逻辑和查询展示。如果放在一起,就很多字段不能保证别人知道有还是没有,是存储还是不存储,毕竟表里设置的都可以为空。这种情况下需要很多沟通,或者干脆一个人进行开发。

设计模式及原则

其他从设计模式及原则的角度上来说,可以说是职责单一,当然再高大上偏理论的我这就扯不出来了。

总结

Q&A

Q: 那前端要将两种甚至多种在一个列表展示该如何处理?

A: 在很多 APP 中大家看到的多种订单都是在一个列表里面展示出来的,比如:支付宝的账单页面。

当然,如果前端分 tab 页,分开展示不同的业务,那对后端来说简直不要太友好。不过实际往往不是这样,这时候就需要将订单统一存储。

你有没有想过为什么交易和退款要拆开不同的表

在订单成功的时候存储到一个公共存储中,可以通过 MQ 等,将数据保送到另一张表/库,或者 ES 中用来存储。这样订单查询还可以和业务逻辑的表/库分开。也可以通过 binlog 进行处理,这里的方案只做参考。

结束语

之所以写这篇文章,也是为了总结一下最近工作中遇到的问题,以及处理方法。同时一瞬间想起来了很久前遇到的相同的问题。

如果小伙伴们还有别的看法,欢迎留言,发表自己的意见及看法,共同讨论。

点赞
收藏
评论区
推荐文章
blmius blmius
3年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
美凌格栋栋酱 美凌格栋栋酱
6个月前
Oracle 分组与拼接字符串同时使用
SELECTT.,ROWNUMIDFROM(SELECTT.EMPLID,T.NAME,T.BU,T.REALDEPART,T.FORMATDATE,SUM(T.S0)S0,MAX(UPDATETIME)CREATETIME,LISTAGG(TOCHAR(
Easter79 Easter79
3年前
springboot2结合mybatis拦截器实现主键自动生成
前言前阵子和朋友聊天,他说他们项目有个需求,要实现主键自动生成,不想每次新增的时候,都手动设置主键。于是我就问他,那你们数据库表设置主键自动递增不就得了。他的回答是他们项目目前的id都是采用雪花算法来生成,因此为了项目稳定性,不会切换id的生成方式。朋友问我有没有什么实现思路,他们公司的orm框架是mybatis,我就建议他说,不然让你老大把m
Wesley13 Wesley13
3年前
MQ消息中间件,面试能问些什么?
MQ消息中间件,面试能问些什么?为什么使用消息队列?消息队列的优点和缺点?kafka、activemq、rabbitmq、rocketmq都有什么优缺点?面试官角度分析:(1)你知不知道你们系统里为什么要用消息队列这个东西?(2)既然用了消息队列这个东西,你知不知道用了有什么好处?(3
Wesley13 Wesley13
3年前
mysql设置时区
mysql设置时区mysql\_query("SETtime\_zone'8:00'")ordie('时区设置失败,请联系管理员!');中国在东8区所以加8方法二:selectcount(user\_id)asdevice,CONVERT\_TZ(FROM\_UNIXTIME(reg\_time),'08:00','0
Wesley13 Wesley13
3年前
NEO从源码分析看UTXO交易
_0x00前言_社区大佬:“交易是操作区块链的唯一方式。”_0x01交易类型_在NEO中,几乎除了共识之外的所有的对区块链的操作都是一种“交易”,甚至在“交易”面前,合约都只是一个小弟。交易类型的定义在Core中的TransactionType中:源码位置:neo/Core/TransactionType
Wesley13 Wesley13
3年前
初探 Objective
作者:Cyandev,iOS和MacOS开发者,目前就职于字节跳动0x00前言异常处理是许多高级语言都具有的特性,它可以直接中断当前函数并将控制权转交给能够处理异常的函数。不同语言在异常处理的实现上各不相同,本文主要来分析一下ObjectiveC和C这两个语言。为什么要把ObjectiveC和
Wesley13 Wesley13
3年前
ThinkPHP 根据关联数据查询 hasWhere 的使用实例
很多时候,模型关联后需要根据关联的模型做查询。场景:广告表(ad),广告类型表(ad\_type),现在需要筛选出广告类型表中id字段为1且广告表中status为1的列表先看关联的设置部分 publicfunctionadType(){return$thisbelongsTo('A
Wesley13 Wesley13
3年前
Java集合及concurrent并发包总结(转)
<divid"cnblogs\_post\_body"class"blogpostbody"<p<strong1.集合包</strong</p<p&nbsp;&nbsp;集合包最常用的有Collection和Map两个接口的实现类,Colleciton用于存放多个单对象,Map用于存放KeyValue形式的键值对。</p<p
为什么mysql不推荐使用雪花ID作为主键
作者:毛辰飞背景在mysql中设计表的时候,mysql官方推荐不要使用uuid或者不连续不重复的雪花id(long形且唯一),而是推荐连续自增的主键id,官方的推荐是auto_increment,那么为什么不建议采用uuid,使用uuid究
线上SQL超时场景分析-MySQL超时之间隙锁 | 京东物流技术团队
前言之前遇到过一个由MySQL间隙锁引发线上sql执行超时的场景,记录一下。背景说明分布式事务消息表:业务上使用消息表的方式,依赖本地事务,实现了一套分布式事务方案消息表名:mqmessages数据量:3000多万索引:createtime和statuss