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。

=============================
题图来源于:http://news.mydrivers.com/img/20090811/04182623.jpg
欢迎关注微信公众号:hellojavacases

关于此微信号:
分享Java问题排查的Case、Java业界的动态和新技术、Java的一些小知识点Test,以及和大家一起讨论一些Java问题或场景,这里只有Java细节的分享,没有大道理、大架构和大框架。

公众号上发布的消息都存放在http://hellojava.info上。

发表评论

电子邮件地址不会被公开。 必填项已用*标注


*