【1024程序员节献礼】鲲鹏性能优化十板斧(四)——磁盘IO子系统性能调优<TaiShan特战队出品>
特战队六月底成立,至今百日有余,恰逢1024程序员节,遂整理此文,献礼致敬!希望能为广大在鲲鹏处理器上开发软件、性能调优的程序员们,提供一点帮助。从今天开始,将陆续推出性能调优专题文章。
1 磁盘IO子系统性能调优
1.1 调优简介
调优思路
CPU的Cache、内存和磁盘之间的访问速度差异很大,当CPU计算所需要的数据并没有及时加载到内存或Cache中时,CPU将会浪费很多时间等待磁盘的读取。计算机系统通过cache、RAM、固态盘、磁盘等多级存储结构,并配合多种调度算法,来消除或缓解这种速度不对等的影响。但是缓存空间总是有限的,我们可以利用局部性原理,尽可能的将热点数据提前从磁盘中读取出来,降低CPU等待磁盘的时间浪费。因此我们的部分优化手段其实是围绕着如何更充分的利用Cache获得更好的IO性能。
另外,本章节也会介绍从文件系统层面的优化手段。
主要优化参数
优化项 | 优化项简介 | 默认值 | 生效范围 | 鲲鹏916 | 鲲鹏920 |
脏数据缓存到期时间 | 调整脏数据缓存到期时间,分散磁盘的压力。 | 3000(单位为1/100秒) | 立即生效 | Y | Y |
脏页面占用总内存最大的比例 | 调整脏页面占用总内存最大的比例(以memfree+Cached-Mapped为基准),增加PageCache命中率。 | 10% | 立即生效 | Y | Y |
脏页面缓存占用总内存最大的比例 | 调整脏页面占用总存最大的比例,避免磁盘写操作变为O_DIRECT同步,导致缓冲机制失效。 | 40% | 立即生效 | Y | Y |
调整磁盘文件预读参数 | 根据局性原理,在读取磁盘数据时,额外地多读一定量的数据缓存到内存。 | 128KB | 立即生效 | Y | Y |
磁盘IO调度方式 | 根据业务处理数据的特点,选择合适的IO调度器。 | cfq | 立即生效 | Y | Y |
文件系统 | 选用性能更好的文件系统以及文件系统相关的选项。 | N/A | 立即生效 | Y | Y |
1.2 常用性能监测工具
1.2.1 iostat工具
介绍
iostat是调查磁盘IO问题使用最频繁的工具。它汇总了所有在线磁盘统计信息,为负载特征归纳,使用率和饱和度提供了指标。它可以由任何用户执行,统计信息直接来源于内核,因此这个工具的开销基本可以忽略不计。
安装方式
iostat一般会随系统安装。如果没有,以CentOS为例,可以使用以下命令安装:
# yum -y install sysstat
使用方式
命令格式:直接使用命令+参数,例如
# iostat -d -k -x 1 100
常用参数如下:
-c | 显示CPU使用情况。 |
-d | 显示磁盘使用情况。 |
-k | 以KB为单位显示。 |
-m | 以M为单位显示。 |
-p | 显示磁盘单个的情况。 |
-t | 显示时间戳。 |
-x | 显示详细信息。 |
我们也可以在最后添加统计周期和统计时长,比如上面的样例中,是要求以1秒为周期统计,总共统计100s。
输出格式:
Device: rrqm/s wrqm/s r/s w/s rkB/s wkB/s avgrq-sz avgqu-sz await svctm %util
sda 0.02 7.25 0.04 1.90 0.74 35.47 37.15 0.04 19.13 5.58 1.09
dm-0 0.00 0.00 0.04 3.05 0.28 12.18 8.07 0.65 209.01 1.11 0.34
dm-1 0.00 0.00 0.02 5.82 0.46 23.26 8.13 0.43 74.33 1.30 0.76
dm-2 0.00 0.00 0.00 0.01 0.00 0.02 8.00 0.00 5.41 3.28 0.00
参数含义如下:
rrqm/s | 每秒合并放入请求队列的读操作数。 |
wrqm/s | 每秒合并放入请求队列的写操作数。 |
r/s | 每秒磁盘实际完成的读I/O设备次数。 |
w/s | 每秒磁盘实际完成的写I/O设备次数。 |
rkB/s | 每秒从磁盘读取KB数。 |
wkB/s | 每秒写入磁盘的KB数。 |
avgrq-sz | 平均请求数据大小,单为为扇区(512B)。 |
avgqu-sz | 平均I/O队列长度(操作请求数)。 |
await | 平均每次设备I/O操作的等待时间(毫秒)。 |
svctm | 平均每次设备I/O操作的响应时间(毫秒)。 |
%util | 用于I/O操作时间的百分比,即使用率。 |
重要参数详解:
1. rrqm/s和wrqm/s,每秒合并后的读或写的次数(合并请求后,可以增加对磁盘的批处理,对HDD还可以减少寻址时间)。如果值在统计周期内为非零,也可以看出数据的读或写操作的是连续的,反之则是随机的。
2. 如果%util接近100%(即使用率为百分百),说明产生的I/O请求太多,I/O系统已经满负荷,相应的await也会增加,CPU的wait时间百分比也会增加(通过TOP命令查看),这时很明显就是磁盘成了瓶颈,拖累整个系统。这时可以考虑更换更高性的能磁盘,或优化软件以减少对磁盘的依赖。
3. await(读写请求的平均等待时长)需要结合svctm 参考。svctm的和磁盘性能直接有关,它是磁盘内部处理的时长。await的大小一般取决于svctm以及I/O队列的长度和。svctm一般会小于await,如果svctm比较接近await,说明I/O几乎没有等待时间(处理时间也会被算作等待的一部分时间);如果wait大于svctm,差的过高的话一定是磁盘本身IO的问题;这时可以考虑更换更快的磁盘,或优化应用。
4. 队列长度(avgqu-sz)也可作为衡量系统I/O负荷的指标,但是要多统计一段时间后查看,因为有时候只是一个峰值过高。
1.3 优化方法
1.3.1 调整脏数据刷新策略,减小磁盘的IO压力
原理
PageCache中需要回写到磁盘的数据为脏数据。在应用程序通知系统保存脏数据时,应用可以选择直接将数据写入磁盘(O_DIRECT),或者先写到PageCache(非O_DIRECT模式) 。非O_DIRECT模式,对于缓存在PageCache中的数据的操作,都在内存中进行,减少了对磁盘的操作。
修改方式
系统中提供了以下参数来调整策略:
1. /proc/sys/vm/dirty_expire_centisecs此参数用于表示脏数据在缓存中允许保留的时长,即时间到后需要被写入到磁盘中。此参数的默认值为30s(3000 个1/100秒)。如果业务的数据是连续性的写,可以适当调小此参数,这样可以避免IO集中,导致突发的IO等待。可以通过echo命令修改:
# echo 2000 > /proc/sys/vm/dirty_expire_centisecs
2. /proc/sys/vm/dirty_background_ratio 脏页面占用总内存最大的比例(以memfree+Cached-Mapped为基准),超过这个值,pdflush线程会刷新脏页面到磁盘。增加这个值,系统会分配更多的内存用于写缓冲,因而可以提升写磁盘性能。但对于磁盘写入操作为主的的业务,可以调小这个值,避免数据积压太多最后成为瓶颈,可以结合业务并通过观察await的时间波动范围来识别。此值的默认值是10,可以通过echo来调整:
echo 8 > /proc/sys/vm/dirty_background_ratio
3. /proc/sys/vm/dirty_ratio 为脏页面占用总内存最大的比例,超过这个值,系统不会新增加脏页面,文件读写也变为同步模式。文件读写变为同步模式后,应用程序的文件读写操作的阻塞时间变长,会导致系统性能变慢。此参数的默认值为40,对于写入为主的业务,可以增加此参数,避免磁盘过早的进入到同步写状态。
如果加大了脏数据的缓存大小和时间,在意外断电情况下,丢失数据的概率会变多。因此对于需要立即存盘的数据,应用应该采用O_DIRECT模式避免关键数据的丢失。
1.3.2 调整磁盘文件预读参数
原理
文件预取的原理,就是根据局部性原理,在读取数据时,会多读一定量的相邻数据缓存到内存。如果预读的数据是后续会使用的数据,那么系统性能会提升,如果后续不使用,就浪费了磁盘带宽。在磁盘顺序读的场景下,调大预取值效果会尤其明显。
修改方式
文件预取参数由文件read_ahead_kb指定,CentOS中为“/sys/block/$DEVICE-NAME/queue/read_ahead_kb”($DEVICE-NAME为磁盘名称),如果不确定,则通过命令以下命令来查找。
# find / -name read_ahead_kb
此参数的默认值128KB,可使用echo来调整,仍以CentOS为例,将预取值调整为4096KB:
# echo 4096 > /sys/block/$DEVICE-NAME /queue/read_ahead_kb
这个值实际和读模型相关,要根据实际业务调整。
1.3.3 优化磁盘IO调度方式
原理
文件系统在通过驱动读写磁盘时,不会立即将读写请求发送给驱动,而是延迟执行,这样Linux内核的I/O调度器可以将多个读写请求合并为一个请求或者排序(减少机械磁盘的寻址)发送给驱动,提升性能。我们在前文介绍工具iostat时,也提到了合并的统计,这个值就是由这个过程统计获得。
目前Linux版本主要支持3种调度机制:
1. CFQ,完全公平队列调度
早期Linux 内核的默认调度算法,它给每个进程分配一个调度队列,默认以时间片和请求数限定的方式分配IO资源,以此保证每个进程的 IO 资源占用是公平的。这个算法在IO压力大,且IO主要集中在某几个进程的时候,性能不太友好。
2. DeadLine,最终期限调度
这个调度算法维护了4个队列,读队列,写队列,超时读队列和超时写队列。当内核收到一个新请求时,如果能合并就合并,如果不能合并,就会尝试排序。如果既不能合并,也没有合适的位置插入,就放到读或写队列的最后。一定时间后, I/O调度器会将读或写队列的请求分别放到超时读队列或者超时写队列。这个算法并不限制每个进程的IO资源,适合IO压力大且IO集中在某几个进程的场景,比如大数据、数据库使用HDD磁盘的场景。
3. NOOP,也叫NONE,是一种简单的FIFO调度策略
因为固态硬盘支持随机读写,所以固态硬盘可以选择这总最简单的调度策略,性能最好。
修改方式
查看当前的调度方式:
# cat /sys/block/$DEVICE-NAME/queue/scheduler
noop deadline [cfq]
[ ]中即为当前使用的磁盘IO调度模式。
操作系统不同,返回的值或默认值可能不同。
如果需要修改,可以采用echo来修改,比如要将sda修改为deadline:
# echo deadline > /sys/block/sda/queue/scheduler
1.3.4 文件系统参数优化
Linux支持多种文件系统,不同的文件系统性能上也存在着差异,因此如果可以选择,可以选用性能更好的文件系统,比如XFS。在创建文件系统时,又可以通过增加一些参数进行优化。
另外Linux在挂载文件分区时,也可以增加参数来达到性能提升的目的。
磁盘挂载方式优化nobarrier
原理
当前Linux文件系统,基本上采用了日志文件系统,确保在系统出错时,可以通过日志进行恢复,保证文件系统的可靠性。Barrier(栅栏),即先加一个栅栏,保证日志总是先写入,然后对应数据才刷新到磁盘,这种方式保证了系统崩溃后磁盘恢复的正确性,但对写入性能有影响。
服务器如果采用了RAID卡,并且RAID本身有电池,或者采用其它保护方案,那么就可以避免异常断电后日志的丢失,我们就可以关闭这个栅栏,可以达到提高性的目的。
修改方式
假如sda挂载在“/home/disk0”目录下,默认的fstab条目是:
# mount -o nobarrier -o remount /home/disk0
nobarrier参数使得系统在异常断电时无法确保文件系统日志已经写到磁盘介质,因此只适用于使用了带有保护的RAID卡的情况。
选用性能更优的文件系统XFS
原理
XFS是一种高性能的日志文件系统,XFS极具伸缩性,非常健壮,特别擅长处理大文件,同时提供平滑的数据传输。因此如果可以选择,我们可以优先选择XFS文件系统。
XFS文件系统在创建时,可先选择加大文件系统的block,更加适用于大文件的操作场景。
修改方式
步骤 1 格式化磁盘。假设我们要对sda1进行格式化:
# mkfs.xfs /dev/sda1
步骤 2 指定blocksize,默认情况下为4KB(4096B),我们假设在格式化时指定为变更为8192B:
# mkfs.xfs /dev/sda1 -b size=8192
----结束
1.3.5 使用异步文件操作libaio提升系统性能
原理
对于磁盘文件,文件的读取是同步的,导致线程读取文件时,属于阻塞状态。程序为了提升性能和磁盘的吞吐,程序会创建几个单独的磁盘读写线程,并通过信号量等机制进行线程间通信(同时带有锁);显然线程多,锁多,会导致更多的资源抢占,从而导致系统整体性能下降。
libaio提供了磁盘文件读写的异步机制,使得文件读写不用阻塞,结合epoll机制,实现一个线程可以无阻塞的运行,同时处理多个文件读写请求,提升服务器整体性能。
修改方式
参考附录中A libaio实现参考。
- 点赞
- 收藏
- 关注作者
评论(0)