leader让我开发一个项目排期的功能,一个看似很简单的功能,我却栽了坑,并从中学到了设计模式及算法的重要性。
一、功能点
目前平台已有项目管理功能,需要增加一个项目排期的功能,其实就两个页面,一个排期的编辑页面,一个按照团队展示团队人员项目排期的甘特图。
二、原来做法
我不怎么会前端,一开始把精力都放在前端甘特图绘制上了,忽略了设计的重要性。
我只是简单的建了一张表,把项目、人员、排期一股脑放在了一张表里,与项目表、人员表存在紧耦合,不符合数据库设计原则。表结构如下
id | schedule_name | schedule_duration | start_time | end_time | staff_name | project_id | project_name | note |
1 | 开发设计 | 4 | 2016-10-20 | 2016-10-28 | 张三,李四,赵红 | 21 | 项目一 | 开发开发 |
2 | 测试评审 | 6 | 2016-10-29 | 2016-11-12 | 小张,小王 | 100 | 项目二 | 测试测试 |
这样设计表时,排期的增删改查很容易,就按照上图一输入的填写的数据表里就可以。但是在第二个页面,按照团队展示时,就遇到问题了,先查到团队下所有的人员,然后一个for循环,不停的http请求去获取每个人的排期,性能很差,后来我的做法是取出所有的人员,取出所有的排期,代码里面处理逻辑,这样满足基本需求,时间复杂度为o(n*m)
三、新需求
后来leader提出新的要求,要求对每个人下面的排期进行分组,时间上没有交集的排期放在一个分组,我在原来的处理逻辑循环里,对每个人的任务进行一个分组处理,分组处理的时间复杂度为o(t*t),再加上外层的循环,复杂度为o(m*n*t*t)。页面展示根本刷不出来,一直超时。但是基于上面的表结构设计,又没办法优化算法。
四、重构
不得已,必须进行重构。
(1)拆表
首先是拆表,按照数据库设计规范,即那几个范式
第一范式:要求所有的域都应该是原子性的。我原来的设计,staff_name一列放n多人名,其实是不合理的
第二范式:要求数据库表中的每个实例或记录必须是可以唯一的区分
第三范式:要求一个关系中不包含已在其他关系已包含的非主键字信息。如我上面的表中,project_name,staff_name都是非主关键字,不应该出现在排期表中。
于是,将上面的表拆成两张表
排期表
id | schedule_name | schedule_duration | start_time | end_time | project_id | note | ||
|
|
|
|
| 项目表主键 |
|
排期-人员关系表
id | schedule_id | Staff_id | gmt_time |
|
| 人员表主键 |
|
通过schedule_id与排期表关联
通过staff_id与员工表关联
(2)使用事务
拆成多个表后,为了保证数据的一致性,就要使用事务,只有一部分成功时要进行回滚,当所有的操作都ok时,才commit
(3)通过mysql的一些函数及用法,减少业务代码量
主要使用了concat、group_concat、ifnull函数,使用了多表查询,使用了group by等用法
使用group_concat时还遇到一个小问题,就是group_concat有个默认长度是1024,超过这个长度字符串会被截断,可以通过设置group_concat_max_len=-1为不限制长度。
(4)优化算法
对于那个新需求,对排期分组,我原来的算法复杂度是o(t*t),将其优化为o(t)
这样,经过上面操作后,时间复杂度由原来的o(m*n*t*t)变成了o(m*t),性能大大提升。
心得体会:处处皆学问,做任何事情前都得思考!