我们的微信公众号:构建设计编程
扫一扫关注我们,更新更好的文章等待着您
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------
1背景
业务背景
某企业现有一应用系统运行该企业业务审批流程,审批流程有6个;
数据量
系统运行3年,拥有业务数据20万余条;
问题描述
查询审批流程时间大约15秒,此查询速度对用户来说无法接受,按行业标准查询速度应该在5秒内,5秒是用户接受响应速度的最大值。
运行环境
表一 客户端运行环境
序号 | 项目 | 参数 | 备注 |
1 | cpu | intel奔腾2.39G |
|
2 | 内存 | 2G |
|
4 | 操作系统 | window xp |
|
5 | 浏览器 | ie8 |
|
客户端计算机的配置比较低,一般都是4年前的计算机 |
表二 应用系统运行环境
序号 | 项目 | 参数 | 备注 |
1 | 数据库 | oracle 11g |
|
2 | 数据库运行环境 | unix |
|
4 | 应用系统开发平台 | java |
|
5 | web服务器 | tomcat6 | 最大内存配置2G |
6 | web服务器运行环境 | Linux |
|
7 | 系统并发数量 | 50人左右 |
|
应用系统运行的服务环境良好 |
2分析
问题定位
应用系统在设计之初选定了JQGrid组件用于展示数据,针对考虑到客户端性能问题,焦点首先集中在此组件上,觉得是此组件在渲染数据时让查询数据变慢,为了验证这一猜想,使用了HttpWatch工具进行分析,分析数据如下面表格:
表三 httpwatch监测数据报告
序号 | 分析内容 | 持续时间(秒) | 备注 |
1 | 加载js、css、图片等内容 | 0.7 |
|
2 | 从数据库中取数据 | 13.5 |
|
3 | js渲染加载数据 | 1.1 |
|
合计 | 15.3 |
|
分析的数据让我们大跌眼镜,本来以为是JQGrid性能问题,结果是数据这个层级出了性能问题。定位到了问题,我们接下来对设计进行多个维度分析。
问题分析
1) 业务
系统运行了6种类型的审批流程,在查询界面需要查看申请单号、申请时间、申请人、申请部门、当前状态、用途、申请类型信息,6种类型的申请单中有5种申请单含有以上所有需要查看的信息,还有1种申请单不具备用途这一信息。
2) 程序设计
软件设计人员对此业务进行分析后进行了如下实体类设计:
u 1个基类:命名为Apply,此类含有申请人、申请部门、申请时间、当前状态、申请类型属性;
u 5个流程类:继承基类Apply,流程类含有业务属性;
u 1个流程类:继承基类Apply,流程类含有用途属性与业务属性
从面向对象的程序设计上来看此种设计不存在任何瑕疵。
3) 数据库设计
根据实体类设计,创建了6张业务表来存储业务数据:1张基表,6张业务表,基表的数据字段如下表:
表四:基表[1]
序号 | 字段名称 | 说明 | 备注 |
1 | id | 系统标识 | 主键 |
2 | code | 申请单号 |
|
3 | deptId | 申请部门id |
|
4 | userId | 申请人id |
|
5 | state | 状态 |
|
6 | time | 申请时间 |
|
7 | type | 申请单类型 |
|
表名:t_apply
表五 业务流程表[2-6](含有用途属性,5张表)
序号 | 字段名称 | 说明 | 备注 |
1 | id | 系统标识 | 主键,值与基表中的id值一样 |
2 | purpose | 用途 | 字符类型,最多100个字符 |
3 | xxx | 业务属性1 |
|
n | …… | 业务属性n |
|
表名:t_apply_1 至 t_apply_5(5张表)
表六 业务流程表[7](不含有用途属性)
序号 | 字段名称 | 说明 | 备注 |
1 | id | 系统标识 | 主键,值与基表中的id值一样 |
2 | xxx | 业务属性1 |
|
n | …… | 业务属性n |
|
表名:t_apply_6
序号 | 数据表 | 表名称 | 数据量(条) | 备注 |
1 | 申请基表 | t_apply | 280,000 |
|
2 | 申请业务表一 | t_apply_1 | 160,000 | 含目的字段 |
3 | 申请业务表二 | t_apply_2 | 10,000 | 含目的字段 |
4 | 申请业务表三 | t_apply_3 | 20,000 | 含目的字段 |
5 | 申请业务表四 | t_apply_4 | 15,000 | 含目的字段 |
6 | 申请业务表五 | t_apply_5 | 30,000 | 含目的字段 |
7 | 申请业务表六 | t_apply_6 | 45,000 | 不含目的字段 |
4) 应用系统层
应用系统在查询申请单时,每页显示100条数据,开发人员使用以下设计方案查询申请数据:
1 查询申请主表t_apply;
2 根据主表的id值分别查询6张子表,从子表中获取purpose字段值,并在页面显示;
根据上面设计方案,查询100条申请数据最少需要执行1+100=101次查询,最多需要1+100*6=601次数据库查询,平均查询次数为(101+601)/2=351,分析得出查询数据缓慢的原因。
3解决方案
经过对问题的分析,我们知道了问题所在的原因,根据当前面向对象的设计观点及数据库三范式的设计原则,此设计是满足标准设计原则的。为何满足了业务需求与设计需求,但是还是会出现以上问题,这值得我们设计人员及开发人员思索这个问题,此问题集中的焦点在查询条件中含“目的”信息,为了实现这一查询目标,在现行的设计框架下必须牺牲系统性能,为此我们能否折中处理,下面提出3种解决方案(其实方案还有更多,办法总比问题多)。
3.1方案1
改进目标
1) 工期最短;
2) 不改变原有系统结构,系统调整最少;
3) 缩短一半查询速度;
改进方案
1) 创建目的视图,将t_apply_1至t_apply_5 union连接为一张视图:
CREATE OR REPLACE VIEW t_apply_purpose_viewAS
SELECTid,purpose FROM t_apply_1
UNION
SELECTid,purpose FROM t_apply_2
UNION
SELECTid,purpose FROM t_apply_3
UNION
SELECTid,purpose FROM t_apply_4
UNION
SELECTid,purpose FROM t_apply_5
2) 创建带有“目的”的申请单视图
CREATE OR REPLACE VIEW t_apply_view AS
SELECT a.*,b.purpose FROM t_apply a
LEFT OUTER JOIN t_apply_purpose_view b ona.id = b.id
3) 修改应用系统代码
将以前的SQL语句中查询的表替换为t_apply_view
改进耗时
半天时间
改进效果
序号 | 分析内容 | 持续时间(秒) | 备注 |
1 | 加载js、css、图片等内容 | 0.7 |
|
2 | 从数据库中取数据 | 6.2 |
|
3 | js渲染加载数据 | 1.1 |
|
合计 | 8 |
|
3.2方案2
改进目标
1) 工期适中;
2) 系统调整适中;
3) 查询速度缩短到5秒内;
改进方案
1) 在申请基表t_apply中增加purpose字段;
2) 每次在不同申请单中申请时自动将purpose值写入申请基表中,修改每个申请单申请结果;
3) 查询时直接查询t_apply中数据,一条SQL语句执行即可;
改进耗时
5天时间
改进效果
序号 | 分析内容 | 持续时间(秒) | 备注 |
1 | 加载js、css、图片等内容 | 0.7 |
|
2 | 从数据库中取数据 | 2 |
|
3 | js渲染加载数据 | 1.1 |
|
合计 | 3.8 |
|
在此方案中查询速度得到了大大提升,是一个近似完美的方案。如果客户给定的资金支持是一个定值,我们架构师是否会考虑时间成本问题,此方案是否是一个最优解?我们架构师能否再仔细的考虑一下,能否实现双方共赢的局面:客户资金耗费少,改造时间成本低?在此问题上,我们引入下面的方案3。
3.3方案3
改进目标
4) 工期适中;
5) 系统调整适中;
6) 查询速度缩短到5秒内;
改进方案
1) 在申请基表t_apply中增加purpose字段,并创建索引;
2) 创建目的视图,将t_apply_1至t_apply_5 union连接为一张视图:
CREATE OR REPLACE VIEW t_apply_purpose_viewAS
SELECTid,purpose FROM t_apply_1
UNION
SELECTid,purpose FROM t_apply_2
UNION
SELECTid,purpose FROM t_apply_3
UNION
SELECTid,purpose FROM t_apply_4
UNION
SELECTid,purpose FROM t_apply_5
3) 初次使用前利用t_apply_purpose_view视图更新t_apply中purpsose字段;
4) 在每次查询的时候更新t_apply表中的purpose字段为空的记录,
UPDATE t_apply a SET
purpose = (SELECTpurpose FROM t_apply_purpose_view WHERE a.id=b.id)
WHERE a. purpose IS NULL
5) 直接查询t_apply数据,2条SQL执行即可;
改进耗时
1天时间
改进效果
序号 | 分析内容 | 持续时间(秒) | 备注 |
1 | 加载js、css、图片等内容 | 0.7 |
|
2 | 从数据库中取数据 | 2.8 |
|
3 | js渲染加载数据 | 1.1 |
|
合计 | 4.6 |
|
在此方案中查询速度得到了大大提升,是一个近似完美的方案。如果客户给定的资金支持是一个定值,我们架构师是否会考虑时间成本问题,此方案是否是一个最优解?我们架构师能否再仔细的考虑一下,能否实现双方共赢的局面:客户资金耗费少,改造时间成本低?在此问题上,我们引入下面的方案3。
3.4 方案效果对比
序号 | 客户需求 | 方案名称 | 工时(天) | 改进后查询速度(秒) | 提高百分比 | 改进前查询速度(秒) |
1 | 工期短;查询速度缩短到原来的一半 | 方案1 | 0.5 | 8 | 47.7% | 15.3 |
2 | 工期适中;查询速度降低到5秒内 | 方案2 | 1 | 3.8 | 75.1% | |
3 | 方案3 | 5 | 4.6 | 69.9% |
4问题分析
我们追根索源这一问题,对当时的情景进行还原再现,想对应的人员请大家看看如果我们是场景中的人,我们是否也是这样来做的,请做个对照。
被访谈人员(甲方):
我们现在有一批申请业务希望电子化,该审批业务涉及到6种不同的申请。
访谈人员(乙方):
您有6种不同类型的申请,请您提供一下6种纸质版本的申请表格,最好已经有了数据的纸质表格,数据可以供我们参考一下,另外给我们提供一份电子表格。请问你们会根据什么样的条件去查询这些申请表?
被访谈人员(甲方):
我们想根据申请时间、部门、人员工号、人员姓名、申请单类型来查询,这些条件基本够了,我们需要在查询列表上显示在查询界面需要查看申请单号、申请时间、申请人、申请部门、当前状态、用途、申请类型信息。
甲方找来已经存档的纸质申请单交给乙方,各种申请单提供了10多份。
访谈人员(乙方):
您能详细描述一下各种申请业务流程是如何审批的吗?
被访谈人员(甲方):
我给你把每种业务解释一下,便于你们能够理解。
甲方将审批流程业务详细的讲解给乙方,直到乙方完全理解各个流程的审批过程,双方的焦点集中在审批流程上。乙方将资料带回单位对业务进行梳理分析,并画出泳道图给客户确认,客户拿到泳道图,对着单位的相关制度进行逐一确认。
乙方根据甲方所提的需求进行设计、开发、测试、上线,在系统运行的第一年里没有什么问题,在第二年里速度会有些慢但是不太明显,到了第三年限系统运行速度缓慢问题就凸显出来,于是便有了上面一幕。我们回过头来看看,从专业的角度看看我们调研的时候所问的问题是否有所欠缺:
1) 我们是否对系统数据量有个预估;
2) 我们在设计的时候是否真的吃透了用户的需求;
3) 我们是否了解客户潜在的需求;
访谈人员在访谈前制定调研提纲,提纲如下:
l 组织架构是什么样的?
l 系统使用人员的人数?
l 每天业务数据有多少?
l 所需系统响应时间为几秒?
l 客户使用计算机是什么样配置?
l 服务器配置是什么样配置?
l 。。。。。。。。。。。。。。。。。。。。。。。。。。
(后面我们将会制作一篇如何做好调研的文章)
根据提纲询问得到的答案,制定架构设计原则:
l 客户端硬件配置决定了系统开发使用的前端框架;
l 系统使用人数*每天业务量*12月=一年业务数据流
设计人员在获取客户需求、系统反应时间、业务数据量的情况下进行设计,回到审批流程查询这一特定问题上,在申请主表上加入“目的”冗余字段,在每个申请提交的时候将“目的”信息写入申请主表中。此种方案正是方案2的设计思路。
5总结
一个小小的设计导致了系统运行缓慢,设计人员要从中吸取一定的经验教训,自己挖下的坑,一定是需要自己或队友去填坑的(不怕复杂的系统,只怕猪一样的队友),出来混迟早是要还的。
造成这一问题原因是在需求及设计阶段未全面分析导致的,在应用级别的系统中低层数据库结构设计是非常重要的,数据库结构设计决定了上层应用开发的复杂度及系统运行的效率,在后面的文章中,我们将会介绍几种典型业务对应的数据库设计知识,敬请期待。
6后续
如果您喜欢我们的文章或者有什么好的意见,请订阅我们的微信公众号并给我们留言,我们是一个专注业务及业务技术实现的团队。
我们的微信公众号:构建设计编程
扫一扫关注我们,更新更好的文章等待着您