如何判断数据库IO是否慢

1. 引言

一个项目的数据库响应慢的问题，结论就是操作系统层面上的 IO 性能差的问题，怎么样才能更有说服力。我们一般衡量 IO 性能主要用两种指标来衡量。响应时间：用微秒来测量完成一项操作所需的时间，这个一般由 oracle 来采集统计。吞吐量：以每个单位时间内完成的操作数量来测量。这个一般通过操作系统下的工具来统计，例如 iostat。本文主要是阐述如何从 oracle 的角度来确定 IO 是否慢，不再详细说明吞吐量及其测量方法。

2. 怎么才算 IO “慢”

IO 慢是一个很主观的术语，它更多的取决于用户对系统和硬件的预期和实际的差异，这个差异是一个比较主观的感觉，量化到具体性能指标来看，在企业级平台下，当 IO 请求的响应时间大于 10ms 时，用户会开始敏感。不过响应时间是会不断变化的，有可能是数据从文件系统迁移到共享存储上，也可能是存储设备出现异常，又或者是业务增长导致的 IO 性能达到峰值。这就需要通过对响应时间这个指标作出作出多维度的测量。

3. 响应时间

硬件不必对于每个 IO 请求都有相同的反映。总会有可能出现高峰和低谷。因此使用平均值是一种测量响应时间的通用方法。
注意：为了减缓这种高峰 / 低谷的异常场景带来的问题，样例数据量需要比较大。样例数据量应该至少是每小时 1000 次操作，目的就是为了提供给决测更可信和实用的依据。

IO 的类型

平均响应时间直接关联到具体的 IO 类型：

读或写

单块或多块
Copy
单块IO，指一次只读一个块。例如，当一个session等待一个单块IO时，典型的等待事件就是“db file sequential read”，表明正在等待需要的块。
多块读指的是一次读多个块，从2到128个Oracle块不等，依赖于块的大小与操作系统设置。通常一个多块请求容量上有1MB的限制。例如当一个session等待一次多块IO时，典型的等待事件就是“db file scattered read”，表明正在等待需要的块。

同步或异步
Copy
同步(阻塞)操作等待硬件完成物理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

Copy
当在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

Copy
对于每个系统级的进程，等待信息包括在进程信息中。通常显示一个活动的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 子系统工程师需要参与进来诊断和修复这个问题。