序
這是自上一本《收獲,不止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寫法糟糕的問題,那規范就容易遵守,而不是停留在嘴上。不過放心,我們的數據庫整體診斷工具,涵蓋了這里大部分的規范檢查。
結論:不成規矩,無以成方圓,如果能制定一定的規范并進行有效的檢查,系統的性能問題必然會大幅減少。這里有一個好消息,就是這些要點大多都涵蓋進筆者的一鍵獲取數據庫整體信息的腳本中了。