1. 引言#
一个项目的数据库响应慢的问题,结论就是操作系统层面上的 IO 性能差的问题,怎么样才能更有说服力。我们一般衡量 IO 性能主要用两种指标来衡量。响应时间:用微秒来测量完成一项操作所需的时间,这个一般由 oracle 来采集统计。吞吐量:以每个单位时间内完成的操作数量来测量。这个一般通过操作系统下的工具来统计,例如 iostat。本文主要是阐述如何从 oracle 的角度来确定 IO 是否慢,不再详细说明吞吐量及其测量方法。
2. 怎么才算 IO “慢”#
IO 慢是一个很主观的术语,它更多的取决于用户对系统和硬件的预期和实际的差异,这个差异是一个比较主观的感觉,量化到具体性能指标来看,在企业级平台下,当 IO 请求的响应时间大于 10ms 时,用户会开始敏感。不过响应时间是会不断变化的,有可能是数据从文件系统迁移到共享存储上,也可能是存储设备出现异常,又或者是业务增长导致的 IO 性能达到峰值。这就需要通过对响应时间这个指标作出作出多维度的测量。
3. 响应时间#
硬件不必对于每个 IO 请求都有相同的反映。总会有可能出现高峰和低谷。因此使用平均值是一种测量响应时间的通用方法。
注意:为了减缓这种高峰 / 低谷的异常场景带来的问题,样例数据量需要比较大。样例数据量应该至少是每小时 1000 次操作,目的就是为了提供给决测更可信和实用的依据。
-
IO 的类型
平均响应时间直接关联到具体的 IO 类型:
- 读或写
- 单块或多块
单块IO,指一次只读一个块。例如,当一个session等待一个单块IO时,典型的等待事件就是“db file sequential read”,表明正在等待需要的块。 多块读指的是一次读多个块,从2到128个Oracle块不等,依赖于块的大小与操作系统设置。通常一个多块请求容量上有1MB的限制。例如当一个session等待一次多块IO时,典型的等待事件就是“db file scattered read”,表明正在等待需要的块。
- 同步或异步
同步(阻塞)操作等待硬件完成物理IO,完成后能得到通知,合理地管理操作的成功或失败(成功读的情况下可以接收结果)。当需要等待系统调用结果的时候,进程的执行是被堵塞的。 对于异步(非阻塞)操作,一旦IO请求传递到硬件,或放入操作系统的队列中(典型的情况是物理IO开始之前),系统调用会立即返回。进程的执行不会被堵塞,因为它不需要等待系统调用的结果。它能继续执行,当IO操作有结果时再接收。
-
响应时间的阈值
一次典型的多块同步读 64x 8k (总计 512KB) 的平均时间应该在未出现 IO 变慢的情况下大约是 20 毫秒左右。小请求应该更快 (10-20 毫秒),大请求的消耗时间应该不多于 25 毫秒。异步操作应该至少和同步操作一样快,甚至还要更快。单块读至少应该和多块读一样快,甚至还要更快。“log file parallel write”,“control file write” 和 “direct path writes“等待时间应该不多于 15 毫秒。数据文件写的测量不像读那样简单。DBWR 以批量的方式 ("db file parallel write") 异步写入块,现在还没有写操作响应时间的标准。如果 DBWR (多块或单块,带或不带 IO salves) 足够快速能够清理脏块,那么其他的等待事件和统计信息就会显露出来。作为规则,超过上述等待事件时间的等待事件都应该详细分析,当对比之前的时间消耗,有明显变化时更需要知晓。
注意:当系统低于这些最大阈值的时候,并不意味着没有其他的调优方法。#
响应时间因系统而有所不同。例如,接下来的几项内容可以看做是正常平均值:
- 多块同步读时间是 10 毫秒。
- 单块同步读时间是 5 毫秒。
- 'log file parallel write' 时间是 3 毫秒。
以上是基于多块 IO 比单块 IO 需要更多的 IO 子系统资源的前提。如果接受这些建议,redo 日志最好放在最快的磁盘,并且没有其它并发活动的争用。
以下是各 IO 相关等待事件的响应时间阈值:
Wait Event R/W Synchronous/ Asynchronous Singleblock/ Multiblock Elapsed Time control file parallel write Write Asynchronous Multi < 15ms control file sequential read Read Synchronous Single < 20 ms db file parallel read Read Asynchronous Multi < 20 ms db file scattered read Read Synchronous Multi < 20 ms db file sequential read Read Synchronous Single < 20 ms direct path read Read Asynchronous Multi < 20 ms direct path read temp Read Asynchronous Multi < 20 ms direct path write Write Asynchronous Multi < 15 ms direct path write temp Write Asynchronous Multi < 15 ms log file parallel write Write Asynchronous Multi < 15 ms -
确定响应时间的途径
-
10046 trace file
当在10046 trace中使用level 8或12时,会包含相关的等待事件的信息,响应时间是ela字段,单位是微秒。 WAIT #5: nam='cell single block physical read' ela= 672 cellhash#=2520626383 diskhash#=1377492511 bytes=16384 obj#=63 tim=1280416903276618 >> 672 microseconds = 0.672 ms WAIT #5: nam='db file sequential read' ela= 1018 file#=2 block#=558091 blocks=1 obj#=0 tim=10191852599110 >> 1018 microseconds => 1.018 ms
-
System State Dump
对于每个系统级的进程,等待信息包括在进程信息中。通常显示一个活动的waiting for,或者等待完成,进程正在CPU中执行waited for/last wait for。 其中waiting for表示进程处于等待状态。11g之前可以查看seconds since wait started字段,显示进程已经等待多久了。从11gR1开始,”total“字段显示等待的时间。 如果waiting for显示一个进程正在等待一个IO相关的操作,seconds since wait started>0,表示可能IO丢失,session处于hang状态。(因为之前提到过平均可接受时间是20毫秒,任何IO等待时间超过1秒都需要关注)。 last wait for是与11g之前的版本相关的,表明进程不在等待(例如正在使用CPU)。等待时间记录到”wait_time“字段。(11g中wait_time被not in wait替代) last wait for 'db file sequential read' blocking sess=0x0 seq=100 wait_time=2264 seconds since wait started=0 file#=45, block#=17a57, blocks=1 >> 2264 microseconds => 2.264 ms waited for表示session不在等待。通常是11gR1以后的系统级trace中使用。total字段表示等待的总时间。 0: waited for 'db file sequential read' file#=9, block#=46526, blocks=1 wait_id=179 seq_num=180 snap_id=1 wait times: snap=0.007039 sec, exc=0.007039 sec, total=0.007039 sec wait times: max=infinite wait counts: calls=0 os=0 >> 0.007039 sec => 7.039 ms
-
Statspack 与 AWR reports
前台进程和后台进程的等待事件,平均响应时间通过 Wait Avg (ms) 反映 (以毫秒计算的平均读)。
表空间 IO
平均响应时间通过 Av Rd (ms) 反映 (以毫秒计算的平均读)。
等待事件直方图可以提供组成这些平均值的写操作时间分布。他会展示出所有写操作都接近于平均值,还是会有若干波峰或波谷的情况。每列都表明每个 bucket 之间等待事件时间分布的百分比。例如,<16ms 的等待大于 < 8ms。只要最大的百分比是从 < 1ms 到 16ms 的范围内,那么 IO 性能通常就可以接受。
-
4. 总结#
本文的目标不是为了排查 IO 变慢的原因,而是为了如何寻找判定 IO 慢的证据。如果性能变差,那么 IO 慢可能成为性能问题的一个潜在原因,需要从数据库角度来分析如何采集支持的证据;如果潜在原因是由于操作系统级别的 IO 慢,那么负责 IO 子系统工程师需要参与进来诊断和修复这个问题。