初識計(jì)算機(jī)組成原理-存儲與IO系統(tǒng)篇(二)

機(jī)械硬盤

在很早的時(shí)候計(jì)算機(jī)還沒有硬盤。整個(gè)操作系統(tǒng)都安裝在 5 寸或者 3.5 寸的軟盤里。后來才用上了安裝在主板上的機(jī)械硬盤,到現(xiàn)在磁帶坯苹,軟盤和光盤基本被替代和淘汰了。

機(jī)械硬盤的 IOPS 大概只能做到每秒 100 次左右摇天。

物理構(gòu)造

前面的硬盤構(gòu)造包含接口粹湃,對應(yīng)的控制電路及實(shí)際的 I/O 設(shè)備恐仑。這里的 IO 設(shè)備也就是機(jī)械硬盤

image

如圖为鳄,一塊機(jī)械硬盤是由盤面裳仆、磁頭和懸臂三個(gè)部件組成的。

  1. 盤面:是實(shí)際存儲數(shù)據(jù)的盤片孤钦。盤面上有一層磁性的涂層歧斟。數(shù)據(jù)就存儲在這個(gè)磁性的涂層上。 盤面中間有一個(gè)受電機(jī)控制的轉(zhuǎn)軸偏形。這個(gè)轉(zhuǎn)軸會控制盤面去旋轉(zhuǎn)静袖。 與盤面有關(guān)系的指標(biāo)叫轉(zhuǎn)速,如硬盤有5400 轉(zhuǎn)的壳猜、7200 轉(zhuǎn)的勾徽,乃至 10000 轉(zhuǎn)的。這個(gè)多少多少轉(zhuǎn)统扳,指的就是盤面中間電機(jī)控制的轉(zhuǎn)軸的旋轉(zhuǎn)速度喘帚,英文單位叫RPM,也就是每分鐘的旋轉(zhuǎn)圈數(shù)咒钟。7200RPM吹由,指的就是一旦電腦開機(jī)供電之后,我們的硬盤就可以一直做到每分鐘轉(zhuǎn)上 7200 圈朱嘴。如果折算到每一秒鐘倾鲫,就是 120 圈。
  2. 磁頭:數(shù)據(jù)并不能直接從盤面?zhèn)鬏數(shù)娇偩€上萍嬉,而是通過磁頭乌昔,從盤面上讀取到,然后再通過電路信號傳輸給控制電路壤追、接口磕道,再到總線上的。通常行冰,一個(gè)盤面上會有兩個(gè)磁頭溺蕉,分別在盤面的正反面。盤面在正反兩面都有對應(yīng)的磁性涂層來存儲數(shù)據(jù)悼做,而且一塊硬盤也不是只有一個(gè)盤面疯特,而是上下堆疊了很多個(gè)盤面,各個(gè)盤面之間是平行的肛走。每個(gè)盤面的正反兩面都有對應(yīng)的磁頭漓雅。
  3. 懸臂:懸臂鏈接在磁頭上,并且在一定范圍內(nèi)會去把磁頭定位到盤面的某個(gè)特定的磁道上。
36150DBA-088C-4536-A0EE-4B908605F976.png

一個(gè)盤面通常是圓形的邻吞,由很多個(gè)同心圓組成庶灿,每一圈都是一個(gè)磁道。每個(gè)磁道都有自己的一個(gè)編號吃衅。懸臂其實(shí)只是控制,到底是讀最里面那個(gè)圈的數(shù)據(jù)腾誉,還是最外面圈的數(shù)據(jù)徘层。

一個(gè)磁道,會分成一個(gè)一個(gè)扇區(qū)利职。上下平行的一個(gè)一個(gè)盤面的相同扇區(qū)呢趣效,叫作一個(gè)柱面,讀取數(shù)據(jù)猪贪,兩個(gè)步驟跷敬。

  1. 把盤面旋轉(zhuǎn)到某一個(gè)位置。在這個(gè)位置上热押,懸臂可以定位到整個(gè)盤面的某一個(gè)子區(qū)間西傀。這個(gè)子區(qū)間的形狀有點(diǎn)兒像一塊披薩餅,一般把這個(gè)區(qū)間叫作幾何扇區(qū)桶癣。意思是拥褂,在“幾何位置上”,所有這些扇區(qū)都可以被懸臂訪問到牙寞。
  2. 就是把懸臂移動到特定磁道的特定扇區(qū)饺鹃,也就在這個(gè)“幾何扇區(qū)”里面,找到我們實(shí)際的扇區(qū)间雀。找到之后悔详,磁頭會落下,就可以讀取到正對著扇區(qū)的數(shù)據(jù)惹挟。

這兩部分組成了硬盤隨機(jī)訪問數(shù)據(jù)的時(shí)間耗費(fèi)組成:

  1. 平均延時(shí):這個(gè)時(shí)間茄螃,其實(shí)就是把盤面旋轉(zhuǎn),把幾何扇區(qū)對準(zhǔn)懸臂位置的時(shí)間匪煌。隨機(jī)情況下责蝠,平均找到一個(gè)幾何扇區(qū),需要旋轉(zhuǎn)半圈盤面萎庭。如:7200 轉(zhuǎn)的硬盤霜医,那么一秒里面,就可以旋轉(zhuǎn) 240 個(gè)半圈驳规。那么肴敛,這個(gè)平均延時(shí)就是1s / 240 = 4.17ms
  2. 平均尋道時(shí)間:也就是在盤面旋轉(zhuǎn)之后,懸臂定位到扇區(qū)的的時(shí)間。現(xiàn)在用的 HDD 硬盤的平均尋道時(shí)間一般在 4-10ms医男。

所以可得砸狞,隨機(jī)在整個(gè)硬盤上找一個(gè)數(shù)據(jù),需要 8-14 ms镀梭。一塊 7200 轉(zhuǎn)的硬盤刀森,一秒鐘隨機(jī)的 IO 訪問次數(shù),也就是1s / 8 ms = 125 IOPS 或者 1s / 14ms = 70 IOPS报账,符合前述HDD 硬盤的 IOPS 每秒 100 次左右研底。

相應(yīng)的,如果不是去進(jìn)行隨機(jī)的數(shù)據(jù)訪問透罢,而是進(jìn)行順序的數(shù)據(jù)讀寫榜晦,最大化讀取效率就是:我們可以選擇把順序存放的數(shù)據(jù),盡可能地存放在同一個(gè)柱面上羽圃。這樣乾胶,我們只需要旋轉(zhuǎn)一次盤面,進(jìn)行一次尋道朽寞,就可以去寫入或者讀取识窿,同一個(gè)垂直空間上的多個(gè)盤面的數(shù)據(jù)。如果一個(gè)柱面上的數(shù)據(jù)不夠脑融,也不要去動懸臂腕扶,而是通過電機(jī)轉(zhuǎn)動盤面,這樣就可以順序讀完一個(gè)磁道上的所有數(shù)據(jù)吨掌。所以半抱,其實(shí)對于 HDD 硬盤的順序數(shù)據(jù)讀寫,吞吐率還是很不錯的膜宋,可以達(dá)到 200MB/s 左右窿侈。

Partial Stroking - 根據(jù)場景提升性能

只有 100 的 IOPS,其實(shí)很難滿足現(xiàn)在互聯(lián)網(wǎng)海量高并發(fā)的請求秋茫。所以史简,今天的數(shù)據(jù)庫,都會把數(shù)據(jù)存儲在 SSD 硬盤上肛著。但是過去的 SSD 硬盤卻很昂貴圆兵。數(shù)據(jù)庫里面的數(shù)據(jù),只能存放在 HDD 硬盤上枢贿。今天殉农,即便是數(shù)據(jù)中心用的 HDD 硬盤,一般也是 7200 轉(zhuǎn)的局荚,因?yàn)槿绻斓碾S機(jī)訪問速度超凳,我們會選擇用 SSD 硬盤愈污。但是在當(dāng)時(shí),SSD 硬盤價(jià)格非常昂貴,還沒有能夠商業(yè)化。硬盤廠商們在不斷地研發(fā)轉(zhuǎn)得更快的硬盤囱淋。在數(shù)據(jù)中心里,往往會用上 10000 轉(zhuǎn)豹芯,乃至 15000 轉(zhuǎn)的硬盤。

不過,10000 轉(zhuǎn),乃至 15000 轉(zhuǎn)的硬盤也更昂貴揍魂。如果要節(jié)約成本,提高性價(jià)比棚瘟,只能使用 Partial Stroking 或者 Short Stroking,即“縮短行程”技術(shù)喜最。即縮短硬盤的尋道時(shí)間偎蘸。只要縮短了“平均延時(shí) + 尋道時(shí)間”這兩個(gè)之一,就可以提升 IOPS 了瞬内。

一般情況下迷雪,硬盤的尋道時(shí)間都比平均延時(shí)要長。其中縮短尋道時(shí)間最極端的辦法就是不需要尋道虫蝶,即我們把所有數(shù)據(jù)都放在一個(gè)磁道上章咧。比如始終把磁頭放在最外道的磁道上。這樣能真,我們的尋道時(shí)間就基本為 0赁严,訪問時(shí)間就只有平均延時(shí)了。那樣粉铐,IOPS疼约,就變成了1s / 4ms = 250 IOPS。當(dāng)然只用一個(gè)磁道蝙泼,能存儲的數(shù)據(jù)則很少程剥。實(shí)踐當(dāng)中,可以只用 1/2 或者 1/4 的磁道汤踏,也就是最外面 1/4 或者 1/2 的磁道织鲸。這樣,硬盤可以使用的容量可能變成了 1/2 或者 1/4溪胶。但是尋道時(shí)間搂擦,也變成了 1/4 或者 1/2,因?yàn)閼冶坌枰苿拥摹靶谐獭币沧兂闪嗽瓉淼?1/2 或者 1/4哗脖, IOPS 就能夠大幅度提升了盾饮。

比如說,一塊 7200 轉(zhuǎn)的硬盤,正常情況下丘损,平均延時(shí)是 4.17ms普办,而尋道時(shí)間是 9ms。那么徘钥,它原本的 IOPS 就是1s / (4.17ms + 9ms) = 75.9 IOPS衔蹲。如果我們只用其中 1/4 的磁道,那么呈础,它的 IOPS 就變成了1s / (4.17ms + 9ms/4) = 155.8 IOPS

這樣舆驶,IOPS 提升了一倍,和一塊 15000 轉(zhuǎn)的硬盤的性能差不多了而钞。同樣此時(shí)硬盤能用的空間也只有原來的 1/4 了沙廉。但在當(dāng)時(shí),同樣容量的 15000 轉(zhuǎn)的硬盤的價(jià)格可不止是 7200 轉(zhuǎn)硬盤的 4 倍臼节。所以撬陵,這樣通過軟件去格式化硬盤,只保留部分磁道讓系統(tǒng)可用的情況网缝,可以大大提升硬件的性價(jià)比巨税。

參考:https://haokan.baidu.com/v?vid=8650689199706446390&pd=bjh&fr=bjhauthor&type=video

總結(jié)

機(jī)械硬盤的硬件,主要由盤面粉臊、磁頭和懸臂三部分組成草添。我們的數(shù)據(jù)在盤面上的位置,可以通過磁道扼仲、扇區(qū)和柱面來定位远寸。實(shí)際的一次對于硬盤的訪問,需要把盤面旋轉(zhuǎn)到某一個(gè)“幾何扇區(qū)”屠凶,對準(zhǔn)懸臂的位置而晒。然后,懸臂通過尋道阅畴,把磁頭放到我們實(shí)際要讀取的扇區(qū)上倡怎。

受制于機(jī)械硬盤的結(jié)構(gòu),我們對于隨機(jī)數(shù)據(jù)的訪問速度贱枣,就要包含旋轉(zhuǎn)盤面的平均延時(shí)和移動懸臂的尋道時(shí)間监署。通過這兩個(gè)時(shí)間,我們能計(jì)算出機(jī)械硬盤的 IOPS纽哥。

7200 轉(zhuǎn)機(jī)械硬盤的 IOPS钠乏,只能做到 100 左右。在互聯(lián)網(wǎng)時(shí)代的早期春塌,我們也沒有 SSD 硬盤可以用晓避,所以工程師們就想出了 Partial Stroking 這個(gè)浪費(fèi)存儲空間簇捍,但是可以縮短尋道時(shí)間來提升硬盤的 IOPS 的解決方案。這個(gè)解決方案俏拱,也是一個(gè)典型的暑塑、在深入理解了硬件原理之后的軟件優(yōu)化方案。

SSD

由于無論是 10000 轉(zhuǎn)的企業(yè)級機(jī)械硬盤锅必,還是用 Short Stroking 這樣的方式進(jìn)一步提升 IOPS HDD也已經(jīng)無法滿足使用需求了事格,上面的 Short Stroking 優(yōu)化方式頂死也在 1000 以下提升,而使用 SSD 卻可以輕松突然萬級搞隐,達(dá)到 1W - 2W驹愚,所以后來 SSD 進(jìn)行了商用。況且如果SATA接口的SSD還不夠劣纲,可以換成PCI Express 接口的 SSD逢捺。

SSD 沒有像機(jī)械硬盤那樣的尋道過程,所以它的隨機(jī)讀寫都更快癞季。

image

機(jī)械硬盤的耐用性遠(yuǎn)強(qiáng)于 SSD劫瞳,如果我們需要頻繁地重復(fù)寫入刪除數(shù)據(jù),那么機(jī)械硬盤要比 SSD 性價(jià)比高很多余佛。我們可以簡單地認(rèn)為,因?yàn)?SSD 硬盤類似CPU Cache 用的 SRAM 是用一個(gè)電容來存放一個(gè)比特的數(shù)據(jù)窍荧。它是由一個(gè)電容加上一個(gè)電壓計(jì)組合在一起辉巡,記錄了一個(gè)或者多個(gè)比特。

SLC蕊退、MLC郊楣、TLC 和 QLC

SLC:記錄一個(gè)比特的方式就是:給電容里面充上電有電壓的時(shí)候就是 1,給電容放電里面沒有電就是 0瓤荔。這樣的 SSD 硬盤被稱為使用了 SLC 的顆粒净蚤。 一個(gè)存儲單元中只有一位數(shù)據(jù)。

image

這種方式的問題:和 CPU Cache 類似的問題输硝,那就是今瀑,同樣的面積下,能夠存放下的元器件是有限的点把。
如果只用 SLC橘荠,就會遇到,存儲容量上不去郎逃,并且價(jià)格下不來的問題哥童。后來就陸續(xù)實(shí)現(xiàn)了能在一個(gè)電容里面存下 2 個(gè)、3 個(gè)乃至 4 個(gè)比特 - 通過電壓計(jì)區(qū)分不同電壓褒翰。

image

QLC:4 個(gè)比特一共可以從0000-1111表示16個(gè)不同的數(shù)贮懈。那么匀泊,如果能往電容里面充電的時(shí)候,充上 15 個(gè)不同的電壓朵你,并且電壓計(jì)能夠區(qū)分出這 15 個(gè)不同的電壓各聘。加上電容被放空代表的 0,就能夠代表從 0000-1111 這樣 4 個(gè)比特了撬呢。

這種方式的問題:表示 15 個(gè)不同的電壓伦吠,充電和讀取的時(shí)候,對于精度的要求就會更高魂拦。這會導(dǎo)致充電和讀取的時(shí)候都更慢毛仪,所以 QLC 的 SSD 的讀寫速度,要比 SLC 的慢上好幾倍芯勘。

P/E 擦寫問題

SSD的物理構(gòu)造是自頂向下的箱靴。
image

和其他IO設(shè)備一樣,它有對應(yīng)的接口控制電路荷愕,現(xiàn)在的 SSD 硬盤用的是 SATA 或者 PCI Express 接口衡怀。并且控制電路中有個(gè)閃存置換層FTL,也是 SSD 的核心模塊安疗,SSD 硬盤性能的好壞抛杨,很大程度上也取決于 FTL 的算法的好壞。

現(xiàn)在新的大容量 SSD 硬盤都是 3D 封裝的荐类,即由很多個(gè)裸片疊在一起的怖现,就好像機(jī)械硬盤把很多個(gè)盤面疊放再一起,這樣可以在同樣的空間下放下更多的容量玉罐。

image

一張裸片上可以放多個(gè)平面屈嗤,一般一個(gè)平面上的存儲容量大概在 GB 級別。一個(gè)平面上面吊输,會劃分成很多個(gè)塊饶号,一般一個(gè)塊的存儲大小, 通常幾百 KB 到幾 MB 大小季蚂。一個(gè)塊里面茫船,還會區(qū)分很多個(gè)頁,就和內(nèi)存里面的頁一樣扭屁,一個(gè)頁的大小通常是 4KB透硝。其中處在最下面的兩層塊和頁非常重要。

其中疯搅,對于 SSD 硬盤來說濒生,數(shù)據(jù)的寫入叫Program。寫入不能像機(jī)械硬盤一樣幔欧,通過覆寫來進(jìn)行的罪治,而是要先去擦除丽声,然后再寫入。

  • SSD 的讀取和寫入的基本單位觉义,是一個(gè)頁雁社。
  • SSD 的擦除單位比較大,是按照塊來進(jìn)行的晒骇。

SSD 的使用壽命霉撵,是每一個(gè)塊的擦除的次數(shù)『槎冢可以把 SSD 硬盤的一個(gè)平面看成是一張白紙徒坡。在上面寫入數(shù)據(jù),就好像用鉛筆在白紙上寫字瘤缩。如果想要把已經(jīng)寫過字的地方寫入新的數(shù)據(jù)喇完,先要用橡皮把已經(jīng)寫好的字擦掉。但是剥啤,如果頻繁擦同一個(gè)地方锦溪,那這個(gè)地方就會破掉,之后就沒有辦法再寫字了府怯。

  • SLC 的芯片,可以擦除的次數(shù)大概在 10 萬次
  • MLC 在 1 萬次左右
  • TLC 和 QLC 在幾千次了牺丙。

所以在購買 SSD 硬盤则涯,會看到同樣的容量的價(jià)格差別很大,因?yàn)樗鼈兊男酒w粒和壽命完全不一樣赘被。

SSD 讀寫生命周期

三種顏色分別來表示 SSD 硬盤里面的的不同狀態(tài)

  • 白色代表這個(gè)頁從來沒有寫入過數(shù)據(jù)
  • 綠色代表里面寫入的是有效的數(shù)據(jù)
  • 紅色代表里面的數(shù)據(jù)是整,在操作系統(tǒng)看來已經(jīng)是刪除的了肖揣。
image

一開始民假,所有塊的每一個(gè)頁都是白色的。隨著我們開始往里面寫數(shù)據(jù)龙优,里面的有些頁就變成了綠色羊异。然后,因?yàn)槲覀儎h除了硬盤上的一些文件彤断,所以有些頁變成了紅色野舶。但是這些紅色的頁,并不能再次寫入數(shù)據(jù)宰衙。因?yàn)?SSD 硬盤不能單獨(dú)擦除一個(gè)頁平道,必須一次性擦除整個(gè)塊,所以新的數(shù)據(jù)供炼,我們只能往后面的白色的頁里面寫一屋。這些散落在各個(gè)綠色空間里面的紅色空洞窘疮,就好像硬盤碎片。如果有哪一個(gè)塊的數(shù)據(jù)一次性全部被標(biāo)紅了冀墨,那我們就可以把整個(gè)塊進(jìn)行擦除闸衫。它就又會變成白色,可以重新一頁一頁往里面寫數(shù)據(jù)诽嘉。這種情況其實(shí)也會經(jīng)常發(fā)生蔚出。畢竟一個(gè)塊不大,也就在幾百 KB 到幾 MB虫腋。你刪除一個(gè)幾 MB 的文件骄酗,數(shù)據(jù)又是連續(xù)存儲的,自然會導(dǎo)致整個(gè)塊可以被擦除岔乔。

隨著硬盤里面的數(shù)據(jù)越來越多酥筝,紅色空洞占的地方也會越來越多。于是雏门,你會發(fā)現(xiàn)嘿歌,我們就要沒有白色的空頁去寫入數(shù)據(jù)了。這個(gè)時(shí)候茁影,我們要做一次類似于 Windows 里面“磁盤碎片整理”或者 Java 里面的“內(nèi)存垃圾回收”工作宙帝。找一個(gè)紅色空洞最多的塊,把里面的綠色數(shù)據(jù)募闲,挪到另一個(gè)塊里面去步脓,然后把整個(gè)塊擦除,變成白色浩螺,可以重新寫入數(shù)據(jù)靴患。不過,這個(gè)“磁盤碎片整理”或者“內(nèi)存垃圾回收”的工作要出,我們不能太主動鸳君、太頻繁地去做。因?yàn)?SSD 的擦除次數(shù)是有限的患蹂。如果動不動就搞個(gè)磁盤碎片整理或颊,那么我們的 SSD 硬盤很快就會報(bào)廢了。

這樣就沒辦法把SSD很好的利用传于,解決辦法:- 預(yù)留空間

image

即一塊 SSD 的硬盤容量囱挑,是沒辦法完全用滿的。不過沼溜,為了不得罪消費(fèi)者平挑,生產(chǎn) SSD 硬盤的廠商,其實(shí)是預(yù)留了一部分空間系草,專門用來做這個(gè)“磁盤碎片整理”工作的通熄。一塊標(biāo)成 240G 的 SSD 硬盤否淤,往往實(shí)際有 256G 的硬盤空間。SSD 硬盤通過我們的控制芯片電路棠隐,把多出來的硬盤空間石抡,用來進(jìn)行各種數(shù)據(jù)的閃轉(zhuǎn)騰挪,讓你能夠?qū)憹M那 240G 的空間助泽。這個(gè)多出來的 16G 空間啰扛,叫作預(yù)留空間。一般 SSD 的硬盤的預(yù)留空間都在 7%-15% 左右嗡贺。

SSD 硬盤隐解,特別適合讀多寫少的應(yīng)用。在日常應(yīng)用里面诫睬,我們的系統(tǒng)盤適合用 SSD煞茫。但是,如果我們用 SSD 做專門的下載盤摄凡,一直下載各種影音數(shù)據(jù)续徽,然后刻盤備份就不太好了,特別是現(xiàn)在 QLC 顆粒的 SSD亲澡,它只有幾千次可擦寫的壽命钦扭。

SSD 對于數(shù)據(jù)的寫入,只能是一頁一頁的床绪,不能對頁進(jìn)行覆寫客情。對于數(shù)據(jù)的擦除,只能整塊進(jìn)行癞己。所以膀斋,我們需要用一個(gè),類似“磁盤碎片整理”或者“內(nèi)存垃圾回收”這樣的機(jī)制痹雅,來清理塊當(dāng)中的數(shù)據(jù)空洞仰担。而 SSD 硬盤也會保留一定的預(yù)留空間,避免出現(xiàn)硬盤無法寫滿的情況练慕。

磨損均衡惰匙、TRIM 和寫入放大效應(yīng)

由于SSD受擦除次數(shù)影響技掏,同時(shí)我們計(jì)算機(jī)在使用SSD的過程中铃将,計(jì)算機(jī)并不會把SSD進(jìn)行區(qū)別對待,所以可能會出現(xiàn)如下圖情況哑梳,存放操作系統(tǒng)常用軟件的物理位置不會經(jīng)常擦除塊數(shù)據(jù)劲阎,而SSD盤的其他存儲日常開發(fā)代碼的地方將會經(jīng)常面臨數(shù)據(jù)被擦除的情況。這樣時(shí)間久了鸠真,就會出現(xiàn)SSD硬盤的一部分壞了不可用悯仙,進(jìn)而整個(gè)SSD硬盤的可用空間變小了龄毡。

image

FTL 和磨損均衡

因?yàn)樯鲜銮闆r的發(fā)生,所以會導(dǎo)致一些壞塊的提前出線锡垄。并且還有一些塊擦鞋次數(shù)的浪費(fèi)沦零,所以就有了磨損均衡這個(gè)策略,來讓 SSD 硬盤各個(gè)塊的擦除次數(shù)货岭,均勻分?jǐn)偟礁鱾€(gè)塊上路操。實(shí)現(xiàn)這個(gè)技術(shù)的核心辦法,和虛擬內(nèi)存一樣千贯,就是添加一個(gè)間接層屯仗。這個(gè)間接層,就是 FTL - 閃存置換層搔谴。就像在管理內(nèi)存的時(shí)候魁袜,通過一個(gè)頁表映射虛擬內(nèi)存頁和物理頁一樣,在 FTL 里面敦第,存放了邏輯塊地址物理塊地址的簡單映射峰弹。

image

操作系統(tǒng)訪問的硬盤地址,都是邏輯地址芜果。只有通過 FTL 轉(zhuǎn)換之后垮卓,才會變成實(shí)際的物理地址,找到對應(yīng)的塊進(jìn)行訪問师幕。操作系統(tǒng)本身粟按,不需要去考慮塊的磨損程度,只要和操作機(jī)械硬盤一樣來讀寫數(shù)據(jù)就好了霹粥。操作系統(tǒng)所有對于 SSD 硬盤的讀寫請求灭将,都要經(jīng)過 FTL。FTL 里面有邏輯塊對應(yīng)的物理塊后控,所以 FTL 能夠記錄下來庙曙,每個(gè)物理塊被擦寫的次數(shù)。如果一個(gè)物理塊被擦寫的次數(shù)多了浩淘,F(xiàn)TL 就可以將這個(gè)物理塊捌朴,挪到一個(gè)擦寫次數(shù)少的物理塊上(擦寫次數(shù)多的塊和次數(shù)少的塊互換指針,同時(shí)把其數(shù)據(jù)也轉(zhuǎn)移到次數(shù)少的塊上)张抄。但是砂蔽,邏輯塊不用變,操作系統(tǒng)也不需要知道這個(gè)變化署惯。

這也是我們在設(shè)計(jì)大型系統(tǒng)中的一個(gè)典型思路左驾,也就是各層之間是隔離的,操作系統(tǒng)不需要考慮底層的硬件是什么,完全交由硬件的控制電路里面的 FTL诡右,來管理對于實(shí)際物理硬件的寫入安岂。

TRIM 指令的支持

不過,操作系統(tǒng)不去關(guān)心實(shí)際底層的硬件是什么帆吻,在 SSD 硬盤的使用上域那,也會帶來一個(gè)問題。這個(gè)問題就是猜煮,操作系統(tǒng)的邏輯層和 SSD 的邏輯層里的塊狀態(tài)琉雳,是不匹配的。當(dāng)在操作系統(tǒng)里面去刪除一個(gè)文件友瘤,其實(shí)并沒有真的在物理層面去刪除這個(gè)文件翠肘,只是在文件系統(tǒng)里面,把對應(yīng)的 inode 里面的元信息清理掉辫秧,這代表這個(gè) inode 還可以繼續(xù)使用束倍,可以寫入新的數(shù)據(jù)。這個(gè)時(shí)候盟戏,實(shí)際物理層面的對應(yīng)的存儲空間绪妹,在操作系統(tǒng)里面被標(biāo)記成可以寫入了。

所以柿究,其實(shí)日常的文件刪除邮旷,都只是一個(gè)操作系統(tǒng)層面的邏輯刪除。這也是為什么蝇摸,很多時(shí)候我們不小心刪除了對應(yīng)的文件婶肩,我們可以通過各種恢復(fù)軟件,把數(shù)據(jù)找回來貌夕。同樣的律歼,這也是為什么,如果我們想要刪除干凈數(shù)據(jù)啡专,需要用各種“文件粉碎”的功能才行险毁。

這個(gè)刪除的邏輯在機(jī)械硬盤層面沒有問題,因?yàn)闄C(jī)械硬盤可以復(fù)寫數(shù)據(jù)们童,因?yàn)槲募粯?biāo)記成可以寫入畔况,后續(xù)的寫入可以直接覆寫這個(gè)位置,不需要真實(shí)的把文件刪除慧库。而SSD硬盤是不一樣的奸远。

image

當(dāng)我們在操作系統(tǒng)里面烁试,刪除掉一個(gè)剛剛下載的文件佩捞,比如標(biāo)記成黃色 openjdk.exe 這樣一個(gè) jdk 的安裝文件携兵,在操作系統(tǒng)里面,對應(yīng)的 inode 里面覆积,就沒有文件的元信息听皿。但是,這個(gè)時(shí)候宽档,SSD 的邏輯塊層面尉姨,其實(shí)并不知道上層操作系統(tǒng)的這個(gè)事情。所以在FTL的邏輯塊層面吗冤,openjdk.exe 仍然是占用了對應(yīng)的空間又厉。對應(yīng)的物理頁,也仍然被認(rèn)為是被占用了的椎瘟。

這個(gè)時(shí)候覆致,如果我們需要對 SSD 進(jìn)行垃圾回收操作,openjdk.exe 對應(yīng)的物理頁肺蔚,仍然要在這個(gè)過程中煌妈,被搬運(yùn)到其他的 Block 里面去(因?yàn)椴恢肋@個(gè)物理塊中的數(shù)據(jù)已經(jīng)是無效數(shù)據(jù))。只有當(dāng)操作系統(tǒng)宣羊,再在剛才的 inode 里面寫入數(shù)據(jù)的時(shí)候璧诵,我們才會知道原來的些黃色的頁,其實(shí)都已經(jīng)沒有用了仇冯,我們才會把它標(biāo)記成廢棄掉之宿。所以,在使用 SSD 的硬盤情況下苛坚,操作系統(tǒng)對于文件的刪除比被,SSD 硬盤其實(shí)并不知道。這就導(dǎo)致泼舱,我們?yōu)榱四p均衡姐赡,很多時(shí)候在都在搬運(yùn)很多已經(jīng)刪除了的數(shù)據(jù)。這就會產(chǎn)生很多不必要的數(shù)據(jù)讀寫和擦除柠掂,既消耗了 SSD 的性能项滑,也縮短了 SSD 的使用壽命。

為了解決這個(gè)問題涯贞,現(xiàn)在的操作系統(tǒng)和 SSD 的主控芯片枪狂,都支持TRIM 命令。這個(gè)命令可以在文件被刪除的時(shí)候宋渔,讓操作系統(tǒng)去通知 SSD 硬盤州疾,對應(yīng)的邏輯塊已經(jīng)標(biāo)記成已刪除了。現(xiàn)在的 SSD 硬盤都已經(jīng)支持了 TRIM 命令皇拣。

如果沒有這個(gè)命令的通知严蓖,在操作系統(tǒng)層面邏輯刪除一個(gè)塊上的數(shù)據(jù)之后薄嫡,SSD并不知情,在操作系統(tǒng)下次再在該位置寫入數(shù)據(jù)前颗胡,SSD都不會知曉這些數(shù)據(jù)是無效數(shù)據(jù)毫深。而會因?yàn)樵搲K的磨損均衡策略進(jìn)而對塊中有效數(shù)據(jù)進(jìn)行遷移,所以毒姨,這些數(shù)據(jù)會被當(dāng)成有效數(shù)據(jù)來進(jìn)行無效的遷移哑蔫。進(jìn)而影響了SSD性能和磨損數(shù)。

寫入放大

TRIM 命令的發(fā)明弧呐,也反應(yīng)了一個(gè)使用 SSD 硬盤的問題闸迷,那就是,SSD 硬盤容易越用越慢俘枫。當(dāng) SSD 硬盤的存儲空間被占用得越來越多腥沽,每一次寫入新數(shù)據(jù),都可能沒有足夠的空白鸠蚪⊙睬颍可能不得不去進(jìn)行垃圾回收,合并一些塊里面的頁邓嘹,然后再擦除掉一些頁酣栈,才能勻出一些空間來。 這個(gè)時(shí)候汹押,從應(yīng)用層或者操作系統(tǒng)層面來看矿筝,可能只是寫入了一個(gè) 4KB 或者 4MB 的數(shù)據(jù)。但是棚贾,實(shí)際通過 FTL 之后窖维,可能要去搬運(yùn) 8MB、16MB 甚至更多的數(shù)據(jù)妙痹。

可以通過铸史,實(shí)際的閃存寫入的數(shù)據(jù)量 / 系統(tǒng)通過 FTL 寫入的數(shù)據(jù)量(遷移) = 寫入放大,可以得到怯伊,寫入放大的倍數(shù)越多琳轿,意味著實(shí)際的 SSD 性能也就越差,會遠(yuǎn)遠(yuǎn)比不上實(shí)際 SSD 硬盤標(biāo)稱的指標(biāo)耿芹。而解決寫入放大崭篡,需要我們在后臺定時(shí)進(jìn)行垃圾回收,在硬盤比較空閑的時(shí)候吧秕,就把搬運(yùn)數(shù)據(jù)琉闪、擦除數(shù)據(jù)、留出空白的塊的工作做完砸彬,而不是等實(shí)際數(shù)據(jù)寫入的時(shí)候颠毙,再進(jìn)行這樣的操作斯入。

AeroSpike

所以其實(shí)用好SSD硬盤十分的困難。無法單純的通過替換HDD來有效利用SSD的高性能蛀蜜,除非合理利用好它的特性刻两。而AeroSpike這樣的KV數(shù)據(jù)庫就是一個(gè)好的實(shí)現(xiàn)。

首先涵防,AeroSpike 操作 SSD 硬盤闹伪,并沒有通過操作系統(tǒng)的文件系統(tǒng)沪铭。而是直接操作 SSD 里面的塊和頁壮池。因?yàn)椴僮飨到y(tǒng)里面的文件系統(tǒng),對于 KV 數(shù)據(jù)庫來說杀怠,只是讓我們多了一層間接層椰憋,只會降低性能,對我們沒有什么實(shí)際的作用赔退。

其次橙依,AeroSpike 在讀寫數(shù)據(jù)的時(shí)候,做了兩個(gè)優(yōu)化硕旗。在寫入數(shù)據(jù)的時(shí)候窗骑,AeroSpike 盡可能去寫一個(gè)較大的數(shù)據(jù)塊,而不是頻繁地去寫很多小的數(shù)據(jù)塊漆枚。這樣创译,硬盤就不太容易頻繁出現(xiàn)磁盤碎片。并且墙基,一次性寫入一個(gè)大的數(shù)據(jù)塊软族,也更容易利用好順序?qū)懭氲男阅軆?yōu)勢。AeroSpike 寫入的一個(gè)數(shù)據(jù)塊残制,是 128KB立砸,遠(yuǎn)比一個(gè)頁的 4KB 要大得多。

另外初茶,在讀取數(shù)據(jù)的時(shí)候颗祝,AeroSpike 倒是可以讀取 512 字節(jié)這樣的小數(shù)據(jù)。因?yàn)?SSD 的隨機(jī)讀取性能很好恼布,也不像寫入數(shù)據(jù)那樣有擦除壽命問題吐葵。而且,很多時(shí)候我們讀取的數(shù)據(jù)是鍵值對里面的值的數(shù)據(jù)桥氏,這些數(shù)據(jù)要在網(wǎng)絡(luò)上傳輸温峭。如果一次性必須讀出比較大的數(shù)據(jù),就會導(dǎo)致我們的網(wǎng)絡(luò)帶寬不夠用字支。

因?yàn)?AeroSpike 是一個(gè)對于響應(yīng)時(shí)間要求很高的實(shí)時(shí) KV 數(shù)據(jù)庫凤藏,如果出現(xiàn)了嚴(yán)重的寫放大效應(yīng)奸忽,會導(dǎo)致寫入數(shù)據(jù)的響應(yīng)時(shí)間大幅度變長。所以 AeroSpike 做了這樣幾個(gè)動作:

  1. 持續(xù)地進(jìn)行磁盤碎片整理揖庄。AeroSpike 用了所謂的高水位算法栗菜。其實(shí)這個(gè)算法很簡單,就是一旦一個(gè)物理塊里面的數(shù)據(jù)碎片超過 50%蹄梢,就把這個(gè)物理塊搬運(yùn)壓縮疙筹,然后進(jìn)行數(shù)據(jù)擦除,確保磁盤始終有足夠的空間可以寫入禁炒。
  2. 是在 AeroSpike 給出的最佳實(shí)踐中而咆,為了保障數(shù)據(jù)庫的性能,建議你只用到 SSD 硬盤標(biāo)定容量的一半幕袱。也就是說暴备,我們?nèi)藶榈亟o SSD 硬盤預(yù)留了 50% 的預(yù)留空間,以確保 SSD 硬盤的寫放大效應(yīng)盡可能小们豌,不會影響數(shù)據(jù)庫的訪問性能涯捻。
image

正是因?yàn)樽隽诉@種種的優(yōu)化,在 NoSQL 數(shù)據(jù)庫剛剛興起的時(shí)候望迎,AeroSpike 的性能把 Cassandra障癌、MongoDB 這些數(shù)據(jù)庫遠(yuǎn)遠(yuǎn)甩在身后,和這些數(shù)據(jù)庫之間的性能差距辩尊,有時(shí)候會到達(dá)一個(gè)數(shù)量級涛浙。這也讓 AeroSpike 成為了當(dāng)時(shí)高性能 KV 數(shù)據(jù)庫的標(biāo)桿。

問題

AeroSpike為什么現(xiàn)在的受歡迎程度不如Redis对省?

作者回復(fù):

你好蝗拿,其實(shí)AeroSpike據(jù)我所知在國內(nèi)外應(yīng)用都很普遍了。沒有Redis火的核心原因我覺得是因?yàn)殚_源得晚了蒿涎。

另外哀托,就是對于大部分?jǐn)?shù)據(jù)量沒有那么大的創(chuàng)業(yè)公司,用內(nèi)存作為緩存劳秋,存儲空間也就夠了仓手,那用Redis也就足夠了,暫時(shí)還用不上AeroSpike玻淑。

DMA

DMA:即便有了 PCI Express 接口的 SSD嗽冒,但比起 CPU,總還是太慢补履。所以如果對于 I/O 的操作添坊,都是由 CPU 發(fā)出對應(yīng)的指令,然后等待 I/O 設(shè)備完成操作之后返回箫锤,那 CPU 有大量的時(shí)間其實(shí)都是在等待 I/O 設(shè)備完成操作贬蛙。并沒有實(shí)際的意義雨女。并且我們對于 I/O 設(shè)備的大量操作,其實(shí)都只是把內(nèi)存里面的數(shù)據(jù)阳准,傳輸?shù)?I/O 設(shè)備而已氛堕。在這種情況下,其實(shí) CPU 只是在傻等而已野蝇。特別是當(dāng)傳輸?shù)臄?shù)據(jù)量比較大的時(shí)候讼稚,比如進(jìn)行大文件復(fù)制,如果所有數(shù)據(jù)都要經(jīng)過 CPU绕沈,實(shí)在是有點(diǎn)兒太浪費(fèi)時(shí)間了锐想。所以后來發(fā)明了 DMA 技術(shù),直接內(nèi)存訪問(Direct Memory Access)七冲,來減少 CPU 等待的時(shí)間痛倚。

DMA技術(shù):即在主板上放一塊獨(dú)立的芯片规婆,在進(jìn)行內(nèi)存和 I/O 設(shè)備的數(shù)據(jù)傳輸?shù)臅r(shí)候澜躺,不再通過 CPU 來控制數(shù)據(jù)傳輸,而直接通過 DMA 控制器抒蚜。這塊芯片掘鄙,就是一個(gè)==協(xié)處理器==。 協(xié)助 CPU 完成對應(yīng)的數(shù)據(jù)傳輸工作嗡髓。

DMAC 最有價(jià)值的地方體現(xiàn)在操漠,當(dāng)要傳輸?shù)臄?shù)據(jù)特別大、速度特別快饿这,或者傳輸?shù)臄?shù)據(jù)特別小浊伙、速度特別慢的時(shí)候。比如說长捧,我們用千兆網(wǎng)卡或者硬盤傳輸大量數(shù)據(jù)的時(shí)候嚣鄙,如果都用 CPU 來搬運(yùn)的話,肯定忙不過來串结,所以可以選擇 DMAC哑子。而當(dāng)數(shù)據(jù)傳輸很慢的時(shí)候,DMAC 可以等數(shù)據(jù)到齊了肌割,再發(fā)送信號卧蜓,給到 CPU 去處理,而不是讓 CPU 在那里忙等待把敞。

DMAC 也是一個(gè)特殊的 I/O 設(shè)備弥奸,它和 CPU 以及其他 I/O 設(shè)備一樣,通過連接到總線來進(jìn)行實(shí)際的數(shù)據(jù)傳輸奋早。

總線上的設(shè)備盛霎,有兩種類型冒冬。而 DMAC 既是主設(shè)備,又是從設(shè)備摩渺。

  • 主設(shè)備:想要主動發(fā)起數(shù)據(jù)傳輸简烤,必須要是一個(gè)主設(shè)備才可以,CPU 就是主設(shè)備摇幻。
  • 從設(shè)備:從設(shè)備(比如硬盤)只能接受數(shù)據(jù)傳輸横侦。所以,如果通過 CPU 來傳輸數(shù)據(jù)绰姻,要么是 CPU 從 I/O 設(shè)備讀數(shù)據(jù)枉侧,要么是 CPU 向 I/O 設(shè)備寫數(shù)據(jù)。一定是 CPU 主設(shè)備主動

從設(shè)備也可以向主設(shè)備發(fā)起請求狂芋,但不是發(fā)送的數(shù)據(jù)內(nèi)容榨馁,而是控制信號。I/O 設(shè)備可以告訴 CPU帜矾,我這里有數(shù)據(jù)要傳輸給你翼虫,但是實(shí)際數(shù)據(jù)是 CPU 拉走的,而不是 I/O 設(shè)備推給 CPU 的屡萤。

image

DMAC 對于 CPU 來說珍剑,它是一個(gè)從設(shè)備;對于硬盤這樣的 IO 設(shè)備來說呢死陆,它又變成了一個(gè)主設(shè)備招拙。

使用 DMAC 進(jìn)行數(shù)據(jù)傳輸?shù)倪^程:

  1. 首先,CPU 還是作為一個(gè)主設(shè)備措译,向 DMAC 設(shè)備發(fā)起請求别凤。這個(gè)請求,其實(shí)就是在 DMAC 里面修改配置寄存器领虹。
  2. CPU 修改 DMAC 的配置的時(shí)候规哪,會告訴 DMAC 這樣幾個(gè)信息:
    1. 源地址的初始值以及傳輸時(shí)候的地址增減方式
      1. 源地址:即數(shù)據(jù)要從哪里傳輸過來掠械。如果我們要從內(nèi)存里面寫入數(shù)據(jù)到硬盤上由缆,那么就是要讀取的數(shù)據(jù)在內(nèi)存里面的地址。如果是從硬盤讀取數(shù)據(jù)到內(nèi)存里猾蒂,那就是硬盤的 I/O 接口的地址均唉。
      2. 地址的增減方式:即數(shù)據(jù)是從大的地址向小的地址傳輸,還是從小的地址往大的地址傳輸肚菠。
    2. 目標(biāo)地址初始值傳輸時(shí)候的地址增減方式舔箭。
    3. 要傳輸?shù)臄?shù)據(jù)長度。也就是一共要傳輸多少數(shù)據(jù)。
  3. 設(shè)置完這些信息之后层扶,DMAC 就會變成一個(gè)空閑的狀態(tài)箫章。
  4. 如果要從硬盤上往內(nèi)存里面加載數(shù)據(jù),這個(gè)時(shí)候镜会,硬盤就會向 DMAC 發(fā)起一個(gè)數(shù)據(jù)傳輸請求檬寂。==這個(gè)請求并不是通過總線,而是通過一個(gè)額外的連線==戳表。
  5. 然后桶至,DMAC 需要再通過一個(gè)額外的連線響應(yīng)這個(gè)申請
  6. 于是匾旭,DMAC 這個(gè)芯片镣屹,就向硬盤的接口發(fā)起要總線讀的傳輸請求。數(shù)據(jù)就從硬盤里面价涝,讀到了 DMAC 的控制器里面女蜈。
  7. 然后,DMAC 再向我們的內(nèi)存發(fā)起總線寫的數(shù)據(jù)傳輸請求色瘩,把數(shù)據(jù)寫入到內(nèi)存里面伪窖。
  8. DMAC 會反復(fù)進(jìn)行上面第 6、7 步的操作泞遗,直到 DMAC 的寄存器里面設(shè)置的數(shù)據(jù)長度傳輸完成惰许。
  9. 數(shù)據(jù)傳輸完成之后席覆,DMAC 重新回到第 3 步的空閑狀態(tài)史辙。

所以,整個(gè)數(shù)據(jù)傳輸?shù)倪^程中佩伤,我們不是通過 CPU 來搬運(yùn)數(shù)據(jù)生巡,而是由 DMAC 這個(gè)芯片來搬運(yùn)數(shù)據(jù)钱豁。但是 CPU 在這個(gè)過程中也是必不可少的谤碳。因?yàn)閭鬏斒裁磾?shù)據(jù)搓茬,從哪里傳輸?shù)侥睦锶干冢鋵?shí)還是由 CPU 來設(shè)置的捌浩。這也是為什么,DMAC 被叫作“協(xié)處理器”。

image

最早愿吹,計(jì)算機(jī)里是沒有 DMAC 的撵颊,所有數(shù)據(jù)都是由 CPU 來搬運(yùn)的妻熊。隨著人們對于數(shù)據(jù)傳輸?shù)男枨笤絹碓蕉嘣せ剩仁浅霈F(xiàn)了主板上獨(dú)立的 DMAC 控制器。到了今天堤尾,各種 I/O 設(shè)備越來越多卜范,數(shù)據(jù)傳輸?shù)男枨笤絹碓綇?fù)雜,使用的場景各不相同。加之顯示器埠啃、網(wǎng)卡死宣、硬盤對于數(shù)據(jù)傳輸?shù)男枨蠖疾灰粯樱愿鱾€(gè)設(shè)備里面都有自己的 DMAC 芯片了碴开。

Kafka

Kafka 就很好的利用了 DMA 的數(shù)據(jù)傳輸方式十电,通過 DMA 實(shí)現(xiàn)了非常大的性能提升。

Kafka 是一個(gè)用來處理實(shí)時(shí)數(shù)據(jù)的管道叹螟,我們常常用它來做一個(gè)消息隊(duì)列鹃骂,或者用來收集和落地海量的日志。作為一個(gè)處理實(shí)時(shí)數(shù)據(jù)和日志的管道罢绽,瓶頸自然也在 I/O 層面畏线。

Kafka 里面會有兩種常見的海量數(shù)據(jù)傳輸?shù)那闆r。一種是從網(wǎng)絡(luò)中接收上游的數(shù)據(jù)良价,然后需要落地到本地的磁盤上寝殴,確保數(shù)據(jù)不丟失。另一種情況呢明垢,則是從本地磁盤上讀取出來蚣常,通過網(wǎng)絡(luò)發(fā)送出去。

后一種情況:從磁盤讀數(shù)據(jù)發(fā)送到網(wǎng)絡(luò)上去痊银。最直觀的辦法抵蚊,用一個(gè)文件讀操作,從磁盤上把數(shù)據(jù)讀到內(nèi)存里面來溯革,然后再用一個(gè) Socket贞绳,把這些數(shù)據(jù)發(fā)送到網(wǎng)絡(luò)上去。

File.read(fileDesc, buf, len);
Socket.send(socket, buf, len);

在這個(gè)過程中致稀,看似兩步操作冈闭,實(shí)則數(shù)據(jù)一共發(fā)生了四次傳輸?shù)倪^程。其中兩次是 DMA 的傳輸抖单,另外兩次萎攒,則是通過 CPU 控制的傳輸遇八。

  1. 第一次傳輸,是從硬盤上耍休,讀到操作系統(tǒng)內(nèi)核的緩沖區(qū)里刃永。這個(gè)傳輸是通過 ==DMA 搬運(yùn)==的。
  2. 第二次傳輸羹应,需要從內(nèi)核緩沖區(qū)里面的數(shù)據(jù)揽碘,復(fù)制到我們應(yīng)用分配的內(nèi)存里面。這個(gè)傳輸是通過 ==CPU 搬運(yùn)==的园匹。
  3. 第三次傳輸雳刺,要從我們應(yīng)用的內(nèi)存里面,再寫到操作系統(tǒng)的 Socket 的緩沖區(qū)里面去裸违。這個(gè)傳輸掖桦,還是由 ==CPU 搬運(yùn)==的。
  4. 最后一次傳輸供汛,需要再從 Socket 的緩沖區(qū)里面枪汪,寫到網(wǎng)卡的緩沖區(qū)里面去。這個(gè)傳輸又是通過 ==DMA 搬運(yùn)==的怔昨。
image

這種傳輸數(shù)據(jù)的方式是比較大的雀久,只是一次簡單的傳輸卻需要4次數(shù)據(jù)的運(yùn)輸。其中上述的4次運(yùn)輸步驟趁舀,從內(nèi)核的讀緩沖區(qū)傳輸?shù)綉?yīng)用的內(nèi)存里赖捌,再從應(yīng)用的內(nèi)存里傳輸?shù)?Socket 的緩沖區(qū)里,其實(shí)都是把同一份數(shù)據(jù)在內(nèi)存里面進(jìn)行運(yùn)轉(zhuǎn)矮烹,沒有效率越庇。

像 Kafka 這樣的應(yīng)用場景,其實(shí)大部分最終利用到的硬件資源奉狈,其實(shí)又都是在干這個(gè)搬運(yùn)數(shù)據(jù)的事兒卤唉。所以,我們就需要盡可能地減少數(shù)據(jù)搬運(yùn)的需求仁期。而Kafka 做的事情就是桑驱,把這個(gè)數(shù)據(jù)搬運(yùn)的次數(shù),從上面的四次蟀拷,變成了兩次碰纬,并且只有 DMA 來進(jìn)行數(shù)據(jù)搬運(yùn),而不需要 CPU问芬。

@Override
public long transferFrom(FileChannel fileChannel, long position, long count) throws IOException {
    return fileChannel.transferTo(position, count, socketChannel);
}

Kafka 的源碼,最終調(diào)用了 Java NIO 庫里的 transferTo 方法

Kafka 的代碼調(diào)用了 Java NIO 庫寿桨,具體是 FileChannel 里面的 transferTo 方法此衅。我們的數(shù)據(jù)并沒有讀到中間的應(yīng)用內(nèi)存里面强戴,而是直接通過 Channel,寫入到對應(yīng)的網(wǎng)絡(luò)設(shè)備里挡鞍。并且骑歹,對于 Socket 的操作,也不是寫入到 Socket 的 Buffer 里面墨微,而是直接根據(jù)描述符(Descriptor)寫入到網(wǎng)卡的緩沖區(qū)里面道媚。于是,在這個(gè)過程之中翘县,我們只進(jìn)行了兩次數(shù)據(jù)傳輸最域。省去了數(shù)據(jù)在內(nèi)存中的滯留過程。

image
  1. 第一次锈麸,是通過 DMA镀脂,從硬盤直接讀到操作系統(tǒng)內(nèi)核的讀緩沖區(qū)里面
  2. 第二次忘伞,則是根據(jù) Socket 的描述符信息薄翅,直接從讀緩沖區(qū)里面,寫入到網(wǎng)卡的緩沖區(qū)里面氓奈。

這樣翘魄,我們同一份數(shù)據(jù)傳輸?shù)拇螖?shù)從四次變成了兩次,并且沒有通過 CPU 來進(jìn)行數(shù)據(jù)搬運(yùn)舀奶,所有的數(shù)據(jù)都是通過 DMA 來進(jìn)行傳輸?shù)摹?/strong> 在這個(gè)方法里面暑竟,沒有在內(nèi)存層面去“復(fù)制”數(shù)據(jù),所以這個(gè)方法伪节,也被稱之為零拷貝光羞。

IBM Developer Works 里面有一篇文章,專門寫過程序來測試過怀大,在同樣的硬件下纱兑,使用零拷貝能夠帶來的性能提升。我在這里放上這篇文章鏈接化借。在這篇文章最后递鹉,你可以看到,無論傳輸數(shù)據(jù)量的大小绪励,傳輸同樣的數(shù)據(jù)黎烈,使用了零拷貝能夠縮短 65% 的時(shí)間,大幅度提升了機(jī)器傳輸數(shù)據(jù)的吞吐量蒜焊。想要深入了解零拷貝倒信,建議你可以仔細(xì)讀一讀這篇文章。

總結(jié)

如果我們始終讓 CPU 來進(jìn)行各種數(shù)據(jù)傳輸工作泳梆,會特別浪費(fèi)鳖悠。一方面榜掌,我們的數(shù)據(jù)傳輸工作用不到多少 CPU 核心的“計(jì)算”功能。另一方面乘综,CPU 的運(yùn)轉(zhuǎn)速度也比 I/O 操作要快很多憎账。所以,我們希望能夠給 CPU“減負(fù)”卡辰。

于是胞皱,工程師們就在主板上放上了 DMAC 這樣一個(gè)協(xié)處理器芯片。通過這個(gè)芯片九妈,CPU 只需要告訴 DMAC反砌,我們要傳輸什么數(shù)據(jù),從哪里來允蚣,到哪里去于颖,就可以放心離開了。后續(xù)的實(shí)際數(shù)據(jù)傳輸工作嚷兔,都會由 DMAC 來完成森渐。隨著現(xiàn)代計(jì)算機(jī)各種外設(shè)硬件越來越多,光一個(gè)通用的 DMAC 芯片不夠了冒晰,我們在各個(gè)外設(shè)上都加上了 DMAC 芯片同衣,使得 CPU 很少再需要關(guān)心數(shù)據(jù)傳輸?shù)墓ぷ髁恕?/p>

在我們實(shí)際的系統(tǒng)開發(fā)過程中,利用好 DMA 的數(shù)據(jù)傳輸機(jī)制壶运,也可以大幅提升 I/O 的吞吐率耐齐。最典型的例子就是 Kafka。

傳統(tǒng)地從硬盤讀取數(shù)據(jù)蒋情,然后再通過網(wǎng)卡向外發(fā)送埠况,我們需要進(jìn)行四次數(shù)據(jù)傳輸,其中有兩次是發(fā)生在內(nèi)存里的緩沖區(qū)和對應(yīng)的硬件設(shè)備之間棵癣,我們沒法節(jié)省掉辕翰。但是還有兩次,完全是通過 CPU 在內(nèi)存里面進(jìn)行數(shù)據(jù)復(fù)制狈谊。

在 Kafka 里喜命,通過 Java 的 NIO 里面 FileChannel 的 transferTo 方法調(diào)用,我們可以不用把數(shù)據(jù)復(fù)制到我們應(yīng)用程序的內(nèi)存里面河劝。通過 DMA 的方式壁榕,我們可以把數(shù)據(jù)從內(nèi)存緩沖區(qū)直接寫到網(wǎng)卡的緩沖區(qū)里面。在使用了這樣的零拷貝的方法之后呢赎瞎,我們傳輸同樣數(shù)據(jù)的時(shí)間牌里,可以縮減為原來的 1/3,相當(dāng)于提升了 3 倍的吞吐率务甥。

這也是為什么二庵,Kafka 是目前實(shí)時(shí)數(shù)據(jù)傳輸管道的標(biāo)準(zhǔn)解決方案贪染。

數(shù)據(jù)完整性 - 單比特反轉(zhuǎn)

單比特翻轉(zhuǎn):比如內(nèi)存中某一數(shù)據(jù)的二進(jìn)制表示是00100100缓呛,遇到單比特翻轉(zhuǎn)問題之后則會變成00110100催享。

內(nèi)存里面的單比特翻轉(zhuǎn)或者錯誤,并不是一個(gè)特別罕見的現(xiàn)象哟绊。無論是因?yàn)?strong>內(nèi)存的制造質(zhì)量造成的漏電因妙,還是外部的射線,都有一定的概率票髓,會造成單比特錯誤攀涵。而內(nèi)存層面的數(shù)據(jù)出錯,軟件工程師并不知道洽沟,而且這個(gè)出錯很有可能是隨機(jī)的以故。

image

解決辦法:

奇偶校驗(yàn)把內(nèi)存里面的 N 位比特當(dāng)成是一組。常見的裆操,比如 8 位就是一個(gè)字節(jié)怒详。然后,用額外的一位去記錄踪区,這 8 個(gè)比特里面有奇數(shù)個(gè) 1 還是偶數(shù)個(gè) 1昆烁。如果是奇數(shù)個(gè) 1,那額外的一位就記錄為 1缎岗;如果是偶數(shù)個(gè) 1静尼,那額外的一位就記錄成 0。這個(gè)額外的一位传泊,稱之為校驗(yàn)碼位鼠渺。

image

如圖,如果這個(gè)字節(jié)中發(fā)生了單比特翻轉(zhuǎn)眷细,那么校驗(yàn)碼位就和實(shí)際的校驗(yàn)碼位不一致了拦盹。就知道該數(shù)據(jù)出錯了。但同樣薪鹦,這種方法還有其他缺陷掌敬。

  1. 奇偶校驗(yàn)只能解決遇到單個(gè)位的錯誤,或者說奇數(shù)個(gè)位的錯誤池磁。
  2. 它只能發(fā)現(xiàn)錯誤奔害,但是不能糾正錯誤。即使在內(nèi)存里面發(fā)現(xiàn)數(shù)據(jù)錯誤了地熄,我們也只能中止程序华临,而不能讓程序繼續(xù)正常地運(yùn)行下去。

所以端考,為了彌補(bǔ)上述兩個(gè)問題的解決辦法出來了雅潭。ECC揭厚,ECC 內(nèi)存的全稱是 Error-Correcting Code memory,中文名字叫作糾錯內(nèi)存扶供。顧名思義筛圆,就是在內(nèi)存里面出現(xiàn)錯誤的時(shí)候,能夠自己糾正過來椿浓。并且它能夠發(fā)現(xiàn)更多位的錯誤太援。

不僅能捕捉到錯誤,還要能夠糾正發(fā)生的錯誤扳碍。這個(gè)策略提岔,通常叫作糾錯碼。它還有一個(gè)升級版本笋敞,叫作糾刪碼碱蒙,不僅能夠糾正錯誤,還能夠在錯誤不能糾正的時(shí)候夯巷,直接把數(shù)據(jù)刪除赛惩。無論是 ECC 內(nèi)存,還是網(wǎng)絡(luò)傳輸鞭莽,乃至硬盤的 RAID坊秸,其實(shí)都利用了糾錯碼和糾刪碼的相關(guān)技術(shù)。

==數(shù)據(jù)量和負(fù)載上來澎怒,沒有ECC的話褒搔,單比特翻轉(zhuǎn)其實(shí)是一個(gè)大概率發(fā)生的故障。==


從老師的描述看單比特翻轉(zhuǎn)問題的概率不低啊喷面,但是大部分PC機(jī)都沒有用ECC星瘾,為什么PC機(jī)很少聽說有出現(xiàn)這個(gè)問題帶來的bug?

作者回復(fù): 有銘同學(xué)惧辈,

你好琳状,這有兩種情況:

  1. 第一種是PC實(shí)際的負(fù)載比服務(wù)器低很多,大部分時(shí)間你的PC是很空閑的盒齿,CPU占用率和內(nèi)存使用率都不高念逞,也沒有什么東西在計(jì)算。而服務(wù)器常常是24小時(shí)高負(fù)載在運(yùn)轉(zhuǎn)的边翁。服務(wù)器可能一天進(jìn)行的計(jì)算量比你PC一年還多翎承。數(shù)據(jù)中心里又有可能同時(shí)有1000臺計(jì)算機(jī),意味著服務(wù)器一天遇到的問題可能PC要一輩子才遇到一次符匾。

  2. 第二是很多時(shí)候發(fā)生了你沒有意識到叨咖,比如程序忽然Crash了,機(jī)器藍(lán)屏重啟了,甚至有程序數(shù)據(jù)錯了甸各,你并會關(guān)心到哪個(gè)是單比特翻轉(zhuǎn)引起的垛贤。

參考:「單比特錯誤」真的會造成整個(gè)程序乃至計(jì)算機(jī)系統(tǒng)的崩潰么?現(xiàn)代計(jì)算機(jī)有哪些檢錯趣倾、糾錯機(jī)制聘惦?

數(shù)據(jù)完整性 - 海明碼

海明碼解決了上述檢錯碼的遺留問題,它不僅可以排查出錯誤誊酌,并且能排查到哪里出錯了部凑。 它也叫糾錯碼糾錯碼需要更多的冗余信息碧浊,通過這些冗余信息,不僅可以知道哪里的數(shù)據(jù)錯了瘟仿,還能直接把數(shù)據(jù)給改對箱锐。 ECC 內(nèi)存也還在用海明碼來糾錯。

最基礎(chǔ)的海明碼叫7-4 海明碼劳较。這里的“7”指的是實(shí)際有效的數(shù)據(jù)驹止,一共是 7 位。而這里的“4”观蜗,指的是我們額外存儲了 4 位數(shù)據(jù)臊恋,用來糾錯,但糾錯碼的糾錯能力是有限的墓捻,在 7-4 海明碼里面抖仅,我們只能糾正某 1 位的錯誤。

4 位的校驗(yàn)碼砖第,一共可以表示 2^4 = 16 個(gè)不同的數(shù)撤卢。根據(jù)數(shù)據(jù)位計(jì)算出來的校驗(yàn)值,一定是確定的梧兼。所以放吩,如果數(shù)據(jù)位出錯了,計(jì)算出來的校驗(yàn)碼羽杰,一定和確定的那個(gè)校驗(yàn)碼不同渡紫。那可能的值,就是在 2^4 - 1 = 15 那剩下的 15 個(gè)可能的校驗(yàn)值當(dāng)中考赛。

15 個(gè)可能的校驗(yàn)值惕澎,對應(yīng) 15 個(gè)可能出錯的位。對于數(shù)據(jù)位的單比特翻轉(zhuǎn)錯誤欲虚,其實(shí)3位就夠了2^3 - 1 = 7集灌,但同樣的校驗(yàn)位也會出錯。所以,7 位數(shù)據(jù)位和 3 位校驗(yàn)位欣喧,如果只有單比特出錯腌零,可能出錯的位數(shù)就是 10 位,2^3 - 1 = 7唆阿,就無法定位是哪一位出錯了益涧。它需要滿足如下等式。

K + N + 1 <= 2^N

數(shù)據(jù)位有 K 位驯鳖,校驗(yàn)位有 N 位

在有 7 位數(shù)據(jù)位闲询,也就是 K=7 的情況下,N 的最小值就是 4浅辙。4 位校驗(yàn)位扭弧,其實(shí)最多可以支持到 11 位數(shù)據(jù)位。如下记舆,數(shù)據(jù)位數(shù)和校驗(yàn)位數(shù)的對照表

image

海明碼糾錯原理

海明碼的編碼方式:一個(gè)4-3 海明碼(也就是 4 位數(shù)據(jù)位鸽捻,3 位校驗(yàn)位)。我們把 4 位數(shù)據(jù)位泽腮,分別記作 d1御蒲、d2、d3诊赊、d4厚满。我們把 3 位校驗(yàn)位,分別記作 p1碧磅、p2碘箍、p3。

從 4 位的數(shù)據(jù)位里面续崖,我們拿走 1 位敲街,然后計(jì)算出一個(gè)對應(yīng)的校驗(yàn)位。這個(gè)校驗(yàn)位的計(jì)算用之前講過的奇偶校驗(yàn)就可以了严望。比如多艇,我們用 d1、d2像吻、d4 來計(jì)算出一個(gè)校驗(yàn)位 p1峻黍;用 d1、d3拨匆、d4 計(jì)算出一個(gè)校驗(yàn)位 p2姆涩;用 d2、d3惭每、d4 計(jì)算出一個(gè)校驗(yàn)位 p3骨饿。就像下面這個(gè)對應(yīng)的表格一樣:

image

此時(shí)亏栈,如果 d1 這一位的數(shù)據(jù)出錯了,我們會發(fā)現(xiàn)宏赘,p1 和 p2 和校驗(yàn)的計(jì)算結(jié)果都不一樣绒北。d2 出錯了,p1 和 p3 的校驗(yàn)的計(jì)算結(jié)果不一樣察署;d3 出錯了闷游,則p2 和 p3;如果 d4 出錯了贴汪,則是 p1脐往、p2、p3 都不一樣扳埂。你會發(fā)現(xiàn)业簿,當(dāng)數(shù)據(jù)碼出錯的時(shí)候,至少會有 2 位校驗(yàn)碼的計(jì)算是不一致的聂喇。

倒過來辖源,如果是 p1 的校驗(yàn)碼出錯了,則只有 p1 的校驗(yàn)結(jié)果出錯希太。p2 和 p3 的出錯的結(jié)果也是一樣的,只有一個(gè)校驗(yàn)碼的計(jì)算是不一致的酝蜒。

所以校驗(yàn)碼不一致誊辉,一共有 2^3-1=7 種情況,正好對應(yīng)了 7 個(gè)不同的位數(shù)的錯誤亡脑。

image

海明碼的生成規(guī)則

首先堕澄,先確定編碼后,要傳輸?shù)臄?shù)據(jù)是多少位霉咨。比如說蛙紫,我們這里的 7-4 海明碼,就是一共 11 位途戒。

然后坑傅,我們給這 11 位數(shù)據(jù)從左到右進(jìn)行編號,并且也把它們的二進(jìn)制表示寫出來喷斋。

接著唁毒,先把這 11 個(gè)數(shù)據(jù)中的==二進(jìn)制的整數(shù)次冪==找出來。在這個(gè) 7-4 海明碼里面星爪,就是 1浆西、2、4顽腾、8近零。這些數(shù),就是我們的校驗(yàn)碼位,我們把他們記錄做 p1~p4久信。如果從二進(jìn)制的角度看窖杀,它們是這 11 個(gè)數(shù)當(dāng)中,唯四的入篮,在 4 個(gè)比特里面只有一個(gè)比特是 1 的數(shù)值陈瘦。

那么剩下的 7 個(gè)數(shù),就是我們 d1-d7 的數(shù)據(jù)碼位了潮售。

然后痊项,對于校驗(yàn)碼位,還是用奇偶校驗(yàn)碼酥诽。但是每一個(gè)校驗(yàn)碼位鞍泉,不是用所有的 7 位數(shù)據(jù)來計(jì)算校驗(yàn)碼。而是 p1 用 3肮帐、5咖驮、7、9训枢、11 來計(jì)算托修。也就是,這些數(shù)在二進(jìn)制表示下恒界,從右往左數(shù)的第一位比特是 1 的情況下睦刃,用 p1 作為校驗(yàn)碼。

剩下的 p2十酣,我們用 3涩拙、6、10耸采、11 來計(jì)算校驗(yàn)碼兴泥,也就是在二進(jìn)制表示下,從右往左數(shù)的第二位比特是 1 的情況下虾宇,用 p2搓彻。那么,p3 自然是從右往左數(shù)文留,第三位比特是 1 的情況下的數(shù)字校驗(yàn)碼好唯。而 p4 則是第四位比特是 1 的情況下的校驗(yàn)碼。如圖:

image

此時(shí)可以看到燥翅,任何一個(gè)數(shù)據(jù)碼出錯了骑篙,就至少會有對應(yīng)的兩個(gè)或者三個(gè)校驗(yàn)碼對不上,這樣就能反過來找到是哪一個(gè)數(shù)據(jù)碼出錯了森书。如果校驗(yàn)碼出錯了靶端,那么只有校驗(yàn)碼這一位對不上谎势,我們就知道是這個(gè)校驗(yàn)碼出錯了。

海明距離

換一個(gè)角度來理解海明碼的作用對于兩個(gè)二進(jìn)制表示的數(shù)據(jù)杨名,他們之間有差異的位數(shù)脏榆,我們稱之為海明距離。比如 1001 和 0001 的海明距離是 1台谍,因?yàn)樗麄冎挥凶钭髠?cè)的第一位是不同的须喂。而 1001 和 0000 的海明距離是 2,因?yàn)樗麄冏钭髠?cè)和最右側(cè)有兩位是不同的趁蕊。

image

所以坞生,所謂的進(jìn)行一位糾錯,也就是所有和我們要傳輸?shù)臄?shù)據(jù)的海明距離為 1 的數(shù)掷伙,都能被糾正回來是己。

而任何兩個(gè)實(shí)際我們想要傳輸?shù)臄?shù)據(jù),海明距離都至少要是 3任柜。

為什么不能是 2 呢卒废?因?yàn)槿绻?2 的話,那么就會有一個(gè)出錯的數(shù)宙地,到兩個(gè)正確的數(shù)據(jù)的海明距離都是 1摔认。當(dāng)我們看到這個(gè)出錯的數(shù)的時(shí)候,我們就不知道究竟應(yīng)該糾正到那一個(gè)數(shù)了宅粥。

image

根據(jù)偏向來糾正該數(shù)的原本值级野。

總結(jié)

海明碼看起來簡單但直到今天的 ECC 內(nèi)存里面,我們還在使用這個(gè)技術(shù)方案粹胯。而海明也因?yàn)楹C鞔a獲得了圖靈獎。

通過在數(shù)據(jù)中添加多個(gè)冗余的校驗(yàn)碼位辰企,海明碼不僅能夠檢測到數(shù)據(jù)中的錯誤风纠,還能夠在只有單個(gè)位的數(shù)據(jù)出錯的時(shí)候,把錯誤的一位糾正過來牢贸。在理解和計(jì)算海明碼的過程中竹观,有一個(gè)很重要的點(diǎn),就是不僅原來的數(shù)據(jù)位可能出錯潜索。我們新添加的校驗(yàn)位臭增,一樣可能會出現(xiàn)單比特翻轉(zhuǎn)的錯誤。這也是為什么竹习,7 位數(shù)據(jù)位用 3 位校驗(yàn)碼位是不夠的誊抛,而需要 4 位校驗(yàn)碼位。

實(shí)際的海明碼編碼的過程也并不復(fù)雜整陌,我們通過用不同過的校驗(yàn)位拗窃,去匹配多個(gè)不同的數(shù)據(jù)組瞎领,確保任何一個(gè)數(shù)據(jù)位出錯,都會產(chǎn)生一個(gè)多個(gè)校驗(yàn)碼位出錯的唯一組合随夸。這樣九默,在出錯的時(shí)候,我們就可以反過來找到出錯的數(shù)據(jù)位宾毒,并糾正過來驼修。當(dāng)只有一個(gè)校驗(yàn)碼位出錯的時(shí)候,我們就知道實(shí)際出錯的是校驗(yàn)碼位了诈铛。

分布式計(jì)算

如果我們只有一臺計(jì)算機(jī)在數(shù)據(jù)中心乙各,我們會遇到三個(gè)核心問題:

  1. 垂直擴(kuò)展和水平擴(kuò)展的選擇問題
  2. 如何保持高可用性
  3. 一致性問題

水平擴(kuò)展

如果你利用云廠商提供的服務(wù)器搭建了一個(gè)自己的網(wǎng)站,對用戶提供服務(wù)癌瘾。機(jī)器配置1 個(gè) CPU 核心觅丰、3.75G 內(nèi)存以及一塊 10G 的 SSD 系統(tǒng)盤。這樣一臺服務(wù)器每個(gè)月的價(jià)格差不多是 28 美元妨退。

但后來用戶訪問量上來了妇萄,服務(wù)器性能有點(diǎn)兒不夠了,你需要升級服務(wù)器咬荷,但此時(shí)面臨兩個(gè)選擇:

  1. 第一個(gè)選擇是升級現(xiàn)在這臺服務(wù)器的硬件冠句,變成 2 個(gè) CPU 核心、7.5G 內(nèi)存幸乒。這樣的選擇我們稱之為垂直擴(kuò)展懦底。
  2. 第二個(gè)選擇則是我們再租用一臺和之前一樣的服務(wù)器。于是罕扎,我們有了 2 臺 1 個(gè) CPU 核心聚唐、3.75G 內(nèi)存的服務(wù)器。這樣的選擇我們稱之為水平擴(kuò)展腔召。

在這個(gè)階段杆查,這兩個(gè)選擇,從成本上看起來沒有什么差異臀蛛。2 核心亲桦、7.5G 內(nèi)存的服務(wù)器,成本是 56.61 美元浊仆,而 2 臺 1 核心客峭、3.75G 內(nèi)存的服務(wù)器價(jià)格,成本是 57 美元抡柿,這之間的價(jià)格差異不到 1%舔琅。不過,垂直擴(kuò)展和水平擴(kuò)展看似是兩個(gè)不同的選擇沙绝,但是隨著流量不斷增長搏明。到最后鼠锈,只會變成一個(gè)選擇。那就是既會垂直擴(kuò)展星著,又會水平擴(kuò)展购笆,并且==最終依靠水平擴(kuò)展==,來支撐 Google虚循、Facebook同欠、阿里、騰訊這樣體量的互聯(lián)網(wǎng)服務(wù)横缔。

垂直擴(kuò)展背后的邏輯和優(yōu)勢都很簡單铺遂。一般來說,垂直擴(kuò)展通常不需要我們?nèi)ジ脑斐绦蚓ジ眨簿褪钦f襟锐,我們沒有研發(fā)成本。但最終還是會使用水平擴(kuò)展的原因其實(shí)很簡單膛锭,因?yàn)槲覀儧]有辦法不停地去做垂直擴(kuò)展粮坞。我們在 Google Cloud 上現(xiàn)在能夠買到的性能最好的服務(wù)器,是 96 個(gè) CPU 核心初狰、1.4TB 的內(nèi)存莫杈。如果我們的訪問量逐漸增大,一臺 96 核心的服務(wù)器也支撐不了了奢入,那么我們就沒有辦法再去做垂直擴(kuò)展了筝闹。這個(gè)時(shí)候,我們就不得不采用水平擴(kuò)展的方案了腥光。

然而关顷,一旦開始采用水平擴(kuò)展,我們就會面臨在軟件層面改造的問題了武福。也就是我們需要開始進(jìn)行分布式計(jì)算了解寝。我們需要引入負(fù)載均衡 這樣的組件,來進(jìn)行流量分配艘儒。我們需要拆分應(yīng)用服務(wù)器和數(shù)據(jù)庫服務(wù)器,來進(jìn)行垂直功能的切分夫偶。我們也需要不同的應(yīng)用之間通過消息隊(duì)列界睁,來進(jìn)行異步任務(wù)的執(zhí)行。

image

所有這些軟件層面的改造兵拢,其實(shí)都是在做分布式計(jì)算的一個(gè)核心工作翻斟,就是通過消息傳遞而不是共享內(nèi)存的方式,讓多臺不同的計(jì)算機(jī)協(xié)作起來共同完成任務(wù)说铃。而因?yàn)槲覀冏罱K必然要進(jìn)行水平擴(kuò)展访惜,我們需要在系統(tǒng)設(shè)計(jì)的早期就基于消息傳遞而非共享內(nèi)存來設(shè)計(jì)系統(tǒng)嘹履。即使這些消息只是在同一臺服務(wù)器上進(jìn)行傳遞。(因?yàn)橹蟊厝粫淮怪辈鸱珠_)

高可用性 - 單點(diǎn)故障

水平擴(kuò)展的好處:

  1. 可以“強(qiáng)迫”從開發(fā)的角度债热,盡早地讓系統(tǒng)能夠支持水平擴(kuò)展砾嫉,避免在真的流量快速增長的時(shí)候,垂直擴(kuò)展的解決方案跟不上趟窒篱。
  2. 系統(tǒng)的可用性的保證焕刮。

上面的 1 核變 2 核的垂直擴(kuò)展的方式,擴(kuò)展完之后墙杯,我們還是只有 1 臺服務(wù)器配并。如果這臺服務(wù)器出現(xiàn)了一點(diǎn)硬件故障,比如高镐,CPU 壞了溉旋,那我們的整個(gè)系統(tǒng)就壞了,就不可用了嫉髓。如果采用了水平擴(kuò)展观腊,即便有一臺服務(wù)器的 CPU 壞了,我們還有另外一臺服務(wù)器仍然能夠提供服務(wù)岩喷。負(fù)載均衡能夠通過健康檢測發(fā)現(xiàn)壞掉的服務(wù)器沒有響應(yīng)了恕沫,就可以自動把所有的流量切換到第 2 臺服務(wù)器上,這個(gè)操作就叫作故障轉(zhuǎn)移纱意。我們的系統(tǒng)仍然是可用的婶溯。

系統(tǒng)的可用性 指的就是,我們的系統(tǒng)可以正常服務(wù)的時(shí)間占比偷霉。無論是因?yàn)檐浻布收掀€是需要對系統(tǒng)進(jìn)行停機(jī)升級,都會讓我們損失系統(tǒng)的可用性类少⌒鹕恚可用性通常是用一個(gè)百分比的數(shù)字來表示,比如 99.99%硫狞。我們說信轿,系統(tǒng)每個(gè)月的可用性要保障在 99.99%,也就是意味著一個(gè)月里残吩,你的服務(wù)宕機(jī)的時(shí)間不能超過 4.32 分鐘报亩。

有些系統(tǒng)可用性的損失婴噩,是在我們計(jì)劃內(nèi)的。比如上面說的停機(jī)升級,這個(gè)就是所謂的計(jì)劃內(nèi)停機(jī)時(shí)間苗分。有些系統(tǒng)可用性的損失兼丰,是在我們計(jì)劃外的,比如一臺服務(wù)器的硬盤忽然壞了,這個(gè)就是所謂的計(jì)劃外停機(jī)時(shí)間漏益。

我們的系統(tǒng)是一定不可能做到 100% 可用的,特別是計(jì)劃外的停機(jī)時(shí)間深胳。從簡單的硬件損壞绰疤,到機(jī)房停電、光纜被挖斷稠屠,乃至于各種自然災(zāi)害峦睡,比如地震、洪水权埠、海嘯榨了,都有可能使得我們的系統(tǒng)不可用。作為一個(gè)工程師和架構(gòu)師攘蔽,我們要做的就是盡可能低成本地提高系統(tǒng)的可用性龙屉。

現(xiàn)在的服務(wù)器的可用性都已經(jīng)很不錯了,通常都能保障 99.99% 的可用性了满俗。如果我們有一個(gè)小小的三臺服務(wù)器組成的小系統(tǒng)转捕,一臺部署了 Nginx 來作為負(fù)載均衡和反向代理,一臺跑了 PHP-FPM 作為 Web 應(yīng)用服務(wù)器唆垃,一臺用來作為 MySQL 數(shù)據(jù)庫服務(wù)器五芝。每臺服務(wù)器的可用性都是 99.99%。那么我們整個(gè)系統(tǒng)的可用性就是99.99% × 99.99% × 99.99% = 99.97%辕万。在這個(gè)系統(tǒng)當(dāng)中枢步,這個(gè)數(shù)字看起來似乎沒有那么大區(qū)別。不過反過來看渐尿,我們是從損失了 0.01% 的可用性醉途,變成了損失 0.03% 的可用性,不可用的時(shí)間變成了原來的 3 倍砖茸。如果我們有 1000 臺服務(wù)器隘擎,那么整個(gè)的可用性,就會變成 99.99% ^ 1000 = 90.5%凉夯。也就是說货葬,我們的服務(wù)一年里有超過一個(gè)月是不可用的

image

在這個(gè)場景下劲够,任何一臺服務(wù)器出錯了宝惰,整個(gè)系統(tǒng)就沒法用了。這個(gè)問題就叫作單點(diǎn)故障問題再沧。

要解決單點(diǎn)故障問題,第一點(diǎn)就是要移除單點(diǎn)尊残。即水平擴(kuò)展炒瘸,就是讓兩臺服務(wù)器提供相同的功能,然后通過負(fù)載均衡把流量分發(fā)到兩臺不同的服務(wù)器去顷扩。即使一臺服務(wù)器掛了扎阶,還有一臺服務(wù)器可以正常提供服務(wù)。不過光用兩臺服務(wù)器是不夠的拒炎,單點(diǎn)故障其實(shí)在數(shù)據(jù)中心里面無處不在击你。我們現(xiàn)在用的是云上的兩臺虛擬機(jī)绒障。如果這兩臺虛擬機(jī)是托管在同一臺物理機(jī)上的必逆,那這臺物理機(jī)本身又成為了一個(gè)單點(diǎn)。那我們就需要把這兩臺虛擬機(jī)分到兩臺不同的物理機(jī)上。不過這個(gè)還是不夠名眉。如果這兩臺物理機(jī)在同一個(gè)機(jī)架上粟矿,那機(jī)架上的交換機(jī)就成了一個(gè)單點(diǎn)。即使放到不同的機(jī)架上损拢,還是有可能出現(xiàn)整個(gè)數(shù)據(jù)中心遭遇意外故障的情況陌粹。

image

除了上述問題,部署在云上的服務(wù)所在的數(shù)據(jù)中心福压,還有可能因?yàn)樯釂栴}觸發(fā)了整個(gè)數(shù)據(jù)中心所有服務(wù)器被關(guān)閉的問題掏秩。面對這種情況,就需要設(shè)計(jì)進(jìn)行異地多活 的系統(tǒng)設(shè)計(jì)和部署荆姆。所以蒙幻,在現(xiàn)代的云服務(wù),你在買服務(wù)器的時(shí)候可以選擇服務(wù)器的 area(地區(qū))和 zone(區(qū)域)胞枕,而要不要把服務(wù)器放在不同的地區(qū)或者區(qū)域里杆煞,也是避免單點(diǎn)故障的一個(gè)重要因素

只是能夠去除單點(diǎn)腐泻,可用性問題還沒有解決决乎。比如,上面我們用負(fù)載均衡把流量均勻地分發(fā)到 2 臺服務(wù)器上派桩,當(dāng)一臺應(yīng)用服務(wù)器掛掉的時(shí)候构诚,我們的確還有一臺服務(wù)器在提供服務(wù)。但是負(fù)載均衡會把一半的流量發(fā)到已經(jīng)掛掉的服務(wù)器上铆惑,所以這個(gè)時(shí)候只能算作一半可用范嘱。想要讓整個(gè)服務(wù)完全可用,我們就需要有一套故障轉(zhuǎn)移機(jī)制员魏。想要進(jìn)行故障轉(zhuǎn)移丑蛤,就首先要能發(fā)現(xiàn)故障。

以我們這里的 PHP-FPM 的 Web 應(yīng)用為例撕阎,負(fù)載均衡通常會定時(shí)去請求一個(gè) Web 應(yīng)用提供的健康檢測的地址受裹。這個(gè)時(shí)間間隔可能是 5 秒鐘,如果連續(xù) 2~3 次發(fā)現(xiàn)健康檢測失敗虏束,負(fù)載均衡就會自動將這臺服務(wù)器的流量切換到其他服務(wù)器上棉饶。于是,我們就自動地產(chǎn)生了一次故障轉(zhuǎn)移镇匀。故障轉(zhuǎn)移的自動化在大型系統(tǒng)里是很重要的照藻,因?yàn)榉?wù)器越多,出現(xiàn)故障基本就是個(gè)必然發(fā)生的事情汗侵。而自動化的故障轉(zhuǎn)移既能夠減少運(yùn)維的人手需求幸缕,也能夠縮短從故障發(fā)現(xiàn)到問題解決的時(shí)間周期群发,提高可用性。

image

可以在 Web 應(yīng)用上設(shè)置了一個(gè) Heartbeat 接口发乔,每 20 秒檢查一次也物,出現(xiàn)問題的時(shí)候可以進(jìn)行故障轉(zhuǎn)移切換。

這樣列疗,通過水平擴(kuò)展相同功能的服務(wù)器來去掉單點(diǎn)故障,并且通過健康檢查機(jī)制來觸發(fā)自動的故障轉(zhuǎn)移的系統(tǒng)的可用性就是100% - (100% - 99.99%) × (100% - 99.99%) = 99.999999%浪蹂。這樣不能提供服務(wù)的時(shí)間就減少到了原來的萬分之一抵栈。在實(shí)際情況中,可用性沒法做到那么理想的地步坤次。光從硬件的角度古劲,從服務(wù)器到交換機(jī),從網(wǎng)線連接到機(jī)房電力缰猴,從機(jī)房的整體散熱到外部的光纖線路等等产艾,可能出現(xiàn)問題的地方太多了。這也是為什么滑绒,我們需要從整個(gè)系統(tǒng)層面闷堡,去設(shè)計(jì)系統(tǒng)的高可用性,因?yàn)樵跈C(jī)器數(shù)量上去之后疑故,每個(gè)環(huán)節(jié)都能產(chǎn)生很大的影響杠览。

總結(jié)

水平擴(kuò)展和可用性,光有這兩點(diǎn)還是不夠的纵势。一旦系統(tǒng)里面有了很多臺服務(wù)器踱阿。特別是,為了保障可用性钦铁,對于同樣功能的软舌、有狀態(tài)的數(shù)據(jù)庫進(jìn)行了水平的擴(kuò)展,我們就會面臨一個(gè)新的挑戰(zhàn)牛曹,那就是分區(qū)一致性問題佛点。不過,這個(gè)問題更多的是一個(gè)軟件設(shè)計(jì)問題躏仇。

通過升級硬件規(guī)格來提升服務(wù)能力的垂直擴(kuò)展恋脚。除此之外,也可以通過增加服務(wù)器數(shù)量來提升服務(wù)能力焰手。不過歸根到底糟描,我們一定要走上水平擴(kuò)展的路徑。

一方面是因?yàn)榇怪睌U(kuò)展不可持續(xù)书妻;另一方面船响,則是只有水平擴(kuò)展才能保障高可用性躬拢。而通過水平擴(kuò)展保障高可用性,則需要我們做三件事情见间。第一個(gè)是理解可用性是怎么計(jì)算的聊闯。服務(wù)器硬件的損壞只是可能導(dǎo)致可用性損失的因素之一,機(jī)房內(nèi)的電力米诉、散熱菱蔬、交換機(jī)、網(wǎng)絡(luò)線路史侣,都有可能導(dǎo)致可用性損失拴泌。而外部的光纜、自然災(zāi)害惊橱,也都有可能造成我們整個(gè)系統(tǒng)的不可用蚪腐。所以,在分析設(shè)計(jì)系統(tǒng)的時(shí)候税朴,我們需要盡可能地排除單點(diǎn)故障回季。進(jìn)一步地,對于硬件的故障正林,我們還要有自動化的故障轉(zhuǎn)移策略泡一。在這些策略都齊全之后,我們才能真的長舒一口氣卓囚,在海量的負(fù)載和流量下安心睡個(gè)好覺瘾杭。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市哪亿,隨后出現(xiàn)的幾起案子粥烁,更是在濱河造成了極大的恐慌,老刑警劉巖蝇棉,帶你破解...
    沈念sama閱讀 206,126評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件讨阻,死亡現(xiàn)場離奇詭異,居然都是意外死亡篡殷,警方通過查閱死者的電腦和手機(jī)钝吮,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,254評論 2 382
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來板辽,“玉大人奇瘦,你說我怎么就攤上這事【⑾遥” “怎么了耳标?”我有些...
    開封第一講書人閱讀 152,445評論 0 341
  • 文/不壞的土叔 我叫張陵,是天一觀的道長邑跪。 經(jīng)常有香客問我次坡,道長呼猪,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,185評論 1 278
  • 正文 為了忘掉前任砸琅,我火速辦了婚禮宋距,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘症脂。我一直安慰自己谚赎,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,178評論 5 371
  • 文/花漫 我一把揭開白布诱篷。 她就那樣靜靜地躺著沸版,像睡著了一般。 火紅的嫁衣襯著肌膚如雪兴蒸。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 48,970評論 1 284
  • 那天细办,我揣著相機(jī)與錄音橙凳,去河邊找鬼。 笑死笑撞,一個(gè)胖子當(dāng)著我的面吹牛岛啸,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播茴肥,決...
    沈念sama閱讀 38,276評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼坚踩,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了瓤狐?” 一聲冷哼從身側(cè)響起瞬铸,我...
    開封第一講書人閱讀 36,927評論 0 259
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎础锐,沒想到半個(gè)月后嗓节,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,400評論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡皆警,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,883評論 2 323
  • 正文 我和宋清朗相戀三年拦宣,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片信姓。...
    茶點(diǎn)故事閱讀 37,997評論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡鸵隧,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出意推,到底是詐尸還是另有隱情豆瘫,我是刑警寧澤,帶...
    沈念sama閱讀 33,646評論 4 322
  • 正文 年R本政府宣布左痢,位于F島的核電站靡羡,受9級特大地震影響系洛,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜略步,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,213評論 3 307
  • 文/蒙蒙 一描扯、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧趟薄,春花似錦绽诚、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,204評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至羡铲,卻和暖如春蜂桶,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背也切。 一陣腳步聲響...
    開封第一講書人閱讀 31,423評論 1 260
  • 我被黑心中介騙來泰國打工扑媚, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人雷恃。 一個(gè)月前我還...
    沈念sama閱讀 45,423評論 2 352
  • 正文 我出身青樓疆股,卻偏偏與公主長得像,于是被迫代替她去往敵國和親倒槐。 傳聞我的和親對象是個(gè)殘疾皇子旬痹,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,722評論 2 345