如何设计一张事件记录流水表(版本2)
在设计完 版本1的事件记录表 后,大家就开始马不停蹄写代码去新增事件记录数据。
流水ID id |
对象ID obj_id |
系统编码 sys_code |
网点编码 area_code |
操作者工号 operator_id |
源状态 source_status |
事件编码 event_code |
事件描述 event_desc |
目标状态 target_status |
创建时间 create_time |
123456 | A111 | TEST_SYSTEM | XXX | 0123777 | 初始 | 001 | 打印 | 打印完成 | 2018-09-06 18:33:35 |
123457 | A111 | TEST_SYSTEM | QQQ | 0123666 | 打印完成 | 002 | 核销 | 核销完成 | 2018-09-07 12:05:43 |
改进:
在记录事件的源状态StateA时,一开始的做法是获取其他业务数据表中某对象的当前状态,来设置事件记录表的源状态。但在一些业务场景下,对于同一个事件编码event_code,虽然目标状态StateB是一样的,但是源状态StateA会有很多种情况,此时如果通过业务数据表的当前状态来设置源状态的值,会十分繁琐,而且业务代码上需要知道当前状态,不够透明。
基于上面情况,我们不再从其他业务数据表中获取某对象的当前状态,而是依赖于事件记录表前后的数据,从版本1的表中,可看到,在按照创建时间排序的前提下,ID为123457的源状态(打印完成),就是上一条记录ID为123456的目标状态(目标状态)。所以想要设置某对象的源状态,只要找到该对象在事件记录表中的上一条记录的目标状态即可。
但这种方案,严重依赖于上一条记录,当有多个节点同时都往这个事件记录表插入数据时,要对这“上一条记录”的数据进行锁定,需要采用分布式锁或者数据库锁的机制。
再次改进:
认真想想,由于事件记录表是按照对象做动作时按时间顺序插入的,只要保证时间顺序,其实可以不需要记录源状态,每条数据的目标状态,也相当于它上一条数据的源状态。因此,我们对事件表进行改造,形成版本2:
流水ID id |
对象ID obj_id |
系统编码 sys_code |
网点编码 area_code |
操作者工号 operator_id |
事件编码 event_code |
事件描述 event_desc |
目标状态 target_status |
创建时间 create_time |
123456 | A111 | TEST_SYSTEM | XXX | 0123777 | 001 | 打印 | 打印完成 | 2018-09-06 18:33:35 |
123457 | A111 | TEST_SYSTEM | QQQ | 0123666 | 002 | 核销 | 核销完成 | 2018-09-07 12:05:43 |
由于事件记录表本身的时间顺序的特性,所以版本2的表基本可以满足我们记录对象全生命周期的需求。还是版本1的需求,比如我们想知道对象A111从打印完成状态到核销完成状态所花费的时间,用sql更容易就实现了:
开始时间:
SELECT create_time AS begin_time FROM db_event_report WHERE obj_id = 'A111' AND target_status = '打印完成';
结束时间:
SELECT create_time AS end_time FROM db_event_report WHERE obj_id = 'A111' AND target_status = '核销完成';
然后将上面两条sql查询出来的begin_time和end_time套入以下sql函数,即可算出相隔的分钟数
SELECT TIMESTAMPDIFF(MINUTE, begin_time, end_time);
至此,我们的事件记录流水表设计结束。
上一篇: RBAC 的表结构设计
下一篇: 男人肾结石吃什么好 肾结石患者饮食指南
推荐阅读