自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

誰還不懂分布式系統(tǒng)性能調(diào)優(yōu),請把這篇文章甩給他~

新聞 前端 分布式
不管是X86、ARM,還是 Linux、Windows,不管是腳本語言還是C語言,這種優(yōu)化都有效的,相對來說收益比較大、比較普惠一些,我把它列為基礎(chǔ)資源優(yōu)化。

 作者簡介:陶輝,畢業(yè)于西安交通大學(xué)計算機(jī)科學(xué)與技術(shù)專業(yè),目前在杭州智鏈達(dá)數(shù)據(jù)有限公司擔(dān)任 CTO 兼聯(lián)合創(chuàng)始人,專注于用互聯(lián)網(wǎng)技術(shù)幫助建筑行業(yè)實現(xiàn)轉(zhuǎn)型升級,曾先后在華為、騰訊、思科、阿里巴巴等公司從事分布式系統(tǒng)下的數(shù)據(jù)處理工作,騰訊云最有價值專家TVP。對 Linux 下的高性能服務(wù)器開發(fā)、大規(guī)模分布式系統(tǒng)的設(shè)計有著豐富經(jīng)驗。著有《深入理解 Nginx:模塊開發(fā)與架構(gòu)解析》一書。

本次分享分成4塊:

基礎(chǔ)資源優(yōu)化。不管是X86、ARM,還是 Linux、Windows,不管是腳本語言還是C語言,這種優(yōu)化都有效的,相對來說收益比較大、比較普惠一些,我把它列為基礎(chǔ)資源優(yōu)化。

網(wǎng)絡(luò)效率優(yōu)化。這包含三個層面3層,一是系統(tǒng)層,二是應(yīng)用層,三是傳輸效率的優(yōu)化。

降低請求時延。怎么用緩存等,我們關(guān)注用戶的體驗,能讓單次請求的速度更快一些。

吞吐量。提升系統(tǒng)并發(fā),從吞吐量來說怎么提升系統(tǒng)的并發(fā)。

一、基礎(chǔ)資源優(yōu)化

基礎(chǔ)資源的優(yōu)化,在我看來核心是提升資源的利用率,資源我們分為四塊:

  • CPU 緩存。CPU 的緩存共分為3級,CPU訪問緩存的時延應(yīng)該在 10 納秒左右,一級緩存可能只有 1 納秒左右。這個時候我們非常關(guān)注怎么讓 CPU 緩存命中率提升,它是一個普惠的優(yōu)化方式。
  • 內(nèi)存。內(nèi)存有百納秒級別的速度,當(dāng)你的頻率非常高的時候也不是那么快,所以會有很多的內(nèi)存池,比如說 C,JVM、Python、Golang、lua 等都有自己的內(nèi)存池,這些內(nèi)存池的怎么提升分配速度、減少碎片、提升內(nèi)存的利用率。
  • 磁盤。磁盤有兩類,一類是HDD,機(jī)械硬盤的尋址在七八毫秒,它的磁頭旋轉(zhuǎn)速度是比較慢的,以往的很多軟件技術(shù)都在優(yōu)化這方面,特別是 PageCache 磁盤高速緩存的使用,包括電梯調(diào)度算法、零拷貝,或者 Direct IO 等等都是圍繞著機(jī)械磁盤來做;另外一類SSD,SSD跟機(jī)械硬盤完全是另一種方式,包括編程方式、優(yōu)化方式都跟傳統(tǒng)的方法不一樣。所以這里簡單總結(jié)一下,以 PageCache 作為切入點,包括命中率、IO調(diào)度算法。
  • 調(diào)度。分布式系統(tǒng)中有很多的請求,這些請求如何切換,無論是多進(jìn)程、多線程,還是協(xié)程,怎么樣能讓它的調(diào)度性能更高、效率更高,數(shù)據(jù)的同步更快?

CPU

CPU 緩存的話題比較多,這里舉個例子,我相信各位都用過 Nginx,Nginx 有兩個哈希表,一個是域名哈希表,如果只是用普通的3級域名,可能觸碰不到它的上界,但是如果有4級、5級或者是更多級的域名,可能就會超過64字節(jié),這時你要調(diào)大哈希表的 bucket_size。

再舉個例子,Nginx 最強(qiáng)大的地方在于它的變量,任何功能都能夠通過變量實現(xiàn)。變量也是存在哈希表中。當(dāng)這個變量比較大的時候,一樣也要調(diào)整到 64 字節(jié)。

那么,為什么是初始的 64 字節(jié)?比如想調(diào)到 100 字節(jié)可不可以,不可以的。要調(diào)到 128 字節(jié)或者是是 64 字節(jié)的整數(shù)倍。

為什么會這樣?關(guān)于 CPU 緩存,我這里舉一個例子。大概在2005年,CPU 的頻率大概升到 3Ghz~4Ghz 后就不再提升,因為單個 CPU 的發(fā)熱量太大,所以只能橫向發(fā)展。

多個 CPU 有一個好處就是真正并發(fā)。操作系統(tǒng)中的并發(fā)是通過時間片切換完成的,微觀上不是真正的并發(fā)。而多核 CPU 的并發(fā)有一個問題,當(dāng)兩個 CPU 同時訪問了兩個進(jìn)程或線程,這兩個數(shù)據(jù)同時落到了一個 64 字節(jié)之內(nèi),CPU 的緩存不是一個字節(jié)拉的,而是一批拉的,每一批 64 字節(jié)。

為了解決這個問題,Java 中有一個常見的詞叫填充法,我剛剛講的 Nginx 也是填充法,用不了這么多字節(jié),但要填這么多,使得微觀上真正并發(fā)。因為 CPU 要保證一致性,如果數(shù)據(jù)沒有一致性就沒有高性能和正確性可言。CPU 的緩存對很多代碼都有影響,并不是只對寫中間件的人有影響,對應(yīng)用層的開發(fā)人員也是有影響的。

內(nèi)存

關(guān)于內(nèi)存池這一方面,我想起3年前我給華為做的一場內(nèi)訓(xùn)。當(dāng)時有一個同學(xué)問我,Nginx 要不要換成谷歌的 TCMalloc?(TCMalloc 是 Google 開發(fā)的一個內(nèi)存池。)

應(yīng)用程序每次要分配內(nèi)存,操作系統(tǒng)內(nèi)核提供系統(tǒng)調(diào)用,叫 brk 和 mmap。這些系統(tǒng)調(diào)用的效率不高,因為有內(nèi)核態(tài)到用戶態(tài)的切換,那么怎么辦?

C程序有一個C庫,Linux 默認(rèn)叫 Ptmalloc2。 默認(rèn)分配一個字節(jié)會給你預(yù)分配 64MB 字節(jié),當(dāng)你分配第二個字節(jié)的時候還從這個池子里面找,釋放后會回到這個內(nèi)存池。這個池子有很多的問題,比如說 Ptmalloc 非常通用的內(nèi)存池,考慮的是效率特別高,比如說A線程釋放的,B線程還能直接拿來用,這肯定有一個并發(fā)問題,肯定要加鎖,一加鎖性能就不高,像 tcmalloc和 Ptmalloc 默認(rèn)了這個,什么也不改。

而谷歌的 TCMalloc 分為小內(nèi)存、中內(nèi)存和大內(nèi)存,小于256KB字節(jié)的就是小內(nèi)存,小內(nèi)存不考慮共享,所以不用加鎖,速度很快;對于中內(nèi)存和大內(nèi)存速度反而不如 Ptmalloc。如果我們做服務(wù)器的開發(fā),特別是 Nginx 簡單的服務(wù)器的開發(fā),如果是做動態(tài)服務(wù)器,經(jīng)常會分配幾MB內(nèi)存,但是對于只有負(fù)載均衡、簡單 lua 腳本操作情況下根本不可能分配大內(nèi)存,所以 TCmalloc 就非常的適合它。

這里我只是介紹了C庫的內(nèi)存池,其實上面還有很多的內(nèi)存池,比如說 Nginx 對共享內(nèi)存中有內(nèi)存池 slab,這個 slab 在 openresty,是復(fù)用的,對于普通的儲存里也有一個內(nèi)存池,還有連接內(nèi)存池和請求內(nèi)存池。

如果是其他的語言,比如說 lua 虛擬機(jī),它有自己的lua,還有自己的內(nèi)存池。Java 有 JVM 內(nèi)存池,golang 也有自己的內(nèi)存池,像 golang 是基于 tcmalloc 改過垃圾回收機(jī)制。這是我希望能夠引起大家注意的,主要是靠線下大家學(xué)習(xí)。

磁盤

磁盤,我想圍繞著 PageCache。PageCache,傳統(tǒng)的機(jī)械硬盤,你想讓它并發(fā),它并發(fā)不了,它只蒙在那里轉(zhuǎn),所以你要通過調(diào)度算法,讓它盡量往一個方向轉(zhuǎn)。讀的話,會考慮 PageCache 不停的緩存命中。

比如說零拷貝,零拷貝是不是一直有效呢?比如做視頻的直播或者 CDN,這個文件很大,再次命中的概率不高,Pagecache 及其有限,如果文件這么大,因為有很多并發(fā)的線路正在同時獲取不同的文件,所以命中的概率很低。

如果進(jìn)到 PageCache 中會怎樣?

  • 很難再次命中,PageCache 也給它進(jìn)行了降低性能。
  • 因為 PageCache 是 least used,只要你現(xiàn)在進(jìn)來一個東西,一定要出去,出去的東西,本來是很有機(jī)會被再次訪問到的小塊資源和文件,所以就失去了再次命中的機(jī)會,所以才會有直接IO和異步IO,Linux 的作者認(rèn)為接口設(shè)計很糟糕,但是這個東西是很有用的,并不是一點用也沒有。

關(guān)于磁盤高速的緩存,還有很多可以分享的。

比如說SDD,SDD在我看來是另外一個物種。平時大家都認(rèn)為“SDD 比機(jī)械硬盤的 IOPS 高,Latency 低,Thoughtput 更高,即總的吞吐量更高”絕對不是簡簡單單這么一點東西。

我舉個例子:第一,SSD有個問題叫寫入放大。

大家都知道 Kafka,Kafka 性能為什么這么好?一個特點就是削峰填谷,它要把數(shù)據(jù)持久化。既然是寫硬盤性能為什么能這么好?因為它充分利用了磁盤的旋轉(zhuǎn)速度,消息隊列先天性是時序隊列,一定不停的往一個文件后面追加,所以磁盤利用率非常高,只要放很多塊機(jī)械磁盤,性能還可以繼續(xù)提升。

如果換了SSD,還有這樣的好處嗎?SSD 本來就是快很多,但再采用這種方式就有問題,因為它有寫入放大問題,什么叫寫入放大?比如說我們本來是寫日志文件,都往后面追加一些字節(jié),但是 SSD 不行,SSD有一個頁面,是它的基本單位,它是按頁面來的。

比如說現(xiàn)在的頁面上已經(jīng)有了一個字節(jié),現(xiàn)在還想寫第二個字節(jié),它會把整個頁面讀到緩存區(qū)中,然后給它加進(jìn)去,最后再寫到一個新的沒有人寫過的頁面,所以有很大的放大作用。每寫一個字節(jié),可能給我寫了幾K,它最怕的是 inplace 這種原地寫入,那像 Kafka 再采用的方式會有問題。

第二,耗損平衡。

SSD的壽命不好,什么壽命短呢?因為它不能清除,每一個存儲單元清除的次數(shù)是有限制的。如果一個硬盤有一個數(shù)據(jù)特別熱,比如說就是放操作系統(tǒng)的,經(jīng)常讀寫,讀著就壞了,這個是大家受不了的,其他地方都好。所以搞了一個GC,不停給你移,這邊是熱數(shù)據(jù)的,這邊是冷數(shù)據(jù)的,我給你移過去,移過去之后大家就平衡,這塊磁盤就不會那么快壞。

但這個機(jī)制就有問題,給應(yīng)用程序帶來了挑戰(zhàn)。如果應(yīng)用程序按照傳統(tǒng)的方式去寫,不停大量的寫入操作進(jìn)去,就會不停觸發(fā)機(jī)制,當(dāng)他的GC趕上過米的時候,又變成阻塞了,性能又下去了。

其實 SSD 有很多和 HDD 不一樣的地方,比如說機(jī)械硬盤的時候,絕對不會考慮多開幾個線程,讓速度變快,因為硬盤就在那里轉(zhuǎn),多開線程最終還是排隊的,但是SSD不一樣,你多開幾個,它就是那么快,因為它先天可以做并發(fā)。

比如說 SSD 做隨機(jī)讀寫也做得特別好,我們在機(jī)械硬盤上盡量減少數(shù)據(jù)讀寫的,但到SSD就可以讀寫。

再回到高并發(fā),為什么高并發(fā)要使用協(xié)程,為什么比多線程要快效率要高?在我看來有兩個原因:

1、每一個協(xié)程消耗的內(nèi)存非常低。每一個線程有多高嗎?給你搞了一個堆內(nèi)存池,你說一個字節(jié)搞一個內(nèi)存池,線程都有棧,棧什么時候會溢出呢?棧到底有多大呢?一般是2MB到8MB,這么大的線程,可以想象有多少的內(nèi)存?

如果幾十G的內(nèi)存上并發(fā)10000個線程,內(nèi)存都不夠,其他業(yè)務(wù)就做不了了。

最大的問題是同時處理一個請求消耗的內(nèi)存不能多,而協(xié)程就能做到。基本上協(xié)程幾KB到十幾KB就可以處理一個請求。所以可以輕松通過十幾G的內(nèi)存并發(fā)到幾萬個、十幾萬個請求。

2、切換成本低。在線程上做切換時有一個內(nèi)核態(tài)到用戶態(tài)的切換,而且要做大量的拷貝。在協(xié)程的話,因為是全棧用戶態(tài),切技能器,或者是不切技能器,直接調(diào)度到代碼里面切一下就可以了,所以相對來說成本也是很低的。

最后再總結(jié)一下,對于 CPU 這方面,有一個特別有效的工具。有時候說優(yōu)化性能,要找到瓶頸,越是瓶頸的地方進(jìn)行優(yōu)化才最有價值。怎么找瓶頸呢?我推薦火焰圖。你要自己去打點和寫日志很容易遺漏,你不用裝任何的東西,只需要裝一個 Linux perf+FlumeGraph 兩個軟件就可以。

它分了兩種,on CPU 和 off CPU。onCPU 它是以暖色調(diào)為主,看消耗了多少個CPU,因為把所有的函數(shù)都考慮到;而offCPU是冷色調(diào),看每一個函數(shù)要進(jìn)程或者是線程進(jìn)入 sleep 的時間有多長?這兩個圖有一個好處,它是一個SVG的矢量圖,所以可以跟各種正則表達(dá)式匹配,也可以點擊放大,而且它會把同一個調(diào)用棧中的相同函數(shù)進(jìn)行合并,很容易比出來哪一個函數(shù)消耗的時間最長。

二、網(wǎng)絡(luò)效率優(yōu)化

網(wǎng)絡(luò)效率優(yōu)化,主要是編解碼和改進(jìn)傳輸方式。

編解碼有三部分:

  1. 系統(tǒng)層傳輸效率。系統(tǒng)層主要是TCP協(xié)議,它的優(yōu)化包括三次握手建鏈優(yōu)化,四次握手關(guān)閉優(yōu)化。主要根據(jù)你的網(wǎng)絡(luò)環(huán)境,丟包率高不高,時延長不長;如果丟包率非常好,重試的次數(shù)很多都可以進(jìn)行調(diào)整。還有緩沖區(qū)優(yōu)化,以及擁塞控制。
  2. 應(yīng)用層編碼效率。
  3. 應(yīng)用層傳輸效率。

應(yīng)用層的話,主要看 HTTP 協(xié)議。

1996年是HTTP1.0,1999年是HTTP1.1,我們現(xiàn)在主要使用的是1.0和1.1,2015年是HTTP2,他們都是跑在TLS和TCP上,為什么呢?因為能夠簡化開發(fā)效率。

TCP 實現(xiàn)了有序字節(jié)流,TLS 也要基于有序字節(jié)流,如果沒有有序字節(jié)流,現(xiàn)在也玩不轉(zhuǎn)。比如在 QUIC 中要重新實現(xiàn)這點。HTTP2,有了有序字節(jié)流,很多問題不用考慮,不管文件有多大,只要正常傳輸就好。

這就帶來了一個問題,在一個有序的字節(jié)流上,放了一個無序的東西(HTTP 是多并發(fā)的,并發(fā)肯定是無序的)。無序的東西承載在有序的字節(jié)流上,帶來的問題就是隊頭阻塞。它有多個stream,只要出現(xiàn)一個 stream,TCP 就有可能丟包,后面的 stream 都玩不轉(zhuǎn)了,所以只能通過 UDP 來解決。

UDP 解決以后,出現(xiàn)了一個 QUIC 層,這個 QUIC 層是獨立的一層,把 TCP 層的很多事(丟包、重傳、擁塞控制)都做了,TLS 也需重新寫,把 HTTP2 中的 stream 也封裝也放到了里面。

HTTP3 到底有什么好處呢?1.多路復(fù)用;2.連接遷移;3.QPACK編碼;4.丟包重傳。

我舉個例子,比如說抓一個 HTTP3 的包就知道,抓包看完最上面是 UDP Header,UDP Header 就是一個4元組:源IP、目的IP、源端口、目的端口。接下來是 Packet Header,Packet Header 中有一個整數(shù),叫 connection_id,為什么有 connection_id?

以前一個連接是如何定義的呢?四元組(源IP、目的IP、源端口、目的端口),只要四元組改了就需要重連。

在 IOT 時代,各種高速移動的設(shè)備會經(jīng)常切換,比如經(jīng)常切換的 5G 基站,或是切到一個 WIFI,這時IP地址肯定會發(fā)生變化,需要重新建立連接。重建連接的成本太高了,怎么樣不重新建立連接呢?從 Packet Header 中,定義了一個 connection_id 的整數(shù)。

只要這個整數(shù)不變,這個連接就可以復(fù)用,TLS 握手也不用做了。為什么可以這樣做呢?其實很簡單,這些東西是加密到 HTTP3,如果能夠解密的話,安全性是沒有問題的。Packet Header 中,這就叫連接遷移,就是我們說的 Connection Migration,連接遷移,這是 HTTP3 的第一個用處。

HTTP3 的第二個用處多路復(fù)用。多路復(fù)用是到 QUIC Frame Header,Packet Header中定義了連接是無序的字節(jié)流連接,只管你不丟包,要是丟包了,我們有一個ID,能找到重發(fā)。

但順序亂了,我是不管的,管順序亂不亂是在 QUIC Frame Header,他做了一個東西,他重新定了叫 QUIC stream 的概念,就像跟 TCP 連接是一樣的,這里做出來一個TCP 連接,跟 TCP 連接用起來是一模一樣的。

大家都知道 TCP 三次握手時在同步 Sequence,SYN 參數(shù)的全稱就是 Synchronize Sequence Numbers。在同步兩邊的序列號,因為在接下來的每發(fā)一個字節(jié),序列號就會加1,對方接受確認(rèn)的時候也是這么做的。他里面也有一個 Sequence,所有的都是一模一樣的。

基于這個做出來的有序字節(jié)流,就完成完成解決了隊頭堵塞問題,所以它的多路復(fù)用是真正的多路復(fù)用。

再接下來到了 HTTP3 Frame Header,QUIC Frame Header 已經(jīng)提供了一個 TCP 連接,但是 HTTP 是有自己很多獨立的應(yīng)用,比如說除了 request/response 以外,還有服務(wù)器推送,服務(wù)器推送就是一種新的 Frame Header,先所以前面又加了一層頭部去完成這樣的功能。

還包括另外一個功能,QPACK,大家知道 HTTP2 當(dāng)中有一個 HPACK,功能就是壓縮頭部。最高的壓縮就像我們看視頻的時候,有關(guān)鍵幀和增量幀,如果經(jīng)過什么壓縮的視頻,壓縮完以后體積能減少幾百倍,看起來還挺清晰的,它就是關(guān)鍵幀和增量幀,壓縮效率特別高,HPACK 也是一模一樣的。當(dāng)你在一個連接上第一次傳輸一個 HTTP header 的時候,其實就是你的關(guān)鍵幀。

后面做增量幀的時候,只要傳一個數(shù)字就行。但是這種方式也有一個時序效應(yīng),誰先誰后。所以就出現(xiàn)了 QPACK,這是基于無序的連接去實現(xiàn)一個功能。

單播和多播,什么是單播?比如說主機(jī)1同時給主機(jī)2、3、4發(fā),如果是單播就建3個TCP 連接,分別是紅藍(lán)綠。如果是網(wǎng)絡(luò)層的多播就是這么發(fā),比如說主機(jī)1發(fā)了以后,路由器 A 把這個報文復(fù)制了一份,一份給路由器B,一份給主機(jī)2,到路由器B以后,又復(fù)制了兩份,一個給主機(jī)3,一個給主機(jī)4,所以它的網(wǎng)絡(luò)效率應(yīng)特別高。

但是它沒有辦法跨網(wǎng)絡(luò),一是安全問題,很容易造成網(wǎng)絡(luò)風(fēng)暴;二是路由器A和路由器B不是一個廠家的,很難辦。所以,只在局域網(wǎng)內(nèi)會有網(wǎng)絡(luò)層多播( ARP 協(xié)議等)。

所以我們最好用的是應(yīng)用層的多播,主機(jī)1要給2、3、4發(fā)的時候是這樣發(fā)的,主機(jī)1可能成為一個中心化的節(jié)點去拉了,說先給3發(fā),3主動去拉也可以,拉完以后,3再從中心服務(wù)器里面拉,比如說4沒有,3的層面就拉給他,這里的用處很大。

在很大的集群內(nèi)要發(fā)布一個幾百兆的新版本,這時候如果單機(jī)向大家推,機(jī)器就打爆了,即使用10G或者是40G的外置網(wǎng)卡,你的下限帶寬也就幾個G,根本抗不住成千上萬同時拉你的服務(wù)器。比如說阿里開源的蜻蜓(Dragonfly)就很好用,他只是用這個理念,怎么實現(xiàn)就太容易了,什么協(xié)議都能實現(xiàn),用 HTTP 協(xié)議就好了,整個實踐成本都會低很多。

再比如說 GossIP 協(xié)議,它其實也是一個傳染病協(xié)議,在 Redis 集群中管理節(jié)點都是使用GossIP 的這個協(xié)議。

三、降低請求延時

降低請求時延,提升用戶體驗。我分為4部分:

  • 緩存
  • 異步化
  • MapReduce
  • 流式計算

除了剛才說的讀加載緩存,還有寫緩存,我們知道有CAP定理,緩存一定有P,因為數(shù)據(jù)有冗余,在兩臺機(jī)器上都有緩存,但是關(guān)注于一致性的時候就是一個write through 方式的的緩存。AP 可能關(guān)注的是 write back 的緩存,這樣單個請求的使用量會更高。

BASE 理論對緩存也有很多的用法,比如 Nginx 或 Openresty,后端掛掉了,每次訪問都給返回 502 請求,你加一個東西后配 HTTP 502,它會把本來后期的緩存直接返回給客戶,所以可用性也就提升了,相當(dāng)于容災(zāi)了,提供了基本可用性。

MapReduce 講了很多,分為3步:數(shù)據(jù)分發(fā)、每個節(jié)點進(jìn)行 Map 函數(shù)計算、輸出以后合并。MapReduce 我們很多是在 SQL 來做,因為你用 SQL 的 Group by 的聚合計算,先天性的跟分發(fā)和規(guī)避能夠?qū)Φ闷?,無論是求標(biāo)準(zhǔn)差或者是平均值,也很容易去做并行的計算,這是可以做到的。當(dāng)然如果有前后依賴關(guān)系那是沒有辦法做的。

MapReduce 還有一個特點,它跟數(shù)據(jù)源強(qiáng)相關(guān),所以基本上 Java 生態(tài)在這塊上是無敵的。原因是,大家的數(shù)據(jù)都放在中,所以數(shù)據(jù)是互聯(lián)網(wǎng)公司的核心資產(chǎn)——數(shù)據(jù)都在這里,其他框架寫得再好也沒有用。

流式計算和 MapReduce 有一個很大的差別,它有時間窗口,不管你用技術(shù)的窗口,還是用時間窗口,都有時序性,但是網(wǎng)絡(luò)報是沒有時序性的,是亂序的。所以對窗口的劃分是比較麻煩的。所以我們會有一個loadmark在一定程度上緩解。二是業(yè)務(wù)是強(qiáng)相關(guān)的,有時候窗口是固定的時間,大部分的時候可能是用戶登陸的時候,它是跟 Session 強(qiáng)相關(guān)的情況。

四、提升系統(tǒng)并發(fā)

提升系統(tǒng)的并發(fā),怎么樣高效的拓展系統(tǒng)?

這里說一下 AKF 擴(kuò)展立方體,我覺得這個東西特別的好用,所以給大家介紹一下。

比如說在 Nginx 上配了很多的上游,上游默認(rèn)使用的是 RoundRobin,不管你的上游有8核16G還是4核8G的,你都會配權(quán)重,配權(quán)重似乎它倆不一樣,但是任何一個請求這兩臺都能夠處理。所以它其實是復(fù)制過來的,用最小連接數(shù),它關(guān)注的點是上游服務(wù)器的負(fù)載。

這是X軸,X軸的成本特別低,加機(jī)器就可以。但是到了Y軸和Z軸就不一樣,他們開始是基于請來。比如說 MySQL 做讀寫分離,我看到了一個 select 的語句和 update 的語句,如果是 select 的語句,我就隨便找一個 slave,找一個備庫去訪問,它又回到X軸了。

如果是一個 update,我可能就要進(jìn)入主庫,這就是讀寫分離。還有像 API Gateway 也是經(jīng)常這樣的,做代碼重構(gòu)以后,希望把用戶類的、日志類的分到不同的集群去處理,這時候都是基于Y軸,Y軸的成本是非常高的。

Z軸就是分庫分表,基本上是基于哈希,用戶A的數(shù)據(jù)可能只能到服務(wù)器1,用戶B的數(shù)據(jù)可能到服務(wù)器2。Z軸的成本不好說,比如說哈希算法,為什么說一致性哈希呢?因為你做這種基于哈希的負(fù)載均衡有很大的問題,請求的集合是近乎無限的集合,但是上游服務(wù)器的映射集合是非常有限的,有多少的服務(wù)器,你有多少個選擇,就只能映射到這樣的集群。

從大集群映射到小的集群,無論前面經(jīng)過多少的算法,最后肯定得有求余的操作,沒有求余的話,沒有把它歸納到這么小的集合內(nèi)。求余的話,就有一個問題,余數(shù)不能變,因為你的上游機(jī)器一旦出現(xiàn)宕機(jī)或者是增加的時候,余數(shù)變了,整個哈希函數(shù)就變,只要這個函數(shù)一變,那整個Z軸就會發(fā)生很大幅度的變化。

我們會使用一次性哈希,它跟哈希函數(shù)還不一樣,哈希函數(shù)其實是O1復(fù)雜度的,但是一致性哈希,它不是O1復(fù)雜度,它會變成logM,它會把一個請求的關(guān)鍵字的信息,映射為哈希節(jié)點,哈希是32位的整型,32位整型構(gòu)成了0~42億,到42億以后也會形成一個環(huán),又到了0,它通過這樣的有序的切分,在正常使用的時候,是是通過二分法來查找。

一致性哈希正常使用還是有問題,我們一般會使用虛擬節(jié)點層。為什么要使用虛擬節(jié)點層?我們要避免雪崩效果,如果上游服務(wù)器掛了,只會影響周邊的節(jié)點,如果周邊的節(jié)點已經(jīng)達(dá)到80%的負(fù)載,這臺機(jī)器所有的流量過去,它也跟著掛了,它掛了以后,下游也掛了,就全部都掛了。

我們希望的是A節(jié)點掛了以后,能讓所有的節(jié)點平分流量,這是最好的,其實二次哈希就可以。所謂的虛擬節(jié)點層聽起來很高大上,其實就是二次哈希。第一次哈希形成一個環(huán),第二次哈希把這個環(huán)再做一個哈希,基本上是兩層。

一般來說,我們每一層是100到200,有了這樣的東西,我們避免雪崩,還有一個好處是分布的均勻性。

無論采用什么樣的函數(shù),做完以后,和你本身數(shù)據(jù)請求的分布不一致,很多的數(shù)據(jù)都很大這時候怎么解決呢?二次哈希能夠使得分布更均衡。

最后再說持久化,以前做持久化數(shù)據(jù)的時候,就兩招,比如說一個數(shù)據(jù)有很多份冗余,就會想到兩招,每次寫的時候,把兩臺都寫一遍,我寫速度的性能就很差。我寫的時候隨便寫一個,用異步的方式同步給其他的機(jī)器,一致性比較差,可能會丟數(shù)據(jù),但是性能會比較好。到了亞馬遜,零幾年的時候出了一個 Quorum 論文,說了這個 NWR 算法,數(shù)據(jù)冗余份數(shù)是N,W是寫節(jié)點數(shù),R是讀節(jié)點數(shù),N是冗余節(jié)點數(shù),所謂的高可用 W+R>N 就能實現(xiàn)。

W+R大于N,比如說數(shù)據(jù)有3個節(jié)點,R1R2R3,讀和寫,如果我們寫的話,1和3返回了,2沒有返回,這時候還是可以成功的。另外在讀的時候,只需要讀2個,一定能讀到1或者是3,這個時候只要我們能夠通過時間戳方式,判斷誰有正確的數(shù)據(jù),就能夠保證數(shù)據(jù)的強(qiáng)一致性。

W大于N,本來寫3個節(jié)點,現(xiàn)在掛了一個節(jié)點,你如果小于這個的話,比如你只有2個節(jié)點,你掛了一個節(jié)點,寫就不能用了。3個節(jié)點的話,掛了一個還能用。

今天我的分享就到這里,我總結(jié)一下,從底層到高層的看法,不一定正確。

 

 

責(zé)任編輯:張燕妮 來源: 高效運維
相關(guān)推薦

2021-03-08 12:47:42

MySQL查詢數(shù)據(jù)

2021-03-08 10:25:37

MySQL數(shù)據(jù)庫索引

2018-12-07 09:31:52

分布式鎖服務(wù)框架分布式系統(tǒng)

2021-02-22 13:32:19

MySQLSQL索引

2020-02-24 21:50:24

瓶頸數(shù)據(jù)庫

2018-10-12 09:42:00

分布式鎖 Java多線

2019-12-10 09:08:29

分布式開源RocketMQ

2020-04-20 13:11:21

HashMap底層存儲

2011-03-10 14:40:54

LAMPMysql

2013-02-28 13:37:59

系統(tǒng)性能調(diào)優(yōu)技術(shù)實戰(zhàn)

2022-02-18 06:56:18

Wi-Fi路由器局域網(wǎng)

2011-03-18 11:21:48

2011-03-18 11:13:07

LAMP度量性能

2013-03-20 17:18:07

Linux系統(tǒng)性能調(diào)優(yōu)

2011-03-21 09:35:38

LAMP調(diào)優(yōu)網(wǎng)絡(luò)文件

2021-07-15 08:00:47

系統(tǒng)性能調(diào)優(yōu)cpunuma架構(gòu)

2011-03-10 14:40:52

2013-03-12 17:33:17

Linux系統(tǒng)性能調(diào)優(yōu)

2023-12-11 08:32:58

數(shù)據(jù)庫DruidDBA

2020-04-28 09:15:58

HashMapJava數(shù)組
點贊
收藏

51CTO技術(shù)棧公眾號