数据库设计
当前表 历史表 清单表 流水表 日志表 控制表 工单表
历史表
我们设计一个数据表的时候,常常只设计表本身,最多对请求记录一条日志。导致实际生产过程中出了点什么bug,查数据流查到shi,有任何一点问题,都要一点点翻代码,代价高,效率低。这就是因为我们我们在进行数据表设计的时候,只在乎实现功能,对数据不够重试,对数据的流转过程并没有记录下来。这个时候就需要我们的--历史表--闪亮登场了。
举例:当前表 customers
{
"_id": {
"type": "string",
"comment": "主键"
},
"key1": {
"type": "string",
"comment": ""
},
"key2": {
"type": "integer",
"comment": ""
},
"operator_id": {
"type": "string",
"comment": "操作员"
},
"operate_type": {
"type": "integer",
"comment": "操作类型, 增:1, 删:2, 改:3"
},
"create_time": {
"type": "date",
"comment": "创建时间"
},
"update_time": {
"type": "date",
"comment": "修改时间"
}
}
其中_id为主键,key1和key2为表的字段,operator_id, operate_type, create_time, update_time为必须要有的字段内容,对于当前数据的操作员,操作类型,创建时间和修改时间是当前表中重要的内容,对于生产环境中出的操作问题或者是bug可以起到快速追溯的作用。
而一个正常的当前表,必然有历史表,一般在当前表的后缀添加_his(即history的缩写),customers_his,历史表与当前表的字段一模一样,一旦当前表出现增删改操作,历史表中增加一条数据,并且有对应的操作类型(tips: 如果是删出操作,当前表中没有,历史表中有删出的操作类型)。
清单表
当某一个功能,产生重复相似的数据(例如通话记录),那么我们就需要一个清单表,将所有可能产生的数据作保存,清单表只作增加和转移,不作删出和修改。
举例:concact_list
{
"_id": {
"type": "string",
"comment": "主键"
},
"key1": {
"type": "string",
"comment": ""
},
"key2": {
"type": "string",
"comment": ""
},
"operator_id": {
"type": "string",
"comment": "操作员"
},
"create_time": {
"type": "date",
"comment": "创建时间"
}
}
清单表中没有操作类型和更新时间,因为清单表只作增加,没有修改操作,也不允许删除(tips:仅在表格数据量压力过大时将数据迁移出清单表)
流水表
当某一数据,经常修改,当并不是以数据列表的形式存在,而是某关联数据的某个状态的时候,这样的数据就不需要采用 当前表 + 历史表 的组合,可以用流水表的形式来体现。
举例: qc_srl
{
"_id": {
"type": "string",
"comment": "主键"
},
"key1": {
"type": "string",
"comment": ""
},
"key2": {
"type": "string",
"comment": ""
},
"operator_id": {
"type": "string",
"comment": "操作员"
},
"create_time": {
"type": "date",
"comment": "创建时间"
}
}
流水表也不需要操作类型和更新时间,因为流水表也只作增加。获取状态的时候,只要获取最近的创建时间对应的数据即可,而且不会流失操作过程,具有可查性。
日志表
日志表其实是清单表的另一种体现形式,记录的是操作日志,例如前端每发送一个请求就记录一条日志,含入参,后端每发出一个返回也记录一条日志,含出参。像这种可以概括的形式,应该封装成模块,发送请求和返回时直接调用即可。
除了入参和出参,也可以自由地在程序当中觉得需要记录日志的节点记录日志。而且日志量可能巨大,因此建议日志系统单独做,不与应用系统共同存在,这样可以减少由于日志表过多的数据量拖慢应用系统的性能。
正因为已上原因,日志表于清单表又有一些不一样的字段。
举例: logs
{
"_id": {
"type": "string",
"comment": "主键"
},
"operator_id": {
"type": "string",
"comment": "操作员"
},
"method": {
"type": "integer",
"comment": "方法:GET:1, POST:2, PATCH:3, DELETE:4, PUT:5"
},
"ip": {
"type": "string",
"comment": "IP"
},
"url": {
"type": "string",
"comment": "url"
},
"system": {
"type": "string",
"comment": "系统"
},
"part": {
"type": "integer",
"comment": "集群化部署时的机器"
},
"level": {
"type": "integer",
"comment": "日志等级,error: 1, warning: 2, info: 3"
},
"keyword": {
"type": "string",
"comment": "关键字"
},
"create_time": {
"type": "date",
"comment": "传输过来的创建时间"
},
"system_time": {
"type": "date",
"comment": "本系统的创建时间"
},
"options": {
"type": "object",
"comment": "日志内容,以json形式储存"
}
}
system字段表示系统,因为公司可能有多个系统,但是公用一套日志系统,因此采用system字段标识系统。 part机器名称,用数字对应机器名称,采用字典翻译,一个有规模的系统不可能是部署在一台机器上的,基本都是呈多台机器集群化部署,因为日志内容中对机器体现也是非常重要的。 level为日志等级,在不同的情况下记录不同的等级,方便查找生产bug的时候快速定位。 keyword,在进行不定向日志记录的时候,有些重要且容易反复发生问题的日志可以记录一个关键字,方便查找问题的时候快速定位目标日志。 create_time和system_time分别是请求传输过来带的时间和日志系统产生的本地时间,两者的差距可以看出网络传输的速度。 options是最重要的日志的内容,以json格式储存。
控制表
有些时候,某个动能可以要批量的操作到大量的数据,而且有可能多个帐号操作同一批数据,或同一匹数据的关联值,那么当一个操作以不同的操作员同时发生的时候,就有可能会产生重复数据或者是数据流转不准确的问题。这个时候,就可以由我们的--控制表--出场了。
控制表,顾名思义,就是对某个事件进行控制,当某个任务开始执行的时候,在控制表中添加一条数据,在任务进行中的时候,如果再次请求,先查询数据的状态,如果正在进行中则返回进行中不再次进行,当任务结束,则将控制表数据改为运行结束的状态。
工单表
当一匹数据将要被处理的时候,我们总担心,如果其中一条数据因为各种各样的原因被处理失败了怎么办?是全部失败直接报错呢还是部分成功,如果是部分成功,那失败的数据是放置还是再次执行,如果执行一直失败会不会卡在循环里搞崩系统,这一系列的问题到底要怎么处理比较好呢? 下一个闪亮登场--工单表。 首先,将要处理的数据全部存入指定的表中。 然后安排指定程序每秒钟扫描指定的表,如果表中有内容,则执行指定程序。执行成功,将数据搬入工单成功表,执行失败将数据搬入工单失败表并标记失败次数。同时程序也扫描工单失败表,成功则搬入工单成功表,失败则更新失败次数。失败到指定次数之后的数据不再继续执行。 这样,数据便能被安全地、分批次地执行完毕,而且不必担心同功能的多次事件并发所造成的压力。最后还可以在页面展示失败数据。人为地进行核查。
类似的实现方式除了工单表之外,订阅--消息的方式更加值得推重。下次补充。