<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          25.五字優(yōu)化訣

          共 7323字,需瀏覽 15分鐘

           ·

          2021-07-03 14:47

          1 被催更了

          上...上周六,之下午,驕陽(yáng)似火,晴空萬(wàn)里...無(wú)云。

          正想吃完瓜刷個(gè)劇呢,突然看到有人加我微信,心里一喜,難道終于有美女愿上當(dāng)了?

          點(diǎn)開(kāi)一看,發(fā)現(xiàn)果然不是。好吧,勉強(qiáng)通過(guò),一聊,竟然是讀者來(lái)催更的。

          我竟然被催更了。

          竟然被催更了。

          被催更了。

          這是逼我出大招啊,看來(lái)我最新研究的優(yōu)化秘籍是藏不了多久了。

          然而,這篇文章的出貨難度竟然出乎意料的大,這一寫(xiě),竟是兩周。


          2 五字優(yōu)化訣:持續(xù)分煎餅

          本篇關(guān)注程序性能優(yōu)化。聚焦這個(gè)主題,本是偶然。始于玩笑,終于本心,也算是為我黨成立百年獻(xiàn)禮了。本想找點(diǎn)高大上的讓人直呼牛逼的東西,奈何能力有限,只能給大家一些既便宜、又好用、還簡(jiǎn)單的普通東西了,不知道你們會(huì)不會(huì)喜歡。

          分為五個(gè)主題,分別是『池』『序』『分』『減』『并』:


          3 『池』字訣

          池化,降低可重用對(duì)象的創(chuàng)建和回收代價(jià)。

          不知道你們發(fā)現(xiàn)沒(méi)有,無(wú)論是電影還是游戲中,主角總是孤單英雄,最多三五成群。但Boss不一樣,Boss手一揮,必須有一群小怪一擁而上,畢竟幫主角刷點(diǎn)經(jīng)驗(yàn)也是好的。

          小怪的特點(diǎn)是:數(shù)量多,容易死,循環(huán)用。電影不可能請(qǐng)?zhí)嗟娜貉荩虼宋覀兘?jīng)常能發(fā)現(xiàn)一人分飾多角的超級(jí)龍?zhí)?。而游戲里,也不可能每一個(gè)小怪都完全不一樣,因?yàn)閯?chuàng)建它們還挺消耗時(shí)間和內(nèi)存的。哈哈,現(xiàn)在你知道了,你正在打的小怪很可能與剛剛死前掉金幣的那只是同一只。

          代碼中,如果某些對(duì)象有重用的價(jià)值,并且創(chuàng)建的時(shí)候會(huì)消耗大量的CPU或IO資源。那么在出現(xiàn)性能瓶頸的時(shí)候,一個(gè)合理的優(yōu)化方向就是池化。剛剛例子中,對(duì)游戲中小怪進(jìn)行池化,通常稱(chēng)為對(duì)象池,類(lèi)似的還有線程池、連接池等。

          灰太狼:我一定會(huì)回來(lái)的~


          4 『序』字訣

          順序讀寫(xiě),減少隨機(jī)IO,減少cache miss。

          『內(nèi)存順序讀寫(xiě)的性能要遠(yuǎn)好于隨機(jī)讀寫(xiě)』,『磁盤(pán)順序讀寫(xiě)的性能要遠(yuǎn)好于隨機(jī)讀寫(xiě)』。類(lèi)似的話我相信很多程序員都似曾相識(shí),然而,我同樣相信很多程序員在寫(xiě)代碼的時(shí)候從來(lái)沒(méi)有認(rèn)真考慮過(guò)這件事件。

          當(dāng)年做游戲的時(shí)候,我見(jiàn)過(guò)很多人使用hash表存儲(chǔ)場(chǎng)景中的各類(lèi)對(duì)象:花鳥(niǎo)魚(yú)蟲(chóng)、白云蒼狗,并每幀遍歷hash表以確定位置或攻擊信息。我建議他們改成遍歷有序表的快照(MVCC了解一下),其中一個(gè)原因是可以提高遍歷性能,而另一個(gè)更重要的原因是可以在遍歷的過(guò)程中修改表結(jié)構(gòu)(插入刪除對(duì)象)。

          有序表一種基于有序數(shù)組的字典結(jié)構(gòu),C#中有一個(gè)名為SortList的標(biāo)準(zhǔn)庫(kù)實(shí)現(xiàn)


          多年以來(lái),CPU一直是計(jì)算機(jī)中跑得最快的部件,也因此被慣出來(lái)一個(gè)多吃多占的毛病。無(wú)論是讀內(nèi)存還是讀磁盤(pán),從來(lái)不講究按需分配,而是大塊大塊的拿數(shù)據(jù)。當(dāng)把一大塊連續(xù)的數(shù)據(jù)拿到手里的時(shí)候,CPU自己也知道一次肯定吞不下,但總覺(jué)得多吞幾次就好了。但這個(gè)特點(diǎn)其實(shí)是需要程序員去配合的,如果代碼使用連續(xù)內(nèi)存的數(shù)據(jù)結(jié)構(gòu),比如array,那在遍歷的時(shí)候就相當(dāng)于投其所好;而如果代碼使用hash表,則遍歷的時(shí)候cache miss的可能性就大大增加。

          鑒于java在服務(wù)器領(lǐng)域的成功,有些公司使用java開(kāi)發(fā)游戲服務(wù)器,我建議他們換一種語(yǔ)言。原因是游戲服務(wù)器很可能需要處理大量跟點(diǎn)、向量等三維空間相關(guān)的運(yùn)算,而在java中,默認(rèn)一切都是對(duì)象。于是在一個(gè)Vertex(頂點(diǎn))數(shù)組中,看似連續(xù)的Vertex對(duì)象在物理內(nèi)存中實(shí)際上是離散的,這樣的遍歷效果就會(huì)差很多。而對(duì)C++, Golang,甚至C#這類(lèi)語(yǔ)言來(lái)說(shuō),它們都支持struct。當(dāng)把Vertex定義為struct的時(shí)候,一個(gè)Vertex數(shù)組的占用的內(nèi)存就是連續(xù)的,遍歷效果會(huì)比java好很多。

          在后端的技術(shù)棧中,kafka絕對(duì)是一個(gè)非常另類(lèi)的存在。擅長(zhǎng)kafka的人,覺(jué)得無(wú)它不歡,而不了解它的人,則覺(jué)得可有可無(wú)。關(guān)于kafka為什么這么快的討論有很多,其中有一個(gè)繞不過(guò)的原因是:kafka會(huì)順序讀寫(xiě)磁盤(pán)。我們通常認(rèn)為磁盤(pán)的讀寫(xiě)性能遠(yuǎn)低于內(nèi)存,但實(shí)際上,在關(guān)閉fsync的前提下,SSD固態(tài)硬盤(pán)的順序讀寫(xiě)速度與內(nèi)存的隨機(jī)讀寫(xiě)速度是相當(dāng)?shù)模蟾哦际? GiB/s,而如果是隨機(jī)讀取,則SSD固態(tài)硬盤(pán)SSD的Seek速度會(huì)直降到70MiB/s,速度下降到1/15。


          5 『分』字訣

          鴻蒙伊始,民智不開(kāi)。為教化萬(wàn)民,神器計(jì)算機(jī)降世。下凡走得急,零件沒(méi)湊齊。計(jì)算機(jī)天生殘疾,CPU與其它IO部件速度差異過(guò)大。為平衡這種速度差異,人間有智者布局各方,分字訣應(yīng)劫而生,它們是分批、分幀、分頁(yè)、分時(shí)、分片、分區(qū)、分庫(kù)、分表、分離

          1 分批≈緩存+緩沖

          在系統(tǒng)設(shè)計(jì)之初,每次修改對(duì)應(yīng)一次IO可能是最簡(jiǎn)單、最直接的設(shè)計(jì),不過(guò)隨著系統(tǒng)流量變大,IO可能會(huì)很快成為系統(tǒng)瓶頸。相比于內(nèi)存操作來(lái)說(shuō),磁盤(pán)IO吞吐小,網(wǎng)絡(luò)IO延遲大。為了減少I(mǎi)O與內(nèi)存之間的速度差異,爭(zhēng)取每次IO都物盡其用才是上上之選。將多次IO合并為一次,減少磁盤(pán)IO(特別是fsync)的次數(shù)和網(wǎng)絡(luò)IO的round-trip次數(shù)。

          所謂讀優(yōu)化靠緩存,寫(xiě)優(yōu)化靠緩沖,而分批≈緩存+緩沖。往小了說(shuō),緩存就是一塊用于讀內(nèi)存,緩沖就是一塊用于寫(xiě)的內(nèi)存。往大了說(shuō),緩存就是Redis,緩沖就是Kafka。這些都是在微服務(wù)體系中司空見(jiàn)慣的招數(shù),不用我多說(shuō)。

          分批優(yōu)化化身千萬(wàn),可謂無(wú)孔不入,甚至多數(shù)流行的數(shù)據(jù)中間件都提供了批量IO操作的API:

          1. MySQL的insert語(yǔ)句支持在values中一次插入多行數(shù)據(jù)。

          2. Redis的Pipeline操作可以批量執(zhí)行多個(gè)操作。

          3. ElasticSearch有專(zhuān)門(mén)的_bulk api支持在單次調(diào)用中索引或刪除多條數(shù)據(jù)。

          4. Kafka更是把分批操作貫徹到了極致:它根本就不提供發(fā)送單條消息的功能,使用send()發(fā)送的單條消息其實(shí)會(huì)被Kafka偷偷在內(nèi)存里攢起來(lái),是的你被騙了。

          5. 包括MySQL, Redis在內(nèi)的各類(lèi)數(shù)據(jù)中間件,跟WAL日志落盤(pán)時(shí)機(jī)相關(guān)的配置中,一定有一條是只寫(xiě)Page Cache,然后后臺(tái)線程定期刷盤(pán)的策略。(注:嚴(yán)格說(shuō)來(lái),Redis那個(gè)不能叫WAL,因?yàn)樗菍?xiě)后日志。)

          6. 在游戲引擎中,把頂點(diǎn)數(shù)據(jù)提交的顯卡的Draw Call也都是合并批次的,否則會(huì)卡死你。


          分批的缺點(diǎn)是在數(shù)據(jù)一致性差,無(wú)論是緩存還是緩沖都存在同樣的問(wèn)題。緩存的話,如果不存在嚴(yán)格的一致保障手段,往往只建議展示用,不作為數(shù)據(jù)修改依據(jù)。參考文章《緩存就像showgirl,看看就行了》。而緩沖在內(nèi)存的這段時(shí)間內(nèi),如果進(jìn)程崩潰了,會(huì)丟失部分?jǐn)?shù)據(jù)。因此在選擇分批優(yōu)化的時(shí)候,架構(gòu)師需要仔細(xì)斟酌數(shù)據(jù)一致性問(wèn)題,比如是否可以接受偶爾丟失數(shù)據(jù),或者是否需要在數(shù)據(jù)輸入端提供重試策略。

          但不管如何,分批都可能是最重要的IO優(yōu)化手段:它邏輯簡(jiǎn)單,不涉及多線程并發(fā),對(duì)數(shù)據(jù)結(jié)構(gòu)沒(méi)有特殊的要求,系統(tǒng)改造成本低??梢哉f(shuō),無(wú)論何時(shí)遇到IO性能瓶頸,分批改造都應(yīng)該是順位第一的候選方案。


          2 分幀、分頁(yè)、分時(shí)

          分幀,是專(zhuān)用于單線程+阻塞IO的平滑技術(shù)。很多時(shí)候,由于受框架限制(比如游戲或網(wǎng)頁(yè)渲染),我們不得不在單一線程中同時(shí)處理用戶邏輯與IO操作,這時(shí)如果IO消耗時(shí)間過(guò)長(zhǎng),就會(huì)阻塞用戶邏輯代碼,從而讓用戶感覺(jué)到卡頓現(xiàn)象。

          Redis由于其單線程特性,很多耗時(shí)比較長(zhǎng)的操作也需要分?jǐn)偟蕉鄮瑘?zhí)行。比如hash表的rehash操作,當(dāng)表的鍵值對(duì)過(guò)多時(shí),rehash會(huì)產(chǎn)生的龐大的計(jì)算量,如果一次性完成可能會(huì)導(dǎo)致server對(duì)外暫停服務(wù)。Redis選擇將rehash分?jǐn)偟蕉啻螆?zhí)行,稱(chēng)為漸進(jìn)式rehash。另外,對(duì)于比較大的hash表,hgetall一次性獲取所有數(shù)據(jù)可能會(huì)卡死server進(jìn)程甚至導(dǎo)致宕機(jī),建議采用hscan來(lái)分次獲取hash表中的數(shù)據(jù)。Redis中像這類(lèi)支持迭代式掃描的命令有四個(gè),分別是:scan, sscan, hscan和zscan。

          分頁(yè)可以看作是一種另類(lèi)的分幀操作:在運(yùn)營(yíng)類(lèi)項(xiàng)目中,由于后臺(tái)數(shù)據(jù)龐大,很多時(shí)候無(wú)法在單一網(wǎng)頁(yè)中顯示,通過(guò)分頁(yè)顯示,可以避免一次性數(shù)據(jù)獲取帶來(lái)的DB加載壓力和網(wǎng)絡(luò)傳輸壓力。

          分時(shí)復(fù)用指多對(duì)象輪流使用同一個(gè)硬件的技術(shù),多見(jiàn)于跟硬件打交道的底層軟件。比如,分時(shí)操作系統(tǒng):一種采用時(shí)間片輪轉(zhuǎn)的方式同時(shí)為幾個(gè)甚至幾百個(gè)用戶服務(wù)的操作系統(tǒng);分時(shí)復(fù)用網(wǎng)絡(luò):指采用同一物理連接的不同時(shí)段來(lái)傳輸不同的信號(hào),以達(dá)到多路傳輸目的的網(wǎng)絡(luò)基礎(chǔ)設(shè)施。

          但有一種分時(shí)復(fù)用技術(shù),雖然它的名字里沒(méi)有分時(shí)二字,卻與后端開(kāi)發(fā)息息相關(guān),那就是IO多路復(fù)用(洋名:Reactor):?jiǎn)尉€程同時(shí)監(jiān)聽(tīng)多個(gè)文件句柄,哪個(gè)句柄就緒,就通知應(yīng)用線程讀寫(xiě)哪個(gè)句柄的技術(shù)。


          3 分片、分區(qū)、分庫(kù)、分表

          隨著77年恢復(fù)高考,這些年近視的年輕人越來(lái)越多了,于是人們?cè)桨l(fā)的無(wú)法區(qū)分那些換個(gè)馬夾兒就重新出來(lái)混的二貨們。就像洗發(fā)水,猛一看飄柔、海飛絲、潘婷百花齊放,仔細(xì)一看,全特么是寶潔的。沒(méi)錯(cuò),這么土味的名字,竟然不是國(guó)貨,害我自以為愛(ài)了這么多年的國(guó)。

          分片、分區(qū)、分庫(kù)、分表也一樣,名字挺花,療效一樣,都是為了突破單體性能限制的水平擴(kuò)展的方案,洋名字:scale out。因?yàn)榉桨割?lèi)似,大家遇到的問(wèn)題自然也是相同的。它們首先要搞定的就是路由問(wèn)題,也就是把數(shù)據(jù)拆分之后,某個(gè)key儲(chǔ)存到哪個(gè)分片/區(qū)/庫(kù)/表的問(wèn)題。路由方案可簡(jiǎn)單分為兩類(lèi):

          一種是非確定性路由,即相同的key多次路由可以映射到的不同的計(jì)算單元,常見(jiàn)方案有:輪詢、隨機(jī)。非確定性路由多用于無(wú)狀態(tài)節(jié)點(diǎn)間的任務(wù)分配,比如nginx把請(qǐng)求隨機(jī)分配到無(wú)狀態(tài)的微服務(wù)節(jié)點(diǎn)上。

          另一種是確定性路由,即相同的key多次路由必須映射到的相同的存儲(chǔ)單元,常見(jiàn)方案為:區(qū)間,Hash,配置表。確定性路由多用于有狀態(tài)節(jié)點(diǎn)間的任務(wù)分配,比如Kafka按user_id把來(lái)自同一用戶的請(qǐng)求映射到同一個(gè)存儲(chǔ)分區(qū)。

          以MySQL為例,它支持四種分區(qū)類(lèi)型,分別是Range, List, Hash, Key。因?yàn)楦鎯?chǔ)密切相關(guān),它們?nèi)谴_定性路由算法,其中Range對(duì)應(yīng)區(qū)間,List是配置表,Hash與Key則都是Hash類(lèi)型。

          除了應(yīng)用于多機(jī)水平擴(kuò)展,在單機(jī)內(nèi)存中分片方案也有應(yīng)用。比如JDK1.8之前,ConcurrentHashMap通過(guò)將整個(gè)Map劃分成N(默認(rèn)16個(gè))個(gè)Segment,而每個(gè)Segment各自持有獨(dú)立的鎖,從而從整體上減少并發(fā)沖突。


          4 分離

          分離式設(shè)計(jì)是一種架構(gòu)模式,通過(guò)把單元功能單一化、純粹化、專(zhuān)業(yè)化,可以降低開(kāi)發(fā)和維護(hù)成本,同時(shí)提高功能單元的可復(fù)用性,這在設(shè)計(jì)模式中我們通常稱(chēng)之為單一職責(zé)。目前,常見(jiàn)的分離式架構(gòu)設(shè)計(jì)有讀寫(xiě)分離、存算分離。

          讀寫(xiě)分離在國(guó)人的文章中常用于指代MySQL寫(xiě)走主庫(kù),而讀走從庫(kù),這有些狹義了。廣義上讀寫(xiě)分離的重點(diǎn)是:讀路徑不關(guān)心寫(xiě),寫(xiě)路徑不關(guān)心讀,兩者均關(guān)注于自己的功能實(shí)現(xiàn),而毋需為對(duì)方的作出任何犧牲或讓步。更多的細(xì)節(jié)我在文章《23.kafka心中的事件溯源》中有更詳細(xì)的描述,感興趣的讀者可以點(diǎn)擊查看。

          這里我想強(qiáng)調(diào)的是,讀寫(xiě)分離是分離式DB(Unbunding Databases)的雛形。我們應(yīng)該認(rèn)識(shí)到,不存在一種單一的數(shù)據(jù)模型可以滿足所有的訪問(wèn)模式。MySQL在線業(yè)務(wù)、Redis加速查詢、ES全文索引、DW離線分析,每一種衍生數(shù)據(jù)系統(tǒng)都有各自不可替代作用。如上圖所示,通過(guò)統(tǒng)一寫(xiě)端,派生讀端,可以形成一種遵循unix傳統(tǒng)的架構(gòu)模型:?jiǎn)我蝗蝿?wù)做好單一事情,內(nèi)部通過(guò)低級(jí)API(pipe)通信,外部通過(guò)高級(jí)語(yǔ)言(shell)組合。在分離式DB架構(gòu)中,目前看來(lái)最合適的,能起到粘接劑作用的是Event Stream(Event Log)。期望未來(lái)有那么一天,我們能像在shell中寫(xiě)ps | grep java一樣,寫(xiě)出mysql | elasticsearch這樣的代碼,屆時(shí)就是分離式DB摘取王之桂冠的榮耀時(shí)刻。

          如果說(shuō)讀寫(xiě)分離是拆功能,那么存算分離就是拆資源:把計(jì)算資源(CPU、內(nèi)存)和存儲(chǔ)資源(磁盤(pán))拆分開(kāi)來(lái)。早期的云DB,其實(shí)是把單體DB搬到云上。人們很快發(fā)現(xiàn)云DB與單機(jī)DB的不同之處:一是隨著企業(yè)數(shù)字化轉(zhuǎn)型的深入,數(shù)量總量飆升,單機(jī)存儲(chǔ)捉襟見(jiàn)肘;二是在應(yīng)對(duì)雙十一這類(lèi)突發(fā)性流量時(shí),計(jì)算峰值波動(dòng)很大,這使得云DB對(duì)彈性伸縮能力要求極高。問(wèn)題一可以通過(guò)分庫(kù)分表這類(lèi)trick的方式緩解,但問(wèn)題二對(duì)原有單體DB『存算一體』的架構(gòu)提出了挑戰(zhàn)。于是,存算分離的架構(gòu)應(yīng)運(yùn)而生,也就是云原生數(shù)據(jù)庫(kù)架構(gòu)。

          存算分離聽(tīng)起來(lái)很云端,似乎跟我們平時(shí)的工作關(guān)系很小。但其實(shí)有一類(lèi)架構(gòu)它就在我們身邊,只是我們可能沒(méi)有意識(shí)到它也是存算分離架構(gòu),那就是微服務(wù)架構(gòu)。計(jì)算節(jié)點(diǎn)無(wú)狀態(tài),存儲(chǔ)節(jié)點(diǎn)無(wú)計(jì)算;計(jì)算節(jié)點(diǎn)橫向擴(kuò)展,存儲(chǔ)節(jié)點(diǎn)縱向擴(kuò)展。

          就像Duck Test講的:如果它看起來(lái)像鴨子、游泳像鴨子、叫聲像鴨子,那么它可能就是只鴨子。



          6 『減』字訣

          很多時(shí)候我們都講不要過(guò)早優(yōu)化,因?yàn)槎鄶?shù)業(yè)務(wù)的初期,數(shù)據(jù)量都很少,任何設(shè)計(jì)都不太可能出現(xiàn)性能問(wèn)題。反而業(yè)務(wù)上線后,由于進(jìn)展不符合預(yù)期,導(dǎo)致調(diào)整業(yè)務(wù)邏輯的可能性更大。因此,怎么簡(jiǎn)單怎么來(lái)才是最優(yōu)選擇,更快的實(shí)現(xiàn)業(yè)務(wù)比業(yè)務(wù)跑得更快優(yōu)先級(jí)更高。再者,你難道不覺(jué)得,就憑咱們拿的那萬(wàn)兒八千的工資,在非常合理的情況下,就不應(yīng)該寫(xiě)出支撐千萬(wàn)并發(fā)的系統(tǒng),嘛?

          萬(wàn)一哪天系統(tǒng)開(kāi)始出現(xiàn)性能瓶頸呢?該怎么辦?恭喜哈,這是好事,說(shuō)明公司賺錢(qián)了,更重要是通過(guò)你的系統(tǒng)賺錢(qián)了。首先,你要做的第一件事就是跟老板要經(jīng)費(fèi),經(jīng)費(fèi)到手,萬(wàn)事不愁。然后,想辦法把系統(tǒng)恢復(fù)到數(shù)據(jù)量小的時(shí)候,這不就又順理成章的撐住了嘛?接著,經(jīng)費(fèi)沒(méi)地方花了不是?總得找個(gè)地方放啊,我私下認(rèn)為你的錢(qián)包就是一個(gè)不錯(cuò)的地方。

          怎么把數(shù)據(jù)量再次變小呢?勸退用戶是一個(gè)辦法,但我估計(jì)老板可能太不樂(lè)意。

          一個(gè)更可行的選擇是優(yōu)化數(shù)據(jù)結(jié)構(gòu)和算法。比如給DB建索引就是一個(gè)辦法,同樣的查詢,同樣是千萬(wàn)級(jí)的數(shù)據(jù),有索引和無(wú)索引的查詢速度千差萬(wàn)別,因?yàn)樗饕龝?huì)極大減少掃描的數(shù)據(jù)行數(shù)。

          另一個(gè)選擇是裁剪數(shù)據(jù)。就像Java的GC會(huì)定期清理垃圾一樣,如果你發(fā)現(xiàn)DB里的數(shù)據(jù)大部分都是過(guò)期無(wú)效的,或者是基本上不會(huì)再查詢到的數(shù)據(jù),把過(guò)期數(shù)據(jù)歸檔,減少線上數(shù)據(jù)集的尺寸會(huì)是一個(gè)非常好的選擇。該操作系統(tǒng)改造成本極低,對(duì)線上業(yè)務(wù)無(wú)影響,對(duì)數(shù)據(jù)后臺(tái)則可以看心情逐步改造。所有相關(guān)人員都?jí)毫Σ淮?,但效果卻是線上系統(tǒng)從此秒開(kāi),絕對(duì)騷得一批。其實(shí)前面提到的分片、分區(qū)、分庫(kù)、分表,也都是在變相減小單位處理單元上的數(shù)據(jù)量,只不過(guò)它們改造成本高,實(shí)施難度大。特別是,在決定采用分表之前,應(yīng)慎重考慮歸檔是不是一個(gè)更合理的選擇。

          當(dāng)年做手游,發(fā)現(xiàn)游戲在4k屏的手機(jī)上運(yùn)行極慢,手機(jī)的GPU根本帶不動(dòng),嘗試了各種優(yōu)化手段都不好使。誰(shuí)曾想,最終解決問(wèn)題的,竟然是降低游戲的輸出分辨率。

          算法上縮,物理上減,現(xiàn)在連國(guó)家不開(kāi)始提倡65歲退休了,我們的老系統(tǒng)也一定能才堅(jiān)持幾年。


          7 『并』字訣

          終于到了并發(fā),一個(gè)大部分人都覺(jué)得應(yīng)該有用,同時(shí)大部分人覺(jué)得用起來(lái)發(fā)怵的優(yōu)化手段。

          如果說(shuō)前面的『池』、『序』、『分』、『減』這幾條頂多算工程技巧,憑著我們聰明的大腦&反復(fù)思考就有可能搞清楚的話,『并發(fā)』這一條就是一個(gè)學(xué)術(shù)問(wèn)題。換句話說(shuō),即使經(jīng)過(guò)幾年的系統(tǒng)學(xué)習(xí),也很少有人敢拍胸脯說(shuō)自己的并發(fā)代碼無(wú) bug。

          并發(fā)真有這么難么?從編程語(yǔ)言的角度看,并發(fā)不就是多線程和死鎖么?賢者大神們的文章已經(jīng)解釋得很清楚了,像《不環(huán)保的死鎖:破解死鎖,我們一般從下三路入手》、《線程安全,唯快不破》,只要規(guī)避掉死鎖的幾個(gè)必要條件,還不是怎么順手怎么寫(xiě)?而且,運(yùn)氣好的話還可以無(wú)鎖解決并發(fā)問(wèn)題,不但準(zhǔn),而且快。

          然而真相遠(yuǎn)不是這么簡(jiǎn)單。我們知道DB是解決數(shù)據(jù)安全和數(shù)據(jù)一致性問(wèn)題的集大成者,我們嘗試從DB的角度來(lái)觀察一下。DB事務(wù)有ACID四個(gè)屬性,其中I是指隔離性Isolation,而它的研究核心就是并發(fā)問(wèn)題。

          毛爺爺說(shuō)事務(wù)物是運(yùn)動(dòng)發(fā)展的,我覺(jué)得他說(shuō)的是對(duì)的。兩年之前還沒(méi)有新冠肺炎呢,今天它已經(jīng)像吃飯喝水一樣融入到了我們每個(gè)人的生活當(dāng)中。在早期的ANSI SQL92標(biāo)準(zhǔn)中,涉及到的并發(fā)異象只有4種,然而發(fā)展到今天,常見(jiàn)的并發(fā)異象已經(jīng)有7種之多,它們分別是:臟讀、臟寫(xiě)、讀傾斜(不可重復(fù)讀)、寫(xiě)傾斜,不可重復(fù)讀、幻讀、更新丟失。每一種并發(fā)異象都有不同的原因,以及不同的解決方案,而這還不是全部。其實(shí)我很想稍微給大家科普一下這些異象相關(guān)的內(nèi)容,但是我發(fā)現(xiàn)細(xì)節(jié)實(shí)在太多了。我盲猜后端的知識(shí)體系中,可能有一半都跟并發(fā)有關(guān)。

          沒(méi)人否定當(dāng)年敲定SQL92標(biāo)準(zhǔn)的應(yīng)該算DB專(zhuān)家吧?連當(dāng)前ANSI的專(zhuān)家都沒(méi)能完全搞清楚事情,誰(shuí)敢告訴我他輕松就能搞定?


          所謂單機(jī)并發(fā)榨硬件,多機(jī)并發(fā)擴(kuò)上限(scale out)。當(dāng)單線程服務(wù)遭遇性能瓶頸,同時(shí)相應(yīng)的機(jī)器硬件還有富余的時(shí)候,進(jìn)行多線程改造從而充分壓榨硬件性能可能會(huì)是一個(gè)比較好的選擇。相應(yīng)的,當(dāng)單機(jī)性能已經(jīng)無(wú)法滿足服務(wù)需要的時(shí)候,就需要進(jìn)行分布式改造,通過(guò)水平擴(kuò)容的方式提升整體服務(wù)能力。這兩種思路,對(duì)應(yīng)到『分』字中,恰好是分表與分庫(kù)的區(qū)別:如果只是數(shù)據(jù)量上去了,CPU和內(nèi)存壓力都不大,那就分表再壓榨一下;反之,如果流量大增,單機(jī)負(fù)載已經(jīng)抗不住了,就可以考慮選擇分庫(kù)。

          『并』字訣好使,但并不好掌握。引入并發(fā)會(huì)極大增大代碼復(fù)雜度,提高維持?jǐn)?shù)據(jù)一致性的難度。就像分庫(kù)分表一樣,它的痛,只有用過(guò)人才知道,因此,往往只會(huì)作為終極優(yōu)化手段。不用則已,用則需要有面對(duì)困難的勇氣和決心。


          8 優(yōu)化即置換

          作為一名程序員,你一定聽(tīng)過(guò)這樣一句話:好的架構(gòu)不是設(shè)計(jì)出來(lái)的,而是演化出來(lái)的。想要獲得什么,就要付出代價(jià),就像想要討老婆,就得努力掙錢(qián)一樣。優(yōu)化會(huì)使代碼邏輯變得復(fù)雜,流程變得混亂。因此簡(jiǎn)單設(shè)計(jì),先上線,用錢(qián)堆,這些看起來(lái)很土的選擇很多時(shí)候可能比盲目?jī)?yōu)化更能使我們遠(yuǎn)離漩渦。

          但無(wú)論如何,經(jīng)常的,持續(xù)的分煎餅是個(gè)好習(xí)慣。

          特別是山東煎餅,山東泰安的。


          瀏覽 58
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  久久亚洲中文字幕 | 青娱乐性 | 大香蕉伊人干 | 操逼在线免费观看 | 国产精品国产亚洲精品看不 |