Java程序員也應該知道的係統知識係列之磁盤
作者:林昊
除了CPU、內存這兩個最重要的也是看起來和應用性能最為相關的硬件外,磁盤也是一個非常重要的部件,尤其是IO壓力比較大的存儲類的係統,磁盤是一個慢速設備,所以如果使用不當,會導致應用性能受到很大的影響。
首先我們需要知道運行的機器上的磁盤的狀況,可以通過執行cat /proc/scsi/scsi來查看,例如:
Attached devices:
Host: scsi0 Channel: 01 Id: 00 Lun: 00
Vendor: SEAGATE Model: ST3300655SS
Type: Direct-Access
上麵這個信息表示機器上插了一塊SEAGATE的硬盤,型號為ST3300655SS,剩下就可以去google下看看這個硬盤的具體信息了。
一般來說服務器為了提升數據的安全性和讀寫磁盤的速度,都會做RAID(關於raid是什麼具體請大家google),RAID的話又分為軟Raid和硬Raid,軟Raid的話可以通過cat /proc/mdstat來查看,如果是做硬Raid的,那麼在cat /proc/scsi/scsi的時候看到的可能就是類似下麵的信息:
Attached devices:
Host: scsi0 Channel: 00 Id: 00 Lun: 00
Vendor: HP Model: P410
Type: RAID
這種表示的是機器帶了Raid卡,並且做過了Raid。
Raid卡的具體信息,以及具體做了什麼方式的Raid,則需要通過硬件Raid廠商提供的工具來具體查看,例如上麵的HP Raid卡,可以用
hpacucli ctrl slot=1 show config detail
上麵slot到底是多少,可以通過
hpacucli ctrl all show來拿到。
在了解了這些硬件信息的情況下,磁盤要真正被應用使用,需要做分區,還有個重要的是要做文件係統的選擇,linux上目前我們要選擇的主要是ext3、ext4,ext4現在已經比較成熟,所以可以優先考慮,具體文件係統的不同請大家自行google吧,分區/文件係統的信息可以通過cat /proc/mounts來查看。
磁盤空間的狀況可通過df -h來查看,如果要分析每個目錄的空間狀況,可以用du -sh .或du -sh *看目錄裏麵所有文件或目錄的磁盤占用狀況,在磁盤空間這塊可能會碰到一個現象,就是df -h看到使用了很多,但du -sh *的時候看到相應的目錄下加起來也沒用掉那麼多,這種有可能是直接在程序裏把這個文件刪除了,但統計磁盤空間占用的時候還統計了,這種可以重啟應用來解決,也可以通過lsof | grep -i deleted來找下是什麼文件,以及是什麼進程中的操作。
Java程序在寫文件的時候,默認情況下並不是直接寫到磁盤上,而是先寫到os的cache裏,這也是為什麼很多時候去測試寫文件會發現速度快的無法理解,關於os cache怎麼去控製請參見之前寫的一篇文章,而對於有Raid卡的則還會有所不同,如果raid卡是帶cache的,那麼即使是os在把cache的內容刷入磁盤時,通常默認情況下也是先放入raid卡的cache,之後才寫入磁盤,可以看到這些多重的措施都是為了提升程序在讀寫文件時的性能,raid卡的cache策略需要通過raid卡的工具來管理,例如
Current Cache Policy: WriteBack, ReadAheadNone, Direct, Write Cache OK if Bad BBU
raid卡為了保障放入cache的數據不會丟失,會采用電池或電容的方式來保障,對於隻是記錄日誌的一些應用場景,就可以設置為即使電池沒電了,也寫入cache,這種情況下的風險就是如果機器掛了,那麼在raid卡cache裏的數據也就丟失了。
對於一些用來做存儲類型的係統,數據的丟失是不可接受的,像這類的場景,就需要強製寫入磁盤,這種在Java裏需要顯示的去調用FileDescriptor.sync或FileChannel.force,在這種情況下,os的cache以及raid卡的cache都將失去作用。
插播一個容易疑惑的點:在不同的機器上Java去獲取目錄上的文件時,有可能會發現返回的文件的順序是不一樣的,這個是因為目錄裏文件的返回順序在linux上是取決於inode的順序,而這個不太好控製,所以有可能會出現這樣的狀況,這個在Java應用中如果有類版本衝突的話很容易導致一個現象是不同機器的行為不一致。
由於磁盤是屬於慢速設備,所以磁盤的利用率如果要利用滿通常不是難事,磁盤的利用率可以通過iostat -x來查看,需要特別注意下這個裏麵的cpu iowait以及%util的值,這兩個值隻要稍微高一些對應用的影響就會非常的明顯,要查磁盤利用率到底是讀寫什麼文件造成的,需要用iotop、blktrace這些來查,具體可以看看我之前寫的一篇cpu iowait 高排查的case文章。
對於非存儲類的係統而言,通常來說要追求的目標都是文件的讀寫這塊不要成為瓶頸,一旦真的這塊成為了瓶頸(如果看到應用的線程大部分時候都卡在文件的讀寫上),那麼通常可以這麼去優化:
1. raid和raid卡;
如果沒有且資金沒問題的話可以考慮。
2. cache
讀盡可能采用cache來提升,而不是去操作磁盤,這幾乎是大多數場景的必殺技,君不見各家互聯網公司都是超級的依賴cache嗎…
3. 同步寫變異步寫
對於不是很重要的信息,例如一些可丟失的日誌,可以將其寫從同步寫入的方式改為異步寫,這個現在的log4j等都是支持的,另外要注意控製日誌級別以及需要輸出的日誌是否有必要;
4. 隨機寫轉順序寫
去了解過一些存儲係統的同學可能都會發現一個現象,這類係統很多都是先寫日誌,然後真正的數據則是放到內存,等內存積累到一定的量才真正寫入磁盤,這個就是典型的隨機寫轉順序寫的一種辦法,大家都知道的是磁盤之所以慢的一個主要原因是尋道時間,所以隨機寫轉為順序寫後寫的速度其實是提升了不少的。
對於存儲類的係統而言,則很有可能磁盤會成為整個係統的瓶頸點,也就是這個時候cpu/內存可能就不是主角了,因為存儲類的係統沒法靠os和raid卡cache來提升性能了,這種如果到了磁盤的瓶頸的話,基本就隻能靠純粹的硬件升級了,例如ssd或更高端的fusionio。
最後更新:2017-04-03 07:57:07