對(duì)軟件系統(tǒng)的一些理解(對(duì)軟件系統(tǒng)的一些理解怎么寫(xiě))
前言
這篇文章是想表達(dá)我對(duì)系統(tǒng)軟件的一些理解,風(fēng)格跟之前的不太一樣,整體偏“務(wù)虛”。我自己其實(shí)是不太擅長(zhǎng)“務(wù)虛”的,甚至是有點(diǎn)排斥。就跟相比起看論文,我更喜歡看code,當(dāng)然我也看論文,只不過(guò)相對(duì)來(lái)說(shuō)少些。 畢業(yè)以來(lái)一直在數(shù)據(jù)庫(kù)存儲(chǔ)引擎領(lǐng)域工作,過(guò)去5年主要精力集中在阿里自研LSM-Tree存儲(chǔ)引擎X-Engine研發(fā)上,并且在過(guò)去兩年多時(shí)間我們完成了X-Engine的云原生架構(gòu)升級(jí)和商業(yè)化,在公有云上承接一定規(guī)模的客戶并穩(wěn)定運(yùn)行,在業(yè)界應(yīng)該也是首個(gè)基于LSM-Tree架構(gòu)實(shí)現(xiàn)云原生能力的TP存儲(chǔ)引擎。完整經(jīng)歷一個(gè)TP存儲(chǔ)引擎的架構(gòu)規(guī)劃、設(shè)計(jì)研發(fā)、落地上線,穩(wěn)定性運(yùn)維的全周期,并且得益于從我進(jìn)入數(shù)據(jù)庫(kù)領(lǐng)域一路以來(lái)經(jīng)歷的高水平團(tuán)隊(duì)、technology leader以及整個(gè)團(tuán)隊(duì)成員的出色工程能力和技術(shù)視野,加上我自己在此過(guò)程中的一些思考,階段性的形成了一些自己的心得體會(huì)。 另外,跟業(yè)界一些優(yōu)秀的架構(gòu)師和工程師交流,發(fā)現(xiàn)對(duì)于系統(tǒng)工程的理解有很多的共鳴,也收到很多非常有價(jià)值的輸入,當(dāng)然也存在一些不同的觀點(diǎn)。這也是促使我寫(xiě)這篇文章的主要原因,希望能將我自己的一些理解表達(dá)清楚,這些觀點(diǎn)并不fashion,更談不上創(chuàng)新,更多的是一些自己的思考和經(jīng)驗(yàn)之談。
對(duì)系統(tǒng)軟件的看法
觀點(diǎn)1:軟件的本質(zhì)是對(duì)硬件資源的消耗。不同軟件的區(qū)別在于,消耗硬件資源去解決什么問(wèn)題以及如何分配硬件資源的消耗。軟件架構(gòu)設(shè)計(jì)中經(jīng)常提到"抽象"和“trade-off”,抽象本質(zhì)上的就是"解決什么問(wèn)題","trade-off"其實(shí)就是"如何分配硬件資源"。 舉個(gè)例子TP存儲(chǔ)引擎和AP存儲(chǔ)引擎,從實(shí)現(xiàn)上可以列舉出一大堆不同的地方,行存 VS 列存、二級(jí)索引 VS ZoneMap索引、強(qiáng)事務(wù) VS 弱事務(wù)等等。這些不同之處其實(shí)都是結(jié)果,導(dǎo)致這些的根本原因是:
1)兩者解決的問(wèn)題不同,TP場(chǎng)景主要是online實(shí)時(shí)業(yè)務(wù),這些業(yè)務(wù)的特征是整體數(shù)據(jù)規(guī)模相對(duì)較小(真正需要online處理的數(shù)據(jù),歷史數(shù)據(jù)可能很多)、請(qǐng)求短平快、數(shù)據(jù)locality明顯、高并發(fā)低時(shí)延等,而AP場(chǎng)景整體的數(shù)據(jù)規(guī)模大、計(jì)算密度高、高吞吐等。(解決什么問(wèn)題)
2)TP引擎的完整事務(wù)支持使得業(yè)務(wù)的并發(fā)控制簡(jiǎn)化很多,其實(shí)就是把業(yè)務(wù)系統(tǒng)本來(lái)需要做的事情,TP引擎自己做了,當(dāng)然也就意味著TP引擎需要為此消耗一部分硬件資源。而AP引擎為了加快數(shù)據(jù)入庫(kù)的速度,事務(wù)的支持比較弱,這部分工作還是由業(yè)務(wù)系統(tǒng)來(lái)完成(比如ETL),也就不需要為此消耗硬件資源。(如何分配硬件資源) 觀點(diǎn)2: 系統(tǒng)軟件的重大變革,背后基本都是硬件發(fā)展所推動(dòng)的。這跟觀點(diǎn)1)是相呼應(yīng)的,系統(tǒng)軟件領(lǐng)域的理論在進(jìn)入21世紀(jì)之前,學(xué)術(shù)界已經(jīng)做了廣泛深入的研究。從最開(kāi)始計(jì)算機(jī)的出現(xiàn),到大型機(jī)和小型機(jī),再到家庭PC和廉價(jià)通用服務(wù)器,以及現(xiàn)在的云計(jì)算IAAS服務(wù),基本上系統(tǒng)軟件發(fā)展也是跟隨這個(gè)脈絡(luò)在發(fā)展。系統(tǒng)軟件的再次火熱,本質(zhì)上也是因?yàn)镮AAS這個(gè)“新硬件”所推動(dòng)的。整個(gè)IAAS的on-demand獲取,打破了系統(tǒng)軟件之前在物理資源受限的背景下很多設(shè)計(jì),這也就是為什么云原生系統(tǒng)軟件會(huì)迎來(lái)新的機(jī)會(huì)。 觀點(diǎn)3: 幾乎不存在某一種系統(tǒng)架構(gòu)全面領(lǐng)先另外一種架構(gòu)。這跟觀點(diǎn)1)2)是相呼應(yīng)的,不同的架構(gòu)選擇背后都是不同的trade-off,所謂有得必有舍。經(jīng)常聽(tīng)到一些說(shuō)法,你看這篇論文、這篇文章,他們這種架構(gòu)就沒(méi)有某問(wèn)題,我們這種架構(gòu)就有這個(gè)問(wèn)題。我聽(tīng)到這些觀點(diǎn)的第一反應(yīng)是質(zhì)疑,這里邊主要有三個(gè)原因:
1)很多論文和文章的實(shí)驗(yàn)結(jié)果是沒(méi)法復(fù)現(xiàn)的,也就說(shuō)很有可能他的結(jié)論就有問(wèn)題;
2)很多時(shí)候只會(huì)強(qiáng)調(diào)“得”的部分,而“舍”的部分是沒(méi)有講的。
3)我們系統(tǒng)所存在的問(wèn)題到底影響有多大,是不是可以解決的,這些需要量化的數(shù)據(jù)才能確定。輕易地被各種論文和文章的結(jié)論影響,很有可能會(huì)做出一個(gè)不倫不類(lèi)的系統(tǒng)。就像習(xí)武之人各個(gè)門(mén)派的武功都學(xué)學(xué),最終很容易走火入魔。 觀點(diǎn)4:條條大路通羅馬,最終系統(tǒng)對(duì)外呈現(xiàn)的區(qū)別,更多的是工程實(shí)現(xiàn)的原因,而非架構(gòu)的原因。不同的系統(tǒng)架構(gòu)需要解決的大部分問(wèn)題本質(zhì)上其實(shí)是一樣的,并且組成一個(gè)系統(tǒng)的零部件都差不多,只是根據(jù)需要選擇哪些零部件來(lái)構(gòu)建系統(tǒng)。只有躬身入局,真正地去面對(duì)問(wèn)題、分析問(wèn)題、解決問(wèn)題,才能認(rèn)清楚其中的本質(zhì),否則很容易變成紙上談兵。 舉個(gè)例子:經(jīng)常有人問(wèn)我LSM-Tree架構(gòu)中持續(xù)寫(xiě)入數(shù)據(jù)時(shí),compaction問(wèn)題對(duì)性能影響很大。這個(gè)問(wèn)題我是這么看的,首先LSM-Tree架構(gòu)上寫(xiě)入吞入優(yōu)勢(shì)的其中一個(gè)原因是,相比于innodb這種磁盤(pán)B Tree在寫(xiě)入的時(shí)候直接sort on write(page內(nèi)有序,全局有序),LSM-Tree架構(gòu)選擇將一部分sort轉(zhuǎn)移到sort on compaction、sort on read,本質(zhì)上是將寫(xiě)入時(shí)排序的資源消耗,轉(zhuǎn)移到了compaction或read。刷臟其實(shí)是包含兩個(gè)動(dòng)作:生成臟頁(yè),將臟頁(yè)刷盤(pán)。innodb相當(dāng)于是在寫(xiě)入的時(shí)候生成臟頁(yè),在刷臟的時(shí)候就是單純的io操作。而compaction其實(shí)是同時(shí)做了生成“臟頁(yè)”和“臟頁(yè)”刷盤(pán)。innodb如果持續(xù)寫(xiě)入的話,也會(huì)有刷臟來(lái)不及時(shí)導(dǎo)致影響寫(xiě)入性能的問(wèn)題。因?yàn)閕nnodb刷臟和compaction之所以成為問(wèn)題,本質(zhì)上都是因?yàn)閮?nèi)存和磁盤(pán)寫(xiě)入速度的差異,導(dǎo)致生產(chǎn)者消費(fèi)者模型失衡。所以innodb的刷臟和LSM-Tree的compaction本質(zhì)上是相同的問(wèn)題,只是通過(guò)不同的方法來(lái)將這個(gè)過(guò)程對(duì)系統(tǒng)的影響降到最低。
系統(tǒng)軟件構(gòu)建的七個(gè)面向
接下來(lái)的內(nèi)容,主要是在進(jìn)行詳細(xì)設(shè)計(jì)的時(shí)候我認(rèn)為比較重要的原則。這些原則的道理其實(shí)很容易理解,并且“軟件工程”這門(mén)學(xué)科已經(jīng)研究的很充分,但是實(shí)際操作的時(shí)候其實(shí)是蠻困難的,可能是歷史包袱的原因,也有可能是外界環(huán)境的原因,需要根據(jù)實(shí)際情況做出不同的trade-off。值得注意的是,我們做出的trade-off一定是要經(jīng)過(guò)仔細(xì)考慮的,而不是草率的,否則很容易出現(xiàn)“有舍沒(méi)有得”。另外遵守這些原則設(shè)計(jì)實(shí)現(xiàn)出來(lái)的系統(tǒng)和不完全遵守這些原則設(shè)計(jì)實(shí)現(xiàn)出來(lái)的系統(tǒng),結(jié)果其實(shí)是“好和更好的區(qū)別”,但是“好多少”這個(gè)量在系統(tǒng)做出來(lái)之前,其實(shí)很難衡量。這七個(gè)原則不是獨(dú)立存在的,而是相輔相成的。 面向場(chǎng)景: 首先我們需要明確要解決什么問(wèn)題,這是整個(gè)系統(tǒng)構(gòu)建的出發(fā)點(diǎn)。one size fit all的系統(tǒng)在過(guò)去是不存在的,在未來(lái)也不一定存在。系統(tǒng)的完善,必然是要靠不斷的迭代來(lái)完成的,那么如何迭代本質(zhì)上就是我們?cè)谀切╇A段解決哪些問(wèn)題。一個(gè)系統(tǒng)可以有遠(yuǎn)大的目標(biāo)去解決很多問(wèn)題,但是所有問(wèn)題的路標(biāo)需要有相對(duì)清晰的規(guī)劃,以達(dá)到既可以快速滿足需求,同時(shí)保留向未來(lái)演進(jìn)和擴(kuò)展的基礎(chǔ)。 實(shí)際研發(fā)過(guò)程中,可能發(fā)生的兩類(lèi)錯(cuò)誤是:
1)想采用敏捷開(kāi)發(fā)的方式來(lái)進(jìn)行工程管理,以滿足整個(gè)迭代的需求。敏捷開(kāi)發(fā)本質(zhì)上先定義最小功能集,也就是首先想清楚解決什么問(wèn)題,然后快速的迭代擴(kuò)充功能,有點(diǎn)像小步快走。在實(shí)操上,很容易把敏捷開(kāi)發(fā)搞成了"快、糙、猛",有點(diǎn)大干30天趕英超美的味道。
2)問(wèn)題定義不清楚,系統(tǒng)的“不變式”設(shè)置就容易草率。每個(gè)系統(tǒng)都有一些“不變式”,隨后很多設(shè)計(jì)都是基于這些不變式進(jìn)行展開(kāi)的,比如在LSM-Tree系統(tǒng)中一個(gè)常見(jiàn)的“不變式”是更新版本的數(shù)據(jù)在更低的層次,同一行的數(shù)據(jù)的多個(gè)版本如果同時(shí)在memtable、level0、level1中存在,那么必然memtable中對(duì)應(yīng)的版本是最新的,level0中的版本也比level1中的更新。如果在迭代的過(guò)程中發(fā)現(xiàn)之前設(shè)置的“不變式”不合理的,那么進(jìn)行改動(dòng)的代價(jià)是非常之大的。 面向解耦:無(wú)論是自上而下的去設(shè)計(jì)系統(tǒng),還是自下而上的去設(shè)計(jì)系統(tǒng),很重要的一個(gè)思考邏輯就是將各個(gè)模塊間的耦合度降到最低。解耦做地比較好的系統(tǒng),往往意味著:
1)每個(gè)模塊的功能是考慮的比較清楚,方案的完整度是比較高的;
2)有利于專(zhuān)注的將某個(gè)模塊實(shí)現(xiàn)的更加高效,避免其他模塊的影響;
3)有利于之后的迭代,影響面可控;
4)出了問(wèn)題好排查,單個(gè)模塊的問(wèn)題是比較好排查,真正那些難搞的問(wèn)題往往是問(wèn)題在各個(gè)模塊間傳導(dǎo)后才暴露出來(lái),比如A模塊出問(wèn)題,經(jīng)過(guò)模塊B、C、D,最后在模塊E暴露出來(lái)。 有些質(zhì)疑的觀點(diǎn)會(huì)說(shuō),面向解耦的思路去設(shè)計(jì),有可能會(huì)犧牲系統(tǒng)的整體性能。其實(shí)這個(gè)跟不要一開(kāi)始就為性能做過(guò)度的設(shè)計(jì)是一樣的道理,真到了某些解耦的設(shè)計(jì)影響了性能,那么該耦合的就去耦合。把兩個(gè)模塊耦合在一起的難度往往是低于把耦合在一起的兩個(gè)模塊拆開(kāi)。 面向防御:這個(gè)就是防御性編程的邏輯,要假設(shè)調(diào)用的函數(shù)都是有可能出錯(cuò)的, ,比如內(nèi)存分配可能出錯(cuò),io可能出錯(cuò),基礎(chǔ)庫(kù)的調(diào)用可能出錯(cuò)等等,基于此來(lái)考慮如果出錯(cuò),系統(tǒng)的行為是什么。有一個(gè)非常簡(jiǎn)單的原則就是"fail stop", 如果沒(méi)有完整的防御,那么即使fail了也很難立即stop,最終造成一些很奇怪的表象。 通常的質(zhì)疑是:
1)你看這個(gè)函數(shù)的邏輯肯定不會(huì)失敗的。也許從當(dāng)前來(lái)看這個(gè)函數(shù)確實(shí)不會(huì)失敗,但是很難保證隨著迭代增加邏輯,之后沒(méi)有失敗的可能性。
2)加了這么多防御,防御代碼比實(shí)際邏輯的代碼還多,會(huì)影響性能。首先,現(xiàn)在cpu的分支預(yù)測(cè)能力,基本上可以做到絕大部分情況下防御代碼不會(huì)影響性能。另外跟對(duì)于面向耦合的質(zhì)疑一樣,真到某些防御代碼成為了性能瓶頸,該優(yōu)化就優(yōu)化。優(yōu)化一個(gè)防御,總比去解決一個(gè)因?yàn)闆](méi)有防御而導(dǎo)致的問(wèn)題代價(jià)更低吧。 面向測(cè)試:在測(cè)試階段修復(fù)問(wèn)題的代價(jià)是遠(yuǎn)低于在生產(chǎn)環(huán)境修復(fù)問(wèn)題的代價(jià),因此讓系統(tǒng)變得可測(cè)試是非常重要的。系統(tǒng)可測(cè)試的標(biāo)準(zhǔn)就是,能方便的進(jìn)行單元測(cè)試、集成測(cè)試,并覆蓋絕大部分的代碼路徑??蓽y(cè)試的系統(tǒng),隨著不斷的迭代,會(huì)累積越來(lái)越多的測(cè)試case,不斷的夯實(shí)穩(wěn)定性基礎(chǔ)。面向測(cè)試跟面向解耦、面向防御是相輔相成的。只有模塊間耦合度足夠的低,才有可能做更多的測(cè)試,否則做一個(gè)模塊的測(cè)試需要mock很多亂七八糟的東西。面向防御會(huì)使得測(cè)試的行為可以更好的預(yù)期,不然輸入了一個(gè)異常的參數(shù),具體怎么失敗是不確定的,那測(cè)試case就很難寫(xiě)了。 面向運(yùn)維:bug是一定會(huì)有的,對(duì)于復(fù)雜的系統(tǒng),不管前期做多少準(zhǔn)備都很難避免生產(chǎn)環(huán)境中遇到未知的問(wèn)題。面向運(yùn)維的主要目的是,遇到問(wèn)題的時(shí)候,能用代價(jià)最低的手段去及時(shí)止損。遇到線上問(wèn)題,動(dòng)態(tài)調(diào)參數(shù)就能解決比需要重啟才能解決的代價(jià)更低,重啟能解決比需要發(fā)版才能解決的代價(jià)更低。面向運(yùn)維不僅僅是加幾個(gè)參數(shù),加幾個(gè)開(kāi)關(guān)那么簡(jiǎn)單,而是需要把“面向運(yùn)維”作為設(shè)計(jì)方案的重要組成部分來(lái)考慮,保證出了問(wèn)題有運(yùn)維手段,有運(yùn)維手段敢用,用了以后有效果。 面向問(wèn)題本質(zhì):當(dāng)去解決一個(gè)問(wèn)題的時(shí)候,一定要多思考這個(gè)問(wèn)題的本質(zhì)原因是什么,簡(jiǎn)單的問(wèn)題復(fù)雜化和復(fù)雜的問(wèn)題簡(jiǎn)單化,都是因?yàn)闆](méi)有抓住本質(zhì)。如果能思考清楚其背后的本質(zhì)原因,從源頭避免掉是更加徹底的解決方式,否則很容易陷入不斷打補(bǔ)丁的狀態(tài),我一直有個(gè)觀點(diǎn):“沒(méi)有抓住問(wèn)題本質(zhì)去解決問(wèn)題,結(jié)果往往是在制造問(wèn)題”。另外一個(gè)經(jīng)驗(yàn)是,如果一個(gè)模塊連續(xù)出了好幾次問(wèn)題,那么就要想想是不是在最開(kāi)始的設(shè)計(jì)上就有需要改進(jìn)的地方。 面向可視化:可視化的目標(biāo)主要是以更加直觀的形式,來(lái)展現(xiàn)系統(tǒng)運(yùn)行狀況,這對(duì)于系統(tǒng)調(diào)優(yōu)和診斷是非常重要的。當(dāng)系統(tǒng)異常時(shí),可視化的方式可以幫助快速定位到系統(tǒng)哪里出了問(wèn)題。另外一方面是,可以提供接口給監(jiān)控系統(tǒng)做歷史狀態(tài)的追蹤。比如oracle的診斷監(jiān)控就是一個(gè)非常優(yōu)秀的案例,而SnowFlake對(duì)于內(nèi)部狀態(tài)的打點(diǎn)監(jiān)控也是近乎瘋狂。
總結(jié)
說(shuō)了這么多,最終系統(tǒng)還是靠一行行的code實(shí)現(xiàn)出來(lái)的,保持匠心、嚴(yán)謹(jǐn)、較真的態(tài)度去打造系統(tǒng)是非常樸素正確,但又很難做到的事情,共勉!
原文鏈接:http://click.aliyun.com/m/1000349863/
本文為阿里云原創(chuàng)內(nèi)容,未經(jīng)允許不得轉(zhuǎn)載。