4、文件同步性能调优
JFS 的非顺序文件 I/O 会一直存储在内存中直到满足一定条件:
空闲列表缩小到 minfree,以致需要进行页替换。
syncd 守护程序按固定调度间隔刷新页。
执行了 sync 命令。
随机后写在达到随机后写阈值后清空脏页面。
如果在以上的任一条件满足前已存储了过多页,则在 syncd 守护程序进行刷新时,会获得一个 i-node 锁并保持到所有的脏页都被写入磁盘。在这段时间里,任何试图访问此文件的线程会由于无法获得 i-node 锁而被阻塞。请记住:syncd 守护程序会顺利的刷新一个文件中的所有脏页,但限于一次一个文件。在一个拥有大量内存并同时有大量页需要修改的系统中,syncd 守护程序刷新页时 I/O 可能达到高峰值。
AIX 有一个称为 sync_release_ilock 的可调选项。ioo 命令加上 -o sync_release_ilock=1 选项允许在清空该文件的脏页面后释放 i-node 锁。这一选项使得在调用 sync() 的过程中访问该文件有更好的响应。
阻塞效果也可通过在 syncd 守护程序中提高同步频率使之最小化。更换用于启用 syncd 守护程序的 /sbin/rc.boot。然后重新引导系统使之生效。对现行系统,杀死 syncd 守护程序进程并按新的值重新启动守护程序。
第三种调优这种行为的方法是使用 ioo 命令开启随机后写功能
文件系统缓冲区调优
以下 ioo 参数可用于调优磁盘 I/O:
numfsbufs 参数
当有大量针对文件系统的同步或大型 I/O 或是存在针对文件系统的大型顺序 I/O 时,这些 I/O 可能会在等待 bufstruct 时成为文件系统级的瓶颈。每个文件系统的 bufstructs 数目(称为 numfsbufs)可使用 ioo 命令增加。该值仅在文件系统加载后才会生效;因此如果更改了这个值,则必须卸载然后再次加载文件系统。numfsbufs 的缺省值目前为每个文件系统 93 个 bufstruct。
j2_nBufferPerPagerDevice 参数
在增强型 JFS 中,bufstruct 数量由参数 j2_nBufferPerPagerDevice 指定。当前增强型 JFS 文件系统的缺省 bufstruct 数是 512。每个增强型 JFS 文件系统的 bufstructs 数(j2_nBufferPerPagerDevice)可以使用 ioo 命令来增加。该值在文件系统被加载后才起作用。
lvm_bufcnt 参数
如果应用程序正在处理很大量的裸 I/O 而不通过文件系统,同文件系统相同类型的瓶颈也可能出现在 LVM 层上。极大量的 I/O 加上极快的 I/O 设备可能会导致 LVM 层上的瓶颈。但是如果真的出现瓶颈,则可以通过 ioo 命令增加 lvm_bufcnt 参数,以提供大量的“uphysio”缓冲区。该值会立刻生效。当前的缺省值是 9 个 “uphysio” 缓冲区。由于当前 LVM 将 I/O 分为每个 128 K,而 lvm_bufcnt 的缺省值为 9,故一次可写入 9*128 K。如果正在进行的 I/O 大于 9*128 K,增加 lvm_bufcnt 的值才会有利。
hd_pbuf_cnt 参数
hd_pbuf_cnt 参数控制可用于 LVM 设备驱动程序的 pbufs 数。pbuf 是用于存放暂挂于 LVM 层的 I/O 请求的固定内存缓冲区。
在 AIX 中,顺序 I/O 的结合使得无论 I/O 包括多少页,每个顺序 I/O 请求只使用单个 pbuf。这种类型的瓶颈一般很难遇到。而对于随机 I/O,除非运行 syncd 守护程序,I/O 一般会被零星地刷新。
确定是否发生 pbuf 瓶颈的最好方法是检查称为 hd_pendqblked 的 LVM 变量。以下的脚本会给出该变量的值:
#!/bin/ksh
# requires root authority to run
# determines number of times LVM had to wait on pbufs since system boot
addr=`echo "knlist hd_pendqblked" | /usr/sbin/crash 2>/dev/null |tail -1| cut -f2 -d:`
value=`echo "od $addr 1 D" | /usr/sbin/crash 2>/dev/null | tail -1| cut -f2 -d:`
echo "Number of waits on LVM pbufs are: $value"
exit 0
ioo -a 命令也会显示 hd_pendqblked 值。
注:
请不要把 hd_pbuf_cnt 值设得太大,因为除了重新引导系统无法减小该值。
pd_npages 参数
pd_npages 参数指定当删除文件时 RAM 的某一块中应该删除的页数。改变此值只对那些需要删除文件的实时应用程序才有用。由于在分派某个进程/线程之前将删除少量的页面,因此通过减小 pd_npages 参数的值,实时应用程序可获得更快的响应时间。缺省值是最大可能文件大小除以页面大小(目前为 4096);如果最大可能文件大小为 2 GB,则 pd_npages 参数的值缺省为 524288。
v_pinshm 参数
当 v_pinshm 参数设置为 1 时,如果执行 shmget() 的应用程序指定 SHM_PIN 作为标志的一部分,就会使共享内存段中的页面由 VMM 固定。缺省值为 0。
应用程序可以选择提供某种可调优性:指定应用程序是否应该使用 SHM_PIN 标志(例如: Oracle 8.1.5 及以上版本中提供的 lock_sga 参数)。请避免固定过多的内存,因为在这种情况下无法进行页替换。由于节约了这些共享内存段的异步 I/O 开销(不需要异步 I/O 内核扩展来固定缓冲区),因此这种固定是很有用的。
fsbufwaitcnt 和 psbufwaitcnt 计数器
只要 bufstruct 变得不可用以及 VMM 将一个线程放入 VMM 等待列表中,fsbufwaitcnt 和 psbufwaitcnt 计数器就会递增。使用 crash 命令或 ioo -a 命令的 fsbufwaitcnt 和 psbufwaitcnt 选项来检查这些计数器的值。下面是输出的示例:
# ioo -a
hd_pendqblked = 305
psbufwaitcnt = 0
fsbufwaitcnt = 337
xpagerbufwaitcnt 计数器
只要增强型 JFS 文件系统上的 bufstruct 不可用,xpagerbufwaitcnt 就会递增。可使用 ioo -a 命令检查 xpagerbufwaitcnt 计数器的值。下面是输出的示例:
# ioo -a
xpagerbufwaitcnt = 815
5、直接 I/O 调优
当您在对文件进行正常的 I/O 处理时,I/O 在应用程序缓冲区和 VMM 之间来回进行。在缓冲区中的内容通过 VMM 把实存作为文件缓冲区的高速缓存的使用而高速缓存于 RAM 中。如果文件高速缓存命中率很高,那么这个高速缓存的类型在提高 I/O 的总体性能上将十分有效。但是高速缓存命中率很低的应用程序或者执行大量 I/O 的应用程序也许不会从正常的高速缓存 I/O 使用中得到很多好处。
直接 I/O 的主要益处在于通过消除从 VMM 文件高速缓存到用户缓存的副本来减少 CPU 对文件读操作和写操作的使用率。如果高速缓存命中率低,那么大多数读取请求不得不转向磁盘。写操作在大多数情况下使用正常高速缓存 I/O 要更快。但是如果文件是以 O_SYNC 或 O_DSYNC打开的,那么写操作将不得不转向磁盘。在这种情况下,直接 I/O 可能使应用程序获益,因为数据的副本被消除了。
另一个益处是直接 I/O 可以允许应用程序避免稀释高速缓存对其他文件的效能。当一个文件被读取或写入时,该文件竞争内存空间,有可能引起其他文件数据被推出内存。如果一个应用程序开发者知道某些文件有较低的高速缓存利用率特征,那么只有那些文件可以用 O_DIRECT 打开。
为了让直接 I/O 有效地工作,I/O 请求适用于正被使用的文件系统的类型。finfo() 和 ffinfo() 子例程可以用来查询偏移量、长度以及固定块大小文件系统、碎片文件系统和大文件文件系统有关对地址校正的需求(直接 I/O 没有支持压缩文件系统)。查询到的信息包含于 diocapbuf 结构中,该结构在 /usr/include/sys/finfo.h 文件中描述。
为了避免一致问题,如果有多个调用用来打开文件并且一个或多个调用没有制定 O_DIRECT 而另一个打开操作指定了 O_DIRECT,那么文件将保留于正常高速缓存 I/O 模式。同样的,如果文件是通过 shmat() 或 mmap() 系统调用来映射到内存中,它们保留在正常高速缓存模式。如果最后一个冲突,非直接存取被消除,那么文件系统将把文件移入直接 I/O 模式(或者使用 close()、munmap() 或者使用 shmdt() 子例程)。从正常模式到直接 I/O 模式可能代价不小,因为所有在内存中修改过的页面将不得不在那点上刷新磁盘。
直接 I/O 读操作的性能
即使使用直接 I/O 可能减少 CPU 的使用,但很有可能产生更长的消逝时间,特别对小型 I/O 请求而言,因为请求不会在内存中高速缓存。
直接 I/O 读取操作会从磁盘引起同步读操作,然而通过正常的高速缓存策略,读取操作可能会从高速缓存那里得到满意的结果。如果数据在内存中遵循正常高速缓存策略,那么这样可能导致性能低下。直接 I/O 也忽略 VMM 预读算法,因为 I/O 并不通过 VMM。预读算法对顺序存取文件非常有用,因为 VMM 可以启动磁盘请求并且能在应用程序请求页面之前使页面早就驻留在内存中。应用程序可以通过一下方法中的一种补偿预读的损失:
执行读取请求(最小 128 K)
使用多个线程执行异步直接 I/O 预读取。
使用异步 I/O 设施诸如 aio_read() 或者 lio_listio()
直接 I/O 写操作的性能
直接 I/O 写操作绕过 VMM 直接写入磁盘,以致于可能产生严重的性能损失;在正常高速缓存的 I/O 中,写操作可以写入内存,稍后通过 sync 或 write behind 操作清空到磁盘上。由于直接 I/O 写操作并不复制到内存,当一个 sync 操作执行后,它不会必须刷新这些页面,这样一来就减少了 syncd 守护程序必须执行的工作量。
直接 I/O 调优摘要
直接 I/O 本质上比常规 I/O 需要更少的 CPU 周期。 I/O 增强性应用程序不会在常规 I/O 所提供的高速缓存中得到益处,但是可以使用直接 I/O 来增强性能。
作为直接 I/O 的适当候选者的程序通常受 CPU 限制并且执行大量的磁盘 I/O。拥有大量顺序 I/O 的技术应用程序是适当的候选者。执行许多小型 I/O 的应用程序一般来说受到较少的性能益处,因为直接 I/O 不能预读或后写。受益于条带区的应用程序同样是不错的候选者。