原文地址: juejin.im/post/5ed992…windows
不知道爲何最近喜歡上了看紙質書, 看的關於OS和一點關於硬件方面的東西, 這篇文章算是對最近看的這些作的一個記錄吧(這東西明明大學就已經教過了, 偷偷流下了不學無術的淚水
).這篇說實話也寫了好久, 主要仍是由於天天下班到家基本到了晚上11點(劃重點: 終於找到藉口了
).centos
在寫完這篇後, 原本打算取一個狂拽酷炫吊炸天的標題, 「磁盤I/O都不懂, 連CRUD你都不知道怎麼寫」
, 這樣的話點擊進來的人應該會多一點, 可是想了想, 如今 販賣焦慮的營銷號已經這麼多了, 不缺我一個
, 開發何須爲難開發(我本身), 最後決定仍是取個簡單點的標題, 說是 "那點事", 可能叫 "磁盤io中的一些事" 會更加恰當一點吧 (文中沒有對一些東西進行太多的延伸, 後面可能會加上)
, 可是取標題也不能太虛, 就它了.緩存
磁盤是用於保存大量數據的存儲設備, 雖然存儲的數據量是 RAM 的不少倍, 可是從磁盤讀取信息的延遲爲毫秒級, 比DRAM(通常是主存) 的讀慢了十萬倍, 比從 SRAM(CPU高速緩存存儲器) 讀慢了100萬倍
.異步
下面是機械硬盤的一個大體的結構圖(圖畫的比較emmm....各位看官將就着看)
post
主軸、盤片、傳動臂、讀寫頭、磁道/扇區性能
每一個磁盤中間都有一根 以固定速率旋轉的主軸
, 盤片跟隨着主軸旋轉
, 一般是 5400 ~ 15000RPM(個人希捷1T就是7200RPM), 一個磁盤一般包含不少個這樣的盤片.測試
每一個盤片中都由一組稱爲磁道的同心圓組成, 每一個磁道被劃分爲一組扇區. 每一個扇區包含相同的數據位(一般是512字節,下面是一些輸出信息)優化
// 下面是在某臺 centos7.2上面的數據
[root@xxxxx ~]# fdisk -l
磁盤 /dev/vda:42.9 GB, 42949672960 字節,83886080 個扇區
Units = 扇區 of 1 * 512 = 512 bytes
扇區大小(邏輯/物理):512 字節 / 512 字節
I/O 大小(最小/最佳):512 字節 / 512 字節
磁盤標籤類型:dos
磁盤標識符:0x000d2717
複製代碼
磁盤操做的時候, 須要幾個部分的東西, 傳動臂、讀寫頭、磁道/扇區.google
傳動臂: 傳動臂的做用是將讀寫頭定位到盤片上的磁道上, 這個叫 尋道
, 後面還會出現.centos7
讀寫頭: 位於傳動臂末端, 用於讀寫數據, 磁盤上的 每一個盤片都有一個獨立的讀寫頭
.
磁道/扇區:
扇區: 扇區是一條磁道上的一塊, 磁盤以扇區爲最小單位讀寫數據(扇區是最小的物理存儲單元).
磁道: 盤片上的每一個同心圓都是一條磁道, 一條磁道由多個扇區和扇區之間的間隙組成(最外圈的磁道長度最長, 扇區最多, 所存儲的數據也是最多的).
題外話: 之前google在最開始作優化搜索的一個步驟是將熱點數據放在磁盤外圈, 由於盤片是一個同心圓, 角速度是同樣的, 相同時間內, 最外圈的磁道走過的路徑是最長的, 也就是相同時間, 最外圈讀取到的數據是最多的.
(尋道時間) 首先接收到要讀取的數據的物理位置, 傳動臂將讀寫頭定位到數據對應的磁道, 通常這個尋道的時間單位爲 ms.
(旋轉時間) 定位到對應磁道上後, 接着等待讀寫頭定位到該磁道上數據對應的扇區的第一個字節.
(傳輸時間) 從磁盤讀出或者寫入數據, 一般這個時間是最低的.
因此訪問磁盤的總時間是: 尋道時間 + 旋轉時間 + 傳輸時間
.
在機械硬盤的磁盤I/O中, 一般磁盤I/O的時間影響: 尋道 > 旋轉 >> 傳輸.
目前通常的硬盤的尋道時間爲 7.5~14ms.
平均尋道時間爲 Tavg seek = 9ms;
盤片旋轉一圈所需時間的一半 , 拿個人 7200RPM 機械硬盤
舉例, 一分鐘旋轉 7200 圈, 7200/60 = 120 圈/秒, 一圈所需的時間就是 1/120(s)
, 因此最後得出的 平均旋轉時間就是 1/120/2 = 1/240(s), 大概 4ms 出一點
.
數據傳輸時間相對於前兩個時間來講, 一般能夠忽略不計, 但仍是有的(偷懶).
隨機I/O的流程就是像上面說的磁盤操做流程那樣, 每次讀寫數據都須要通過完整的步驟: 尋道、旋轉、傳輸
, 也就是每一次讀寫數據都須要將讀寫頭定位到對應的磁道上, 讀寫比較頻繁的時候, 就會出現一會定位內圈, 一會定位外圈, 簡稱反覆橫跳(好像哪裏聽過的樣子)
, 磁盤的操做時間大部分都花在了第一步的尋道還有第二部的旋轉上(由於只有尋道+旋轉才能肯定最終數據的位置).
既然是分開了, 那順序I/O天然就是不須要或者說幾乎沒有尋道時間, 由於順序I/O的話, 基本上是在同一個磁道進行操做或者鄰近的磁道進行操做, 這時候的尋道次數很是少, 相比隨機I/O, 效率天然是更高的.
至於後面的傳輸時間, 兩種類型的I/O都是同樣的.
Q: 如何將隨機I/O轉化成順序I/O?
A: 要知道怎麼實現, 先要知道他們的區別: 隨機I/O和順序I/O的區別就是尋道和旋轉的次數, N次隨機I/O須要尋道N次、旋轉N次, 而順序 I/O 最好的狀況是尋道1次、旋轉1次, 即便不是1次, 讀寫數據也是在第一次尋道後的鄰近的磁道讀寫, 因此尋道時間和旋轉時間確定是 遠遠低於N次隨機I/O的時間
. 因此若是是要變成順序I/O的話, 數據在空間上必須是連續的, 才能實現順序讀寫.
so 數據最好是做爲一個總體, 統一讀取/寫入, 整塊的數據就是連續的, 就能夠實現順序I/O.
衡量磁盤的重要主要指標是IOPS和吞吐量
IOPS(Input/Output Per Second)即每秒讀寫次數, 就像QPS.
磁盤的IOPS = 1000(ms)/(尋道時間+旋轉時間+傳輸時間).
按照上面的數據 (尋道時間+旋轉時間+傳輸時間(這裏忽略不計)) = 9+4 = 13ms, 1000/13 ≈ 76左右.
吞吐量(Throughput),指單位時間內能夠成功傳輸的數據數量.
(爲了白piao一下讀寫性能的測試結果, 上了某東打開了家裏電腦的希捷7200RPM 1T的商品頁面, 問客服要了讀寫性能的連接, 結果客服竟然說商家沒給他們具體的讀寫性能參數, 多是我想多了?打擾了~)
因而開啓了本身的windows, 下了個性能測試軟件進行I/O性能測試, 下面是測試的結果圖.
能夠看到Seq的比沒有Seq的高出了很是多. 沒錯, Seq就是順序I/O, 沒有Seq的就是隨機I/O, 性能差距一下體現出來了. 順序I/O的性能達到了 150MB/s 左右, 而隨機I/O位於 0.5 ~ 1.5MB/s之間,
測試多幾回結果可能會不一樣, 但不會相差太大
.
先發一下仍是個人windows電腦上面, 用在C盤上的固態
從數據能夠看到, 固態硬盤的數據幾乎都是遙遙領先, 由於固態硬盤在物理結構上和機械硬盤的差異致使的, 固態由半導體構成, 沒有移動的部件, 因此訪問的效率比機械硬盤高.
先說一下在計算機中的一個原理, 局部性原理.
局部性原理:當某個數據被使用到的時候, 跟它鄰近的數據也極可能會在後面被使用到.
在Linux中, 正是基於這個原理, 對磁盤I/O作了一些的優化.
當第一次讀取磁盤數據的時候, 好比只讀取 page1(4k), Linux 會同步的將後面 幾(3)個 page(page二、三、4)一塊兒讀取出來, 存放在 page cache當中, 第一次觸發的是 同步預讀
, 也就是上層必須等待這4個page都讀取完畢, 而且 page二、三、4 被作了預讀的標記. 當要讀page2的數據的時候, 由於以前已經讀取出來了, 在 page cache中能夠找到, 因此直接在page cache中讀取便可, 而且由於作了預讀標記, 代表這個預讀是有效的, 接下來會觸發第二次的預讀, 但此次是 異步預讀
, 上層須要的數據從page cache cp過去便可, 底層的此次異步預讀對上層程序是透明的, 此次會讀取 page5 ~page8 放入到 page cache 當中, 而且也作上標記, 再後面會繼續沿用第二次異步預讀的流程.
這讓我想起了 MySQL 中的一個page大小默認爲 16K, 會不會純屬是巧合呢??
這是預讀的情形, 還有一種就是後寫的狀況.
在OS每次接收到I/O請求的時候, 不會每次都去進行磁盤寫入, 若是每次都寫入的話, 效率就像上面測試的隨機I/O那樣, 爲了優化這種狀況, OS在進行寫入的時候, 會進行寫入合併, 將相鄰的寫請求進行合併以致於至少能夠進行 局部的順序I/O
, 這種處理提升了不少磁盤寫入的效率. 因此在通過OS的這個優化後, 最終的IOPS會超過計算出來的IOPS
.
Kafka 中的消息都是經過日誌追加的方式, 在查找的時候先經過二分查找對應 partition 下面的 Segment, 實際上kafka是維護了一個 Segment 的跳錶結構, 找到對應的 Segment, Segment上的文件數據都是順序讀寫, 實際上 partition 也是順序讀寫, 這也是爲何Kafka的吞吐能達到十萬級的緣由之一.
批量操做大部分狀況下都是有好處的, 要不是有OS的優化, 怕是真的連平常CRUD都不會寫.
此次算是對磁盤I/O相關的一次記錄吧(不說了, 最近的bug有點燙手).