深入理解計(jì)算機(jī)組成原理-存儲(chǔ)于I/O
在 SRAM 里面,一個(gè)比特的數(shù)據(jù),需要 6~8 個(gè)晶體管。所以 SRAM 的存儲(chǔ)密度不高。同樣的物理空間下,能夠存儲(chǔ)的數(shù)據(jù)有限。不過(guò),因?yàn)?SRAM 的電路簡(jiǎn)單,所以訪問(wèn)速度非??臁?/p>
在 CPU 里,通常會(huì)有 L1、L2、L3 這樣三層高速緩存,每個(gè) CPU 核心都有一塊屬于自己的 L1 高速緩存,通常分成指令緩存和數(shù)據(jù)緩存,分開(kāi)存放 CPU 使用的指令和數(shù)據(jù)
L1 的 Cache 往往就嵌在 CPU 核心的內(nèi)部;
L2 的 Cache 同樣是每個(gè) CPU 核心都有的,不過(guò)它往往不在 CPU 核心的內(nèi)部。所以,L2 Cache 的訪問(wèn)速度會(huì)比 L1 稍微慢一些。而 L3 Cache,則通常是多個(gè) CPU 核心共用的,尺寸會(huì)更大一些,訪問(wèn)速度自然也就更慢一些。
DRAM 被稱為“動(dòng)態(tài)”存儲(chǔ)器,是因?yàn)?DRAM 需要靠不斷地“刷新”,才能保持?jǐn)?shù)據(jù)被存儲(chǔ)起來(lái)。DRAM 的一個(gè)比特,只需要一個(gè)晶體管和一個(gè)電容就能存儲(chǔ)。所以,DRAM 在同樣的物理空間下,能夠存儲(chǔ)的數(shù)據(jù)也就更多,也就是存儲(chǔ)的“密度”更大。但是,因?yàn)閿?shù)據(jù)是存儲(chǔ)在電容里的,電容會(huì)不斷漏電,所以需要定時(shí)刷新充電,才能保持?jǐn)?shù)據(jù)不丟失。DRAM 的數(shù)據(jù)訪問(wèn)電路和刷新電路都比 SRAM 更復(fù)雜,所以訪問(wèn)延時(shí)也就更長(zhǎng)。


直接映射 Cache(Direct Mapped Cache直接映射
現(xiàn)代 CPU 已經(jīng)很少使用直接映射 Cache 了,通常用的是組相連 Cache(set associative cache)
一個(gè)內(nèi)存的訪問(wèn)地址,最終包括高位代表的組標(biāo)記、低位代表的索引,以及在對(duì)應(yīng)的 Data Block 中定位對(duì)應(yīng)字的位置偏移量

volatile 關(guān)鍵字究竟代表它會(huì)確保我們對(duì)于這個(gè)變量的讀取和寫入,都一定會(huì)同步到主內(nèi)存里,而不是從 Cache 里面讀取

緩存同步 L1 L2 多核
總線嗅探機(jī)制和 MESI 協(xié)議
M:代表已修改(Modified)E:代表獨(dú)占(Exclusive)S:代表共享(Shared)I:代表已失效(Invalidated)
這個(gè)策略,本質(zhì)上就是把所有的讀寫請(qǐng)求都通過(guò)總線(Bus)廣播給所有的 CPU 核心
總線本身就是一個(gè)特別適合廣播進(jìn)行數(shù)據(jù)傳輸?shù)臋C(jī)制,所以總線嗅探這個(gè)辦法也是我們?nèi)粘J褂玫?Intel CPU 進(jìn)行緩存一致性處理的解決方案
映射表。
我們的內(nèi)存需要被分成固定大小的頁(yè)(Page),然后再通過(guò)虛擬內(nèi)存地址(Virtual Address)到物理內(nèi)存地址(Physical Address)的地址轉(zhuǎn)換(Address Translation),才能到達(dá)實(shí)際存放數(shù)據(jù)的物理內(nèi)存位置。而我們的程序看到的內(nèi)存地址,都是虛擬內(nèi)存地址。
TLB,全稱是地址變換高速緩沖
我們通過(guò)頁(yè)表這個(gè)數(shù)據(jù)結(jié)構(gòu)來(lái)處理。為了節(jié)約頁(yè)表的內(nèi)存存儲(chǔ)空間,我們會(huì)使用多級(jí)頁(yè)表數(shù)據(jù)結(jié)構(gòu)
不過(guò),多級(jí)頁(yè)表雖然節(jié)約了我們的存儲(chǔ)空間,但是卻帶來(lái)了時(shí)間上的開(kāi)銷,變成了一個(gè)“以時(shí)間換空間”的策略。原本我們進(jìn)行一次地址轉(zhuǎn)換,只需要訪問(wèn)一次內(nèi)存就能找到物理頁(yè)號(hào),算出物理內(nèi)存地址。但是用了 4 級(jí)頁(yè)表,我們就需要訪問(wèn) 4 次內(nèi)存,才能找到物理頁(yè)號(hào)。
為了節(jié)約頁(yè)表所需要的內(nèi)存空間,我們采用了多級(jí)頁(yè)表這樣一個(gè)數(shù)據(jù)結(jié)構(gòu)。但是,多級(jí)頁(yè)表雖然節(jié)省空間了,卻要花費(fèi)更多的時(shí)間去多次訪問(wèn)內(nèi)存。于是,我們?cè)趯?shí)際進(jìn)行地址轉(zhuǎn)換的 MMU 旁邊放上了 TLB 這個(gè)用于地址轉(zhuǎn)換的緩存。TLB 也像 CPU Cache 一樣,分成指令和數(shù)據(jù)部分,也可以進(jìn)行 L1、L2 這樣的分層。

我們現(xiàn)在常用的硬盤有兩種。一種是 HDD 硬盤,也就是我們常說(shuō)的機(jī)械硬盤。另一種是 SSD 硬盤,一般也被叫作固態(tài)硬盤?,F(xiàn)在的 HDD 硬盤,用的是 SATA 3.0 的接口。而 SSD 硬盤呢,通常會(huì)用兩種接口,一部分用的也是 SATA 3.0 的接口;另一部分呢,用的是 PCI Express 的接口?,F(xiàn)在我們常用的 SATA 3.0 的接口,帶寬是 6Gb/s。這里的“b”是比特。這個(gè)帶寬相當(dāng)于每秒可以傳輸 768MB 的數(shù)據(jù)。而我們?nèi)粘S玫?HDD 硬盤的數(shù)據(jù)傳輸率,差不多在 200MB/s 左右。
SSD 硬盤能夠更快,所以我們可以換用 PCI Express 的接口。我自己電腦的系統(tǒng)盤就是一塊使用了 PCI Express 的三星 SSD 硬盤。它的數(shù)據(jù)傳輸率,在讀取的時(shí)候就能做到 2GB/s 左右,差不多是 HDD 硬盤的 10 倍,而在寫入的時(shí)候也能有 1.2GB/s
順序讀寫和隨機(jī)讀寫
隨機(jī)讀寫的時(shí)候,數(shù)據(jù)傳輸率也只能到 40MB/s 左右,是順序讀寫情況下的幾十分之一。
在 top 命令的輸出結(jié)果里面,有一行是以 %CPU 開(kāi)頭的。這一行里,有一個(gè)叫作 wa 的指標(biāo),這個(gè)指標(biāo)就代表著 iowait,也就是 CPU 等待 IO 完成操作花費(fèi)的時(shí)間占 CPU 的百分比。下一次,當(dāng)你自己的服務(wù)器遇到性能瓶頸,load 很大的時(shí)候,你就可以通過(guò) top 看一看這個(gè)指標(biāo)。
我們看到,即使是用上了 PCI Express 接口的 SSD 硬盤,IOPS 也就是在 2 萬(wàn)左右。而我們的 CPU 的主頻通常在 2GHz 以上,也就是每秒可以做 20 億次操作。
這里的 tps 指標(biāo),其實(shí)就對(duì)應(yīng)著我們上面所說(shuō)的硬盤的 IOPS 性能。而 kB_read/s 和 kB_wrtn/s 指標(biāo),就對(duì)應(yīng)著我們的數(shù)據(jù)傳輸率的指標(biāo)。
通過(guò) iotop 這個(gè)命令,你可以看到具體是哪一個(gè)進(jìn)程實(shí)際占用了大量 I/O,那么你就可以有的放矢,去優(yōu)化對(duì)應(yīng)的程序了
每秒鐘能夠進(jìn)行輸入輸出的操作次數(shù),也就是 IOPS 這個(gè)核心性能指標(biāo)。
如果隨機(jī)在整個(gè)硬盤上找一個(gè)數(shù)據(jù),需要 8-14 ms。我們的硬盤是機(jī)械結(jié)構(gòu)的,只有一個(gè)電機(jī)轉(zhuǎn)軸,也只有一個(gè)懸臂,所以我們沒(méi)有辦法并行地去定位或者讀取數(shù)據(jù)。那一塊 7200 轉(zhuǎn)的硬盤,我們一秒鐘隨機(jī)的 IO 訪問(wèn)次數(shù),也就是1s / 8 ms = 125 IOPS 或者 1s / 14ms = 70 IOPS

SSD 硬盤,我們也可以先簡(jiǎn)單地認(rèn)為,它是由一個(gè)電容加上一個(gè)電壓計(jì)組合在一起,
記錄了一個(gè)或者多個(gè)比特。
我們上面說(shuō)的 SLC 的芯片,可以擦除的次數(shù)大概在 10 萬(wàn)次,MLC 就在 1 萬(wàn)次左右而 TLC 和 QLC 就只在幾千次了
為什么ssd斷電后不會(huì)丟數(shù)據(jù)
作者回復(fù): 現(xiàn)在大家用的SSD的存儲(chǔ)硬件都是NAND Flash。實(shí)現(xiàn)原理和通過(guò)改變電壓,讓電子進(jìn)入絕緣層的浮柵(Floating Gate)內(nèi)。斷電之后,電子仍然在FG里面。但是如果長(zhǎng)時(shí)間不通電,比如幾年,仍然可能會(huì)丟數(shù)據(jù)。所以換句話說(shuō),SSD的確也不適合作為冷數(shù)據(jù)備份。
https://www.bilibili.com/video/av61437877


當(dāng) SSD 硬盤的存儲(chǔ)空間被占用得越來(lái)越多,每一次寫入新數(shù)據(jù),我們都可能沒(méi)有足夠的空白。我們可能不得不去進(jìn)行垃圾回收,合并一些塊里面的頁(yè),然后再擦除掉一些頁(yè),才能勻出一些空間來(lái)。這個(gè)時(shí)候,從應(yīng)用層或者操作系統(tǒng)層面來(lái)看,我們可能只是寫入了一個(gè) 4KB 或者 4MB 的數(shù)據(jù)。但是,實(shí)際通過(guò) FTL 之后,我們可能要去搬運(yùn) 8MB、16MB 甚至更多的數(shù)據(jù)。我們通過(guò)“實(shí)際的閃存寫入的數(shù)據(jù)量 / 系統(tǒng)通過(guò) FTL 寫入的數(shù)據(jù)量 = 寫入放大”,可以得到,寫入放大的倍數(shù)越多,意味著實(shí)際的 SSD 性能也就越差,會(huì)遠(yuǎn)遠(yuǎn)比不上實(shí)際 SSD 硬盤標(biāo)稱的指標(biāo)。
嘗試不停地提升 I/O 設(shè)備的速度。把 HDD 硬盤換成 SSD 硬盤,我們?nèi)匀挥X(jué)得不夠快;用 PCI Express 接口的 SSD 硬盤替代 SATA 接口的 SSD 硬盤,我們還是覺(jué)得不夠快,所以,現(xiàn)在就有了傲騰(Optane)這樣的技術(shù)
但是,無(wú)論 I/O 速度如何提升,比起 CPU,總還是太慢。SSD 硬盤的 IOPS 可以到 2 萬(wàn)、4 萬(wàn),但是我們 CPU 的主頻有 2GHz 以上,也就意味著每秒會(huì)有 20 億次的操作。如果我們對(duì)于 I/O 的操作,都是由 CPU 發(fā)出對(duì)應(yīng)的指令,然后等待 I/O 設(shè)備完成操作之后返回,那 CPU 有大量的時(shí)間其實(shí)都是在等待 I/O 設(shè)備完成操作。但是,這個(gè) CPU 的等待,在很多時(shí)候,其實(shí)并沒(méi)有太多的實(shí)際意義。我們對(duì)于 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)過(guò) CPU,實(shí)在是有點(diǎn)兒太浪費(fèi)時(shí)間了。因此,計(jì)算機(jī)工程師們,就發(fā)明了 DMA 技術(shù),
也就是直接內(nèi)存訪問(wèn)(Direct Memory Access)技術(shù),來(lái)減少 CPU 等待的時(shí)間
Kafka 是一個(gè)用來(lái)處理實(shí)時(shí)數(shù)據(jù)的管道,我們常常用它來(lái)做一個(gè)消息隊(duì)列,或者用來(lái)收集和落地海量的日志。作為一個(gè)處理實(shí)時(shí)數(shù)據(jù)和日志的管道,瓶頸自然也在 I/O 層面。Kafka 里面會(huì)有兩種常見(jiàn)的海量數(shù)據(jù)傳輸?shù)那闆r。一種是從網(wǎng)絡(luò)中接收上游的數(shù)據(jù),然后需要落地到本地的磁盤上,確保數(shù)據(jù)不丟失。另一種情況呢,則是從本地磁盤上讀取出來(lái),通過(guò)網(wǎng)絡(luò)發(fā)送出去。

Kafka 做的事情就是,把這個(gè)數(shù)據(jù)搬運(yùn)的次數(shù),從上面的四次,變成了兩次,并且只有 DMA 來(lái)進(jìn)行數(shù)據(jù)搬運(yùn),而不需要 CPU
fileChannel.transferTo(position, count, socketChannel);
Kafka 的代碼調(diào)用了 Java NIO 庫(kù),具體是 FileChannel 里面的 transferTo 方法。我們的數(shù)據(jù)并沒(méi)有讀到中間的應(yīng)用內(nèi)存里面,而是直接通過(guò) Channel,寫入到對(duì)應(yīng)的網(wǎng)絡(luò)設(shè)備里。并且,對(duì)于 Socket 的操作,也不是寫入到 Socket 的 Buffer 里面,而是直接根據(jù)描述符(Descriptor)寫入到網(wǎng)卡的緩沖區(qū)里面。于是,在這個(gè)過(guò)程之中,我們只進(jìn)行了兩次數(shù)據(jù)傳輸。

單筆特翻轉(zhuǎn)發(fā)生的概率是0.01%,那么兩個(gè)比特都翻轉(zhuǎn)概率就是0.000001%。 要解決這個(gè)問(wèn)題成本會(huì)進(jìn)一步大幅度上升,就沒(méi)有必要在硬件層面這么干了。






