前言
注:这里讨论的场景在于MIS,OA一类的系统
大部分同学在看到这个问题的时候,第一反应是糟糕的SQL语句,没有加索引,这甚至已经成为一种惯性思维。
当发现性能出问题的时候,一般都会想到加个索引,或者改造下连接方式,去掉“not exists”诸如此类,但效果往往不太理想,或过一段时间后,效果又不理想了,此时,一般大家都会觉得问题在于自己还不是大神。
正文
今天给大家分享的就是:为了提高性能,不是大神的我们还有多少方法可以采取?
性能有三大杀手:1)糟糕的SQL使用;2)糟糕的数据结构;3)糟糕的业务模型
下面举一个实际的例子,在EPO物资采购系统中的需求跟踪功能中,可以查询到所有历史提交的需求申请单。查询包含从需求申请->需求受理->订单->验收入库->通知领用->发放完成所有环节的状态。最初的做法是通过join将需求表、需求受理表、订单表、验收表、领用通知表和发放表连接起来形成一张视图。当数据量小的时候,看起来一切都是那么的完美,但是后来就不那么美好了。于是,后来我们采取了一系列的优化。
阶段一:物化视图
当join的时候,问题出现在join的计算过程,数据量大了,计算耗费就会大,性能便会下降。不过,当问题来的那天,对于这种问题,大家是非常坦然的面对,做一张物化视图就可以了,每天晚上计算一下,第二天查询物化视图即可。利用原生的SQL功能,物化视图,对于大家来说手到擒来,投入低,收益高,效果立竿见影。
物化视图:牺牲实时性,牺牲存储空间,换取性能提升。
阶段二:改变数据结构
随着数据量加大,我们发现,物化视图的计算越来越慢,虽然是放到晚上,但是随着大家都认识到物化视图可以解决问题,大批量开始使用物化视图,且都放到晚上,滥用的程度甚至都惊动了DBA,晚上的DB负荷居然超过白天的业务高峰运行期。
这个场景下的物化视图隐藏的一个问题,就是会重复计算那些永远不会发生变化的数据。比如,有一张2010年的需求单,申请的是电脑,公司规定电脑4年报废,如果我们在2015年跑物化视图时,那张需求单对应到的数据,就永远不会变化;但在每次物化过程这张需求单都会被join到视图中,而join的过程又会产生计算花费。
此时我们要做的就是通过数据结构的改变,干掉那些无效的计算。此时会有多种选择,分离历史数据、实体化表等。我们采取的是实体化表的方式,实体化表上会对需求单进行区分,比如说刚刚提到的报废的需求单或领用完成的需求单,这类需求单是再也不会发生改变的,我们给予不会变化
标示。而对于那些还未领用完成的需求单,我们标示为待计算
,对于那些待计算
的需求单我们每天晚上重新计算一次,删除原有数据,填充最新数据。在目前的数据情况下,待计算的需求单占总单的5%以内,相比之前的方式我们至少省去了95%的无效计算过程。
改变数据结构:牺牲存储空间,干掉无效计算过程,从而有效降低数据库负荷。
阶段三:优化业务模型
需求跟踪中有一个查询条件是物料名称,对于这类文本型的查询条件,99%的同学选择都是“like %XYZ%”,但只有一半的同学知道,这种写法使用不到索引,这一半同学中又有99%的同学都会认为这是没有办法的。确实,在技术上99%的人都想不到办法,在技术上这个问题是无解的,因为我们都不是大神。不过可以分享给大家的是,这不是一个错误的SQL写法,而是一个糟糕的业务模型。
比如说,我们通过后台可以分析出,大家喜欢用“电脑”、“主机”这类关键字,而输入“电脑”在我们物料库中对应肯定是“平板电脑”、“笔记本电脑”,“主机”对应的一定是“电脑主机”,我们在后台通过将“电脑”转化为“平板电脑”、“笔记本电脑”、“电脑主机”,“主机”转化“电脑主机”,便可以使用“like 电脑主机%”、“like 平板电脑%’、”like 笔记本电脑%”。通过业务模型的优化,再选择合适的SQL语句。上面例子中,80%的查询条件,都可以在后台转化为“XYZ%”的方式,进而可以使用到了大家耳熟能详的索引。
优化业务模型:通过运营数据分析,优化查询条件,更好使用我们已知的SQL技巧。
当我们不是大神的时候,我们在发现性能问题的时候,我们的分析方式可以是:你有没有犯常识性的SQL错误?你有没有选择合适的数据结构?你有没有思考过业务模型非常糟糕?