铭鸿体育资讯网

磁盘IO爆满、应用卡顿,别急着加硬盘,根源竟在内存和SQL。

最近发现数据库响应特别慢,监控一看磁盘IO一直跑满,平均等待时间飙升。很多人第一反应就是磁盘不够快,该换SSD或者加硬盘

最近发现数据库响应特别慢,监控一看磁盘IO一直跑满,平均等待时间飙升。很多人第一反应就是磁盘不够快,该换SSD或者加硬盘了。但经验告诉我,事情没那么简单,IO爆满只是表象,真正的原因往往藏在内存、SQL和锁这些软件层面。

我碰到过好几次这种情况,每次都是先查操作系统,用iostat看看是哪个磁盘在忙。有时候是数据文件,有时候是日志文件,甚至可能是归档文件。如果发现%util一直100%,而读写速度还没到物理极限,那多半是IO请求太多,而不是磁盘本身慢。

接下来得看Oracle内部,查v$filestat,按物理读和物理写排序,找出最忙的数据文件。然后看平均IO时间,如果超过20毫秒,说明单次IO已经变慢了,这时候才要考虑存储层。但大多数情况下,平均IO时间并不高,只是请求量太大,把磁盘队列堵住了。

这时候AWR报告就很有用,看Top 5 Foreground Events。最常见的是“db file sequential read”和“db file scattered read”。前者是单块读,通常是索引读取;后者是多块读,一般是全表扫描。如果全表扫描占了大部分等待时间,那SQL就是元凶。

内存优化是性价比最高的办法。先检查SGA和PGA加起来占了多少内存,别超过物理内存的80%。如果SGA太小,数据页经常被挤出内存,每次查询都得从磁盘读,IO自然就上去了。Buffer Cache命中率低于95%的话,要么加DB_CACHE_SIZE,要么优化SQL让数据重用。

我习惯把热点表放到KEEP POOL里,比如那些频繁用到的字典表。低频访问的表放到RECYCLE POOL,避免它们占着缓存不放。这样精细化管理,能省不少IO。

SQL优化是最直接的手段。通过AWR的Top SQL,按disk_reads排序,找出那些一次执行就产生几十万物理读的查询。这些SQL通常是大表全表扫描,比如没有索引的ORDER BY、LIKE '%...%',或者GROUP BY。给它们加上合适的索引,就能把随机IO变成单块读,效率高很多。

有一次我遇到一个查询,跑一次要读20GB数据,把IO带宽直接占满。后来发现是WHERE条件里用了函数,像TO_CHAR(date)导致索引失效。去掉函数后,查询走了索引,IO瞬间降到1%以下。

并发控制也很重要。有时候应用卡死,是因为死锁导致会话挂起,其他会话反复重试,产生大量无效IO。用v$locked_object和v$session定位被锁的会话,杀掉僵尸进程,释放文件句柄,IO就能恢复。

回滚段和UNDO表空间也要注意。长事务回滚会产生大量写IO,如果UNDO表空间太小,动态扩展又会增加IO压力。设置合理的UNDO_RETENTION,并分配足够大的空间,避免回滚时临时扩容。

操作系统层面也能优化。在挂载文件系统时加上noatime和nodiratime,避免每次读都更新访问时间,减少元数据写IO。IO调度器选noop或deadline,尤其是SSD,别用CFQ拖慢速度。异步IO要确保开启,Oracle的写进程靠它才能高效。

DBWn进程数可以根据CPU核心数适当增加,让脏块写入并行化。临时表空间放在高速存储上,别跟业务数据抢IO。

物理存储架构是最后的手段。如果软件和OS都优化到位,IO还是高,那就得考虑物理隔离。把Redo日志和控制文件放在低延迟的RAID10或NVMe上,数据文件和索引分开,UNDO表空间单独放。条带大小也要根据场景调整,OLTP用128KB-256KB,数据仓库用1MB。

RAID级别选RAID10,读写性能均衡,RAID5有写惩罚,不适合高写入的日志。硬件升级到NVMe SSD能从根本上解决IOPS问题,但那是万不得已的选择。

调优的顺序很重要:先SQL,再内存,然后锁,接着操作系统,最后存储。千万别跳过前两步直接换硬盘,那是花钱不讨好。我见过太多人一上来就加内存换SSD,结果问题依旧,最后发现是SQL写得烂。

持续监控是必须的。用iostat、sar、v$filestat和AWR建立常态化监控,设置IO延迟告警。如果平均IO时间超过20毫秒,就得赶紧排查。

总之,磁盘IO性能问题不是硬件不行,而是软件没调好。只要按这个思路走,大部分问题都能解决。普通人都能理解,别把调优想得太高深,一步一步来就行。