起因:小公司要求sql优化,这是因为之前业务不规范,过多联表,数据量级达到了百万级,再加上链表中的子查询用了group by,union两个关键字,导致子查询的数据走不了索引所导致的sql执行慢的表象。
其中,核心的瓶颈压力是group by,union,2 个 group by百万级数据,union 1w数据,总共执行时间为8.7s,因为是子查询,前面的查询步骤依次执行,sql总耗时才达到16s。
对此,我的想法是,sql优化解决不了当前的问题,首先2个group by和1个union是sql逻辑运算必要的步骤,所取的字段,又落在6、7张表上,每张表的数据量级均在10w+以上,有2张表有100w+量级,mysql能在16s内返回所需要的数据,已经是非常强大了,查询次数可以说是:100w x 38w(没有走索引的group by) x 22w(没有走索引的group by) x 2w(没有走索引的union) x 3 x 3,这还是最优的没有没回表时间,要是每张表都回表,那么mysql的查询次数是:100w x 38w(没有走索引的group by) x 22w(没有走索引的group by) x 2w(没有走索引的union) x 3 x 3 x 2 x 2 x 2 x 2 x 2,能在这么多查询次数中,仅仅16s就返回联表数据,mysql内部肯定是有黑科技,不得不说其实sql优化层面mysql做的够好了,只是使用者的方式不对,多联表,还用group by、union生成新数据,让一部分的数据不走索引。
再优化的方向无非是筛选条件,或者临时表。
1:筛选条件
实现是最快的,但是将筛选条件潜入到mybatis xml 的 sql里面,显得不美观;
并且,筛选条件用户进来必须有筛选条件,目前是已经有了1个默认筛选条件,但是还是慢,因为1个筛选条件根本过滤不了多久数据,也可以让用户进来,直接默认给他一堆的筛选条件,但是在查询接口上,用户可以主动去掉那些默认的筛选条件,那么此举动,无异于掩耳盗铃、皇帝新衣。
2:临时表
将费时的操作group by,union,同步在不同的表里面,并把他们的数据加上索引,那么有group by的子查询,就可以退化成为简单的联表查询,而且走了索引,速度应该会有1个较为明显提升。
不足之处,group by的操作需要同步时间,也就是新数据,必须要定时器的group by操作才行,有延迟。
其实我更看好的是大数据库的大宽表方式,虽然也有同步延迟,但是大宽表可以将联表降维成单表,那么根据水桶定理,其实查询次数是:100w+(最大的单表主表数),在查询方面,可以实现毫秒级的查询,目前我想不出有比这种方式还快的优化手段了。
但是没办法,谁叫领导硬要sql优化,只能说是去尝试以这种方式去优化。
重点:
要临时表肯定要造数据,业务数据不好造,肯定是线上的数据最真实,也最能说明问题,但是直接在线上数据库操作,万一一不小心,就是生产事故。
所以我的想法是,把线上的数据copy到测试数据库,测试环境风险低一点。
copy数据的方式有两种:
1:转成文件,然后在测试环境用文件同步数据。
2:直接用Navicat for MySQL的拖一拖功能:
这种方式又快又方便,要是没有接下来的问题,那就是爽歪歪。
(注:该图用的是自己的数据库资源做复现操作,暂时不大敢操作真实项目的数据库了)
3:问题
该操作的方式是直接删除测试环境库里面的表,然后再同步输入数据。
这也意味着,我要是脑袋那么一昏,就直接把线上环境上的100w+数据量的表给替换成测试环境的表,要是线上环境没开binlog日志无法撤回操作,也没有备份数据库的话,那就代表着我,直接把一个业务给弄的宕机了,造成比删库低一个层次的删表操作,这么一想,不由的心中大惊,倒吸一口冷气,不过幸运的是,我这次操作无误,操作确实是将生产环境的数据库,给copy到了测试环境,侥幸的逃过一劫。
不过这一劫还带着另一劫,测试库的测试数据没了,因为测试库被执行drop table操作,新表是有了,但是那是线上的数据,线上的数据肯定不适配测试环境的数据,那就造成测试环境的功能失效,虽然影响面小一点,但也能是说把功能给宕机了。
但是,更幸运的是,我在进行复制操作前,在测试环境进行了备份操作,所以,很轻松的把拷贝线上数据的新表名字+1,然后直接用备份表改回表名,最小程度的阻止了测试库功能宕机的问题,又逃过一劫。
我开始复盘原因,是因为我太想当然,因为在同库里操作一张表,进行复制操作:
直接是有表就加copy后缀,有copy后缀的就后面的尾标+1,根本不会有那个删表的动作,再加上平时自己玩数据库,也没有想太多,直接进行了操作,差点良成大错,还好操作准确,一切还算有惊无险。
谨以此文,聊表记录,大家也引以为戒,Navicat for MySQL拷贝数据至文件复制的方法,麻烦是麻烦了一点,但是直接Navicat for MySQL拖一拖,太容易出错了,搞不好就得跑路,希望大家重视起来。