L 又回来了...
走在 8 月的夜魔都街头,霓虹依然那么撩人,黄浦江畔依然清晰可听的传来酒杯欢快的交割。这一切再次在 L 身旁流过,却早已是云烟。
公司的世界500强ITIL项目已经瓜熟蒂落,该拿到的工程款一分不落,要不怎么能是500强呢,一个报表开价都超3万。这段时间,在 L 手里流过了至少陆家嘴一栋房。那是什么概念,L 唏嘘,“做得再好,也只是为资本家数钱,他们餐桌上宁可多添几瓶82年的拉斐,也不会念着你有个娃儿,给你多涨一点工资。连超过9点的打车报销都取消了。该换个地儿了”
L 一口长气,走出滨江森林软件园区,换上了张江方向的2号线。他的下一站便是浦东软件园。地大,人稀,活儿少,离家近。L 觉得这才是自己想要的工作。重新做上开发类岗位,做点自己开心的事,不用担心10多张嘴等着他来喂,心静了许多。
“L 听说你以前做过很多 SQL 调优,帮我看个报表呗。30多秒,优化提高不多” 新办公室的小 C 是个话不多的南方姑娘,每次有问题都是很直接,没有任何前戏。L 倒也很喜欢这样的对话方式,直来直去效率最高。
SELECT Prod.Class, Sum(Fct.SalesAmount) AS Amount FROM FactInternetSales Fctinner join DimProduct Prod on Prod.ProductKey = Fct.ProductKey WHERE Prod.Class IS NOT NULL AND Prod.ListPrice between 20 and 200GROUP BY Prod.Class
其实这样的代码,优化本来很简单,一个索引,一个SQL条件下推就可以搞定了。但偏偏生产上没有找到索引,而加索引这类 DDL 操作基本不可能在平时动手做。
“执行计划看得出哪里有问题吗?”
“一切都很正常啊”
“你看,执行计划的上下两个访问表的方式,有啥子问题没”
“访问了聚集索引”
“访问方式是什么”
“都是聚集索引扫描方式”
“问题就在这里”
“不懂”
“扫描引起很多无关数据页的访问,浪费很多时间。就像查字典,你会选择一页一页去翻你想要查的字,还是直接翻到对应拼音的那几页?”
“你的意思是,让表访问方式改变成搜索形式”
“确切的说,那是 seek 方式。”
“再看这里事实表的 scan, 查了6万多数据,相当于是做了全表扫表”
“那怎么让他变成 seek 查找呢?这 Join 两边都有索引,已经最优化了啊”
“Predicate Pushdown 不知道你听说过没,就是在 Join 之前,先把条件下推到基表中去,以减少 Join 时的基数。这需要配合 bitmap filter 来实现”
SELECT Prod.Class, Sum(Fct.SalesAmount) AS Amount FROM DimProduct Prod inner HASH join FactInternetSales Fct on Prod.ProductKey = Fct.ProductKey WHERE Prod.Class IS NOT NULL AND Prod.ListPrice between 20 and 200GROUP BY Prod.Class OPTION(QueryTraceOn 8649)
“这里的 Hash Join 与 QueryTraceOn 8649 好厉害”小C第一次看到这样的SQL写法,不禁拍手叫绝。
“我们把 Join 改造成了 Hash Join, 并且执行计划也打造成了并行执行模式,还引入了 bitmap 这货。还记得之前我提过的 bloom filter 嘛,就是 bitmap 的底层思想。Hash 会改变 Join 的顺序,所以放到第一个去了。而重点是 QueryTraceOn 8649 ,它使得所有的单线程执行变为多线程执行了。”
“你看,自从引入了 Bitmap, 条件筛选下推了,仔细看 Cluster Index Scan, 在Predicate这一栏,有 Probe(Bitmap1004,Productkey,N'['IN ROW']') 这个提示,原本需要查 6万多数据,现在总共需要读取6千多。问题解决”
“我要回去好好研究,今天学的东西有点多”
看着小C满载而归的身影,L也止不住的苦笑。“其实公司真不是仅仅让你来学东西的。主动创造价值才能够走的更远,当然小C还年轻,不会考虑那么多。年轻真好”