序
这是自上一本《收获,不止Oracle》一书后,我第二次为作者写序,我知道这又是一本极不寻常的书。
果然,初翻开此书,就给我带来了惊喜。作者将全书脉络展现得非常清晰,先在前言中通过小故事梳理出SQL优化的方法论,接下来将各SQL优化的知识点融入到方法论中,形成了全书目录,从而让读者明白为什么要讲解这些知识,学了这些知识对优化有什么帮助。更让人称道的是,这个目录是以一个生动有趣的足迹图展现在读者面前的,不落俗套的同时给人一种视觉上的惊艳感。这是谁的足迹,分明是你自己的足迹!于是,一种强烈的代入感油然而生,来,迈开双腿,学习着,思考着,奔跑着!
足迹所到之处,感动如影随形,只因案例无数。我看到了作者十多年如一日在工作的荆棘之路中勇往直前的精神,看到了作者在攻坚克难后的沉思总结,看到了作者作为感动福富十大人物的一种坚持的精神!更难得的是,这些实战案例背后密布的代码不但没让我迷糊,反倒让我觉得非常亲切,因为本书为每个章节的案例都进行了详细的分类和汇总,让人一目了然。
翻开此书,作者极佳的文字表现能力和技术实力立刻跃然纸上,读者一定会感叹作者怎么具备将晦涩难懂的技术书写得如此清新脱俗的能力!不过我却一点都不感到意外,始终是抱着一种验证的心态来阅读,其中的原因来自于他在公司的双重身份。梁敬彬是福富特级专家,又是公司四星级内训师,前者的荣誉显示了IT人的辉煌技术成就,后者的勋章证明了老师的杰出教学能力,两者一完美结合,书中再多的惊喜也不会使你感到意外了。我看到IT企业中有很多技术牛人由于在表达沟通交流方面的欠缺,在传帮带方面做得不够好;也看到很多技术人员具备良好的沟通能力却苦于技术不过硬而无法与人深入交流。作者在这方面给我们广大IT技术人员树立了一个很好的榜样,会打硬仗还要会带兵。据统计每年接受梁敬彬培训的福富技术人员多达400人,加上他每年在公司以外的演讲和技术分享,梁老师可谓桃李满天下,给梁老师点个大大的赞!
随着对此书的进一步了解,我知道作者邀请了业界许多专家对此书进行完善、美化、审核。至此,我又读出了一种精神,叫“团队精神”,此书正是团队协作的结晶!作者把工作中的团队精神带入书籍编写中,值得称道。我在感叹此书的不同凡响之余,更感慨团队的无穷力量!
此书必将成为IT书籍的又一个经典传奇,我相信广大读者在翻阅此书时,除了可以学到精妙的SQL优化实用技术外,还可以从无数案例中感受到什么叫激情、震撼;从方法论总结上理解什么叫升华、用心;从各种梳理的表格和思维导图中体会什么叫清晰、极致;从书的精妙视觉设计中领悟什么叫求道、协作。我想说的是,从菜鸟到SQL大师其实不易,真正的大师不止是技术上精湛,还需要一种精神。这种精神,还请你在阅读本书中感悟吧!
福富软件公司副董事长 杨林
匠心独运 独树一帜
——与梁敬彬先生序
在拿到敬彬新书的稿件时,我的脑海第一时间呈现出来的就是这八个字:匠心独运,独树一帜。
技术书籍的写作也是一个创作过程,平庸者千篇一律,卓越者自出机抒。
写作一本千篇一律的书很容易,而要想自出机抒,形成自己的风格,并且为读者认可,则是难上加难。而敬彬的系列作品,已经形成了自己独特的风格,并且为广大技术爱好者们所喜爱,这不独是匠心所在,更是隐现宗师风范。
如作者所说,有数据库就有SQL,而SQL又因其灵活、复杂,而让众多应用系统饱受性能之苦。我一直认为,在开发环节提高SQL质量才是数据库优化的治本良方,SQL审核也是DevOps理念在数据库领域的最佳落地点,云和恩墨也在此保持持续的关注并研发了产品。敬彬的新书从SQL入手,以其独特的故事演绎法,让SQL优化成为了一种趣味,书中还通过实例打破了以讹传讹的种种法则,让读者获得思想上的自由。
这是一本活的书,活跃的思想,活泼的行文,活动的二维码,活灵活现的音视频,互联网时代,原来书还可以这样写。
快点来一起体验吧!
盖国强
云和恩墨创始人,Oracle ACE总监,ACOUG主席
致谢
我首先要感谢福富软件公司,因为这本书的原型,正是公司的认证资格课程《基于案例学SQL优化》。公司福富大学专程请来专业的企业内训专家为福富内训师们做内训课程的培训和完善,最终这门课程有幸成为我们公司年度三门精品课程之首。这期间福富研究院的专家们对本课程进行了大量的评审,并提出了各种宝贵的意见。感谢福富公司!感谢福富大学和福富研究院!
我要感谢我们项目组团队的成员,没有黄锏、荣志等公司杰出的技术专家和我并肩作战,我也没有精力写完这本书。我要感谢姚建艺、郑清泉、郑超群等,他们为我们团队的工作提供了最有力的帮助。要特别感谢我们的杨总,她一如既往的支持、她热情洋溢的《序》让我感动不已!
我要感谢我弟梁敬弘在本书内容上倾注精力。感谢林舒楠、张凤为本书在视觉设计上提供的帮助。感谢谢恒忠在线上拓展方面提供的帮助。谢谢你们的参与!
此外,我要感谢苏旭晖、卢涛、丁俊等这几位业内知名的数据库专家对本书的审核校验,感谢博文视点在出版方面的专业性指导意见。感谢大家的帮助!
最后,我要感谢我亲爱的家人,谢谢你们的支持!
梁敬彬
2017年4月
前言与意识:从优化方法到全书脉络
叹IT之一入深似海
传说:一入IT深似海,从此菜鸟泪成河。
老师,搞IT真有传说中的这么惨吗,那我从此要珍爱生命、远离IT了。
我们慢慢聊吧。话说这时代啊,应该是最好的时代了。知识的获取相当便利,基本上没有什么知识点是搜索引擎搜不到的;此外,现在的技术书籍、教学视频也非常丰富。除了自学手段外,我们甚至还可以在论坛上提问,或者参加各种线上和线下的培训。当今时代,IT学习成本越来越低,门槛似乎一点都不高!
对啊,那咋说深似海泪成河呢?
其实这话也是有道理的。我们来说说这个时代的IT系统,其和从前也大不相同了,现在对外的IT系统大多需要同时支持电脑终端和手机终端(手机终端进一步分为Android和iOS等操作系统),此外还要考虑各个接口,如关联业务接口、短信接口、微信接口、公安接口、银行接口……系统显然比以前更复杂了。这意味着系统开发在功能实现方面的难度更大了,而系统实现难度大又意味着对IT开发人员要求更高了!
嗯,好像是这样。
其实不止是IT系统功能实现的难度变大。你想想看,现在几乎人人都有手机,手机端的接入就意味着成千上万的人可以随时随地拿起手机访问系统,这给系统带来了可怕的访问量。此外,不可避免地会出现同一时刻大量用户同时访问某应用的景象,这又带来了巨大的并发量。因此系统如果没有良好的性能规划,很容易垮掉。所以说IT开发人员的压力不仅是实现难,还会遭遇性能瓶颈。当然,IT运维人员的压力更大,因为假如系统有问题,他们首当其冲。
哇,好像还真是如此,听得我手心出汗了。
前面我们谈到了功能实现困难,又提到性能瓶颈压力,现在我再提一点,即定位困难。还记得之前我说的接口吗?随着时代的发展,各种IT应用已从孤岛走向关联。比如你的系统是计费系统,当要对用户进行计费时,你可能要从客服系统中获取用户的套餐等资料,或许还要去网厅系统完成……这下问题来了,假如应用有故障,你知道问题出在哪吗?是你自己的系统出问题,还是接口的系统出问题?再比如,你好不容易定位出是自己系统的问题,那请问,到底是数据库、前端应用还是中间件的问题呢?
老师,有没有手帕,我擦擦脸上的汗。
假如你已经知道系统的问题出在数据库。那请问,是SQL还是其他问题,你如何定位,如何判断?再假如你通过努力判断出是SQL问题,那该如何优化,是动手改写呢,还是不用改写,加加索引啥的……
老师,要考虑的方方面面太多了,看来我是把IT系统想简单了。
刚讨论的话题,放在以前,是基本不用担忧的,这是时代高速发展带来的问题。接下来你换一个角度想想,这个时代越来越多的人依赖IT系统,你的系统一旦出现问题,多少用户会受到影响?这个时代越来越多的IT系统之间有关联,你的系统一旦出现问题,多少别人的系统会受到影响?怎么样,是不是又感受到另一层面的压力了。
哇,看来这时代IT人尤其是IT菜鸟的日子真的不好过啊!一入IT深似海,从此菜鸟泪成河。
结论:当今时代的IT系统复杂度高、数据量和并发量大、关联性强,无论是定位解决故障还是应用开发维护,难度都比较大,并不是一件轻松的事情。
赞IT之SQL地位高
你也吟上这诗了,别伤感,这不也有好事嘛,我之前就说过如今学习比以前容易很多。
说的也是,我都忘记了,还好还有这点值得安慰。
真是如此吗?好吧,以IT学习中的SQL优化为例,你知道如何进行学习吗?
这个我知道,SQL优化主要是看执行计划,比如发现是不合理的全表扫描,就设法转成索引扫描等。
说得有点简单啊。其实SQL本不需要优化,就是因为前面我们所说的当今IT系统复杂度高、访问量和并发量都大,而数据又是IT应用中访问的热点,因此这些压力自然就体现在IT系统对应的数据库模块上,所以SQL需要优化。
哦,原来如此,明白了。
任何IT系统,数据都是核心,同时也是访问和展现的热点,脱离数据库的IT项目几乎不存在,甚至可以说几乎没有不需要进行数据库操作的编程人员,而能与数据库进行无缝交互的就只有SQL了。此外,SQL是一种学起来非常容易的“傻瓜语言”,随便一个where条件就是一个需求实现,基本上新手级别的开发人员坐下来看看简单语法即可编写SQL,如果有3天时间边做边学,基本上所有SQL都会编写了。用我本人的例子来说吧,有人忽然问我学SQL开发学了多久,我几乎是本能般从嘴里冒出一句:SQL开发,我有花时间学吗,写SQL难道不是自然而然就会了吗?
正因为SQL如此重要,学习成本又如此之低,同时与IT系统中不可或缺的数据库交互起来浑然天成,所以几乎所有Java、C等开发人员都能较熟练应用数据库SQL开发技术。这导致应用SQL开发的人在数量上异常庞大,简单地说,就是所有前后端程序开发人员和IT运维人员以及数据库开发人员的总和!
于是在高访问量、高并发的IT系统数据库模块中,平均每秒运行成千上万条SQL的场景已成常态。在这种情况下,这些SQL如果运行较慢,便容易迅速拖垮整个IT系统,因此SQL优化就变得特别重要了。此外,由于SQL过多,不可能仅靠一两个SQL专家筋疲力尽地调优,我们便可以高枕无忧了。最有效的方式是,每个SQL编写人员自己要有SQL优化的意识和本领!
老师,我在项目组里做Java开发,确实也涉及大量的SQL开发,您说得没错,我也感觉SQL开发特简单,不过我的SQL经常跑得很慢,您说SQL优化好学吗?
结论:数据是IT系统的核心、重中之重,而SQL是数据交互的必然手段,所以SQL的应用非常广泛,使用人群数量也非常庞大,因此SQL的重要性不言而喻!
涌SQL之优化泪水
SQL优化肯定比SQL编写本身要难很多,但也存在一些优化的基础知识,如SQL执行计划、索引原理,等等。这些都比编写SQL本身要复杂得多,因此要成为SQL优化高手仅知道一些优化基础知识是远远不够的,还需要经验的沉淀,并且要转化成你的方法论。
老师,您能否举例说明一下需要什么经验吗?
不妨我回答得更有趣点吧,先给你说几个SQL优化的小故事,其中奥妙我们后续探讨。
太好了!
嗯,你边听边思考。故事1:话说某天上午小王被告知某系统的一个菜单访问非常慢,于是他开始介入优化,他跟踪到该菜单调用的具体SQL,接下来他通过观察该SQL的执行计划发现该SQL访问某张表时没走索引,觉得该表应该加一个索引,他花了整整1个小时发现这个问题后非常兴奋,于是马上动手开始建索引了。建索引大概花费了几分钟时间,随后他发现SQL走索引了,并且真的快了许多。于是测试一下该菜单,果然快了不少。故事1讲完了。
啥,讲完了,太短了吧,您这说的是优化成功案例吗?
是否成功一会儿再下结论,接下来说故事2。话说小王优化效果杠杠的,正自鸣得意时,被告知虽然这个菜单变快了,不过刚才持续几分钟时间出现访问该菜单一直报错的情况,随后正常。
老师,这啥意思,为啥应用程序会出错,怎么就恢复了?您故事2讲完了吗,怎么您的故事都这么短啊?
嗯,现在开始说第3个故事了。当天下午小王又接到电话,被告知那菜单访问又慢了。小王有点吃惊,于是赶紧登录系统运行该SQL,发现确实变慢了,不过奇怪的是该SQL正常走索引,没什么问题啊,小王很是郁闷。正在他束手无策之时,小王又接到电话,说该菜单又快了。晕,他现在可是啥都没做,这是咋回事?当晚,小王辗转反侧,无心睡眠。第2天小王又接到电话……他崩溃了。
好可怜啊。然后呢?
继续第4个故事。话说小王崩溃之后无法正常工作,于是领导把这难题交给其他人处理了,小王得知后满血复活再度投入工作中。几天后小王又迎来一个新任务,开发项目组中有一条SQL很慢,希望能优化一下。小王看了一眼觉得写得歪瓜裂枣样很不舒服,于是挽起袖子对SQL进行重写,改改改!
然后呢?
小王改好后,满怀期待的开发组对新的SQL一运行,发现跑得比之前的SQL还要慢很多!
可怜,小王又崩溃了吧,接下来呢?
嗯,说故事5吧。在小王再度崩溃之前,公司的SQL优化大师老丁正好路过,他分析了之后,建议将SQL语句涉及的某表的外键加上索引。后来大家照办了,果然性能迅速提升!老丁告诉小王,优化前可通过各种手段先观察观察SQL涉及的表结构、索引等,看它们有无不合理之处,急于动手改造SQL太盲目了,是没有抓住主要矛盾的体现,而且改代码需要测试、打补丁、上线,也是一件很辛苦的事。小王频频点头,谨记老丁教诲。
看来小王成长了。
是吗?那我们继续说故事6。
一周后小王又面对一条SQL需要优化,这次他不动手改写了,尝试了加索引,又尝试了调整表结构,结果提升效果非常不明显……
然后呢,又崩溃了?
好在老丁又及时出现了,他这次没有修改表结构,而是和开发人员进行了半小时的交谈,然后居然对SQL进行重写,改改改!然后 SQL就变得飞快了。小王傻眼了,不是说尽量不着急先动手改SQL吗?怎么老丁一看到这SQL就改改改。悲催啊,该怎么做才是对的!让小王悲催的还不止这个,老丁的新SQL看上去并不是很复杂,可是小王居然看不懂老丁为什么能这么改写。
没错,看不懂!他看不懂老丁写什么,也完全不理解新旧SQL的等价性。
可怜的人!老师,我觉得不是“一入IT深似海,从此菜鸟泪成河。”而是“一入SQL深似海,从此优化泪成河。”
结论:SQL优化不是一件容易的事,明明优化后变快了,结果不一会儿又慢了。有时不改写可以解决问题,有时又必须要改写才能解决问题。让人难以适应。
析SQL之悲催故事
你故事听完了,啥感想,就是觉得小王好惨,优化很难吗?
嗯,差不多吧。
好吧,让我来解读一下这些故事吧。故事1里小王能定位到具体SQL,并且能根据SQL执行计划进行SQL优化,这至少说明了小王还是掌握了一定的SQL优化基础技能的,否则估计执行计划是什么都没有听过。不过接下来的故事2和故事3说明他是失败的。为啥呢?那是因为他经验太少,同时也缺乏由经验转换而成的做事方法论。
我是怎么下的这个结论呢?你注意到没,小王他接到电话后就开始动手优化了。他难道不应该多问一句这问题是一直以来就存在呢,还是今天忽然出现的。
老师,问这有啥用呢?
如果是第一次出现,你可能就会关注一下是否昨晚系统做了啥动作,比如昨晚打了一个补丁,这补丁引发这次故障的可能性就非常大,于是目标就很清晰了。在实在无法解决的情况下,回退补丁也是一个思路。而如果是经常有这故障,那应该有其他同事处理应对过,获取他们之前的分析成果,或者收集之前的日志和现在进行比对,这些难道对小王没帮助吗?
老师,事实上是不是因为晚上打补丁导致的?
你可以认为是,也可以认为不是。我说明一下,这几个故事没有真相,只是解读一下。真相不重要,你猜测的过程就是你进步的过程。另外这里小王是加了一个索引,你觉得这个动作是对的还是错的?
老师,我觉得应该是错的,因为在第3个故事里,他加索引后,系统虽然前期变快,但是后来又慢了。我觉得这个加的索引没有用,或者至少是没有解决全部问题。不过我就是奇怪为啥时快时慢?
这没啥奇怪的,我们还是进行假设。你不觉得可能有这么一种情况,此时整个系统都非常地慢,根本不只是这个菜单慢。求助者没有提出其他模块慢或许只是因为他平时仅用这个菜单,那你想想,如果整个平台都瘫痪了,局部能快起来吗?
哦,这我怎么一点都没想到啊!
嗯,我只是说可能性。那为什么整个数据库都慢?可能性更多了,比如数据库主机上某些应用程序耗尽了主机的CPU、内存等资源,数据库运行在这台主机上,覆巢之下安有完卵? 接下来解释为啥忽快忽慢问题,正常啊,如果这些害人的应用程序有时运行,有时结束,结束时数据库不就正常了?
原来如此啊,第3个故事里的小王崩溃得好冤啊,如果知道事实的真相,他把数据库主机的程序停了或者优化了或者移到别的地方去,就解决问题了。
你的这个解决方案非常棒,不过别忘了我这里不说真相,一切都是可能性解读。当然这些可能性猜测是基于经验和推理的,也不是完全没依据的。另外,故事2里描述菜单曾经出错了几分钟,你注意到并思考过为什么了吗?
有的,不过这是为啥呢?
你没注意到建索引也是几分钟时间吗?几乎可以肯定地说,是这个建索引动作导致的故障。因为建索引会锁全表,这时候更新数据肯定会失败。小王犯了一个大错,在业务高峰期做DDL操作,严重地影响了生产,问题解决者成了麻烦制造者。这故事2倒是不需要推测的,真相一定如此。
哇,这么严重的错误原来是小王一手造成的,我还真没想到。
嗯,再说故事4吧,小王动手改改改,然后效果差差差,这里我们可以解读到什么呢?那就是解决问题要有目的性,不能没找到真正原因盲目动手。你看他觉得SQL写得不好,就改。实际上应该观察慢在什么地方,比如可以通过执行计划看出最大的开销在某全表扫描上,而该全表扫描完全可以通过索引减少访问路径,这时加索引就可以解决问题,改写SQL不可能解决问题的。
哦,确实如此。
接下来,在故事5里,老丁果然只是增加了索引后就解决了性能问题。优化SQL不一定非要改写才可以优化,有时根据数据库的体系逻辑结构不改写SQL也可完成优化。改写SQL的代价也更高,因为现实中如果你要改SQL肯定需要经过测试、打补丁、上线等多个过程,不可能直接就在生产中修改。
最后看故事6,小王学习老丁只调整参数进行SQL优化。接下来剧情反转了,小王学老丁不改,效果差差差。而老丁则动手改改改,效果又是杠杠的。小王欲哭无泪,不知自己该如何做了。其实这里小王生搬硬套了,他没有找到本质原因,比如此时可能是由于冗长的写法导致表访问了多次,而老王改写SQL将表访问次数大幅度降低下来。这时不改写是无法优化的。又或者是老丁根据业务需求,砍掉了某些多余的逻辑,这就更需要改写SQL了。
哦,老师您总结得很到位啊!
由于不改写通常来说比改写高效,而不改写的优化一般都和数据库的体系逻辑架构有关。因此我们需要认真学习这部分基础知识,这也就是老师的SQL优化课程中为什么会涉及这部分知识的原因。不过能不改写优化固然好,有时等价改写也是必需的,而且改写其实分成两个部分:一个是等价改写;一个是根据业务改写。比如小王看某SQL写得很不顺眼,然后动手改改改,这显然是等价改写。而老丁和开发人员交谈了半个多小时,改造后的SQL连小王都看不明白,这就很可能是根据业务改写的。
明白了,原来如此。
业务改写是优化的最高境界,老丁通过和开发人员交流后发掘出真正的需求,然后写出来的代码表面上看和旧代码逻辑完全不等价,实际却等价。
哦,老师您能否解释一下什么叫真正需求?
好吧,还是讲生动点的例子吧。从前我曾经讲过一个《小余买鱼》的故事。小余妈妈一个劲地让小余买鱼招待客人,条件不允许的时候还坚持如此。后来小余让妈妈用冰箱里的牛肉来代替鱼,妈妈也猛然醒悟了,她忘记了需求的本质是做美味给大家分享。站在做美味这角度上看,去老远的地方买鱼和拿冰箱里的牛肉,又有什么区别呢?
我明白了,没有提炼到这层需求的人,真的很难理解吃牛肉和吃水煮鱼其实是一样的,代码改写前后表面上有这么大差距,也难怪小王会大惑不解。
是的,这里顺便再说一点,小王看不懂老丁写的SQL,也可能是因为老丁用了某些可能小王没见过的SQL语法来改写,我们这里称之为高级SQL。比如WITH子句、树形递归、分析函数,等等。
哦,好想见识一下。
结论:其实这一节想说的就是,做事要有方法论,要先整体后局部,解决问题要注重效率,先尽量考虑不改写的优化,再考虑改写的优化。而不改写的优化靠的是体系结构知识的沉淀,而改写则要考虑逻辑等价改写和业务改写两大思路,其中业务改写是SQL优化的最高境界。另外还是要有一定的知识沉淀,高级SQL的语法也要掌握,其在很多场合下能帮上我们大忙。
推规范流程之必要
其实小王的系列故事还暴露了他身上一个非常重要的缺陷,那就是做事缺乏流程、缺乏规范。先不说解决问题的方法论,小王在系统中建了索引后导致系统出现问题,就是犯了一个不规范操作的低级错误。这应该在DBA的工作流程中是明确禁止操作的。当然小王有权限做这件事也是值得深究的,这种权限是不是要管控得更严格一些?这里涉及了作业流程规范。
接下来小王手忙脚乱地进行优化,是不是都很有序?他要花一小时时间才定位到某处需要建立索引,如果建索引有依据的规范并且能快速被体检报告诊断出来,他需要花一小时吗?这里涉及了数据库的设计规范。
故事中还涉及SQL改写优化的过程,如果性能低下的SQL在运行之前能被事先捞取出来,则很多故障就能避免。这里涉及SQL写法的规范。
规范不仅重要,我们更要主动去履行。如果我们能一键发现权限不当、数据库设计不良、SQL写法糟糕的问题,那规范就容易遵守,而不是停留在嘴上。不过放心,我们的数据库整体诊断工具,涵盖了这里大部分的规范检查。
结论:不成规矩,无以成方圆,如果能制定一定的规范并进行有效的检查,系统的性能问题必然会大幅减少。这里有一个好消息,就是这些要点大多都涵盖进笔者的一键获取数据库整体信息的脚本中了。