记一次磁盘IO高问题排查过程

本文详细介绍了如何通过修改`updatedb.conf`配置文件来停止对特定目录的索引更新操作,并将索引更新频率从每日调整为每周一次,以减轻服务器磁盘IO负载的问题。同时,解释了`updatedb`脚本的作用及工作原理,包括其用于建立索引的过程和可能引发的性能问题。

我们服务器搭建了cacti进行监控,一次通过查看磁盘IO图像的时候,发现每天凌晨3:20-3:35的时候磁盘IO一下飙的很高,然后想知道到底是哪个进程占用那么高的IO,下面是解决方法:

写个检测脚本check_io_process.sh,当磁盘IO占用高的时候进行磁盘IO读写进程次数的检测:

#!/bin/bash
# Date: 2013/8/20
# Author: zhangkai
# Description: This script is used to check IO higher process.
# History:
iostat_log=/data/logs/iostat/iostat.log
dmesg_log=/data/logs/iostat/dmesg.log
dstat_log=/data/logs/iostat/dstat.log

if [ ! -d /data/logs/iostat ];then
    mkdir -p /data/logs/iostat
fi

add(){
str=$@
sum=`echo ${str// /+}|bc -l`
}

iostat -x 1 5 > $iostat_log
idle_percent=`cat $iostat_log | awk 'BEGIN{flag=0} {if(flag ==1){print $12; flag=0;} if (index($0, "%util" ))  {flag = 1;}}'`

add $idle_percent

#求5次查询IO占用率的平均值

avg=`echo $sum/5|bc`

if [[ $avg -ge 70 ]];then
    echo 1 > /proc/sys/vm/block_dump
    echo "-----------------------------------------------" >> $dmesg_log
    echo `date "+%Y-%m-%d %H:%M:%S"` >> $dmesg_log
    python /data/dmesg_io.py >> $dmesg_log
   
    echo "-----------------------------------------------" >> $dstat_log
    echo `date "+%Y-%m-%d %H:%M:%S"` >> $dstat_log
    dstat -d --top-bio 1 10 >> $dstat_log
    echo 0 > /proc/sys/vm/block_dump   
fi

其中该shell脚本调用了检测磁盘IO读写进程次数的python脚本,下面是dmesg_io.py的代码:

#!/usr/bin/python
# Monitoring per-process disk I/O activity
# written by http://www.vpsee.com

import sys, os, time, signal, re

class DiskIO:
    def __init__(self, pname=None, pid=None, reads=0, writes=0):
        self.pname = pname
        self.pid = pid
        self.reads = 0
        self.writes = 0

def main():
    argc = len(sys.argv)
    if argc != 1:
        print "usage: ./iotop"
        sys.exit(0)

    if os.getuid() != 0:
        print "must be run as root"
        sys.exit(0)

    signal.signal(signal.SIGINT, signal_handler)
    os.system('echo 1 > /proc/sys/vm/block_dump')
    print "TASK              PID       READ      WRITE"
   while True:
    os.system('dmesg -c > /tmp/diskio.log')
    l = []
    f = open('/tmp/diskio.log', 'r')
    line = f.readline()
    while line:
        m = re.match(\
            '^(\S+)\((\d+)\): (READ|WRITE) block (\d+) on (\S+)', line)
        if m != None:
            if not l:
                l.append(DiskIO(m.group(1), m.group(2)))
                line = f.readline()
                continue
            found = False
            for item in l:
                if item.pid == m.group(2):
                    found = True
                    if m.group(3) == "READ":
                        item.reads = item.reads + 1
                    elif m.group(3) == "WRITE":
                        item.writes = item.writes + 1
            if not found:
                l.append(DiskIO(m.group(1), m.group(2)))
        line = f.readline()
    time.sleep(1)
    for item in l:
        print "%-10s s d d" % \
            (item.pname, item.pid, item.reads, item.writes)

def signal_handler(signal, frame):
    os.system('echo 0 > /proc/sys/vm/block_dump')
    sys.exit(0)

if __name__=="__main__":
    main()

 

发现在3:20-3:35的日志如下(仅列出部分):

[root@localhost iostat]# cat dmesg.log

-----------------------------------------------

2013-08-22 03:23:06
TASK              PID       READ      WRITE
updatedb        18661       2951          0
kjournald         804          0        525
kjournald        1826          0        576

-----------------------------------------------
2013-08-22 03:24:05
TASK              PID       READ      WRITE
updatedb        18661       3007          0
kjournald         804          0        238
kjournald        1826          0        112
flush-8:0       11687          0         18
-----------------------------------------------
2013-08-22 03:25:05
TASK              PID       READ      WRITE
updatedb        18661       2689          0
kjournald         804          0        229
kjournald        1826          0         44

 

说明是updatedb这进程惹的祸,google查之,这个是由[cron]自动运行的更新系统数据的脚本。
其作用是为你系统里面的文件建立索引,以便于locate和whereis等查询命令的能够快速执行

而我们服务器/data/目录每天会产生大量的小文件,导致建立索引的时候占用很高的磁盘IO

服务器每天定时对硬盘上的文件进行索引,简单的说就是建立一个数据库,把所有文件目录信息存放到这个库里面,当使用whereis和locate命令搜索文件时,它直接到这个数据库中读取数据。而不是像find一样在硬盘上找文件。Whereis搜索一个文件几乎只要几秒钟就可以搞定,而find需要花费几分钟或者更长时间。updatedb.Conf使搜索的效率提高了很多倍。但是有缺点,它每天都需要索引更新,这会导致IO负载过高,因为不是时时更新,所以会出现搜索到已经删除的文件,搜不出新添加的文件,平时管理中我们很少用到。如果文件数量多而且更新平凡,我们大可把这个功能关闭

优化方法:

1.停止对/data目录进行建立索引操作

vim /etc/updatedb.conf

找到PRUNEPATHS,在后面添加上你不想让这个updatedb建立索引的目录

2.设置定时更新的工作频率从每天一次降低到每周执行一次,命令如下:

mv /etc/cron.daily/mlocate.cron /etc/cron.weekly/
当然如果你服务器用不着建立索引,也可以直接移除

<think>好的,用户想要总结一篇关于服务器问题排查的文档,涵盖CPU、内存磁盘IO磁盘空间不足的情况。首先,我需要回忆之前提供的引用内容,看看有哪些工具和方法可以整合进来。 用户提到的四个主要方面:CPU、内存、磁盘IO磁盘空间。每个部分都需要详细的分析步骤和对应的工具。比如,引用2里提到了很多工具,比如pidstat、top、vmstat、iostat、df等,这些都需要在相应的部分提到。 对于CPU使用率的情况,首先得确定是用户态还是内核态的问题,或者是进程的问题。top和pidstat是常用的工具,可以查看具体进程的CPU占用。如果发现某个进程占用过,可能需要进一步分析是否代码有死循环或者配置问题。此外,perf和gperftools可以用来做性能剖析,找到热点函数。 内存方面,需要检查是否有内存泄漏或者大量缓存。free和top可以看整体内存使用,而pmap和valgrind可以帮助分析进程的内存分布和泄漏。如果内存占用但CPU低,可能是应用占用了内存未释放,这时候需要检查应用代码或者调整JVM参数。 磁盘IO的话,iostat和pidstat -d可以查看磁盘的读写情况,找到IO的进程。然后结合iotop或者lsof确认具体文件。优化方法可能包括调整调度算法,优化SQL查询,或者使用缓存。 磁盘空间不足的话,df和du是基础工具,找出大文件或目录。有时候可能需要清理日志或临时文件,或者扩容磁盘。lsof也能帮助查看被删除但未释放的文件,比如正在使用的日志文件。 用户可能希望指南结构清晰,步骤明确,每个问题都有对应的命令和解决思路。需要确保引用正确的工具,比如引用3中提到的pidstat -d来查看进程的磁盘IO情况,这个要放在磁盘IO的部分。 另外,用户可能需要示例命令和代码块,比如展示如何使用du查找大文件,或者如何用valgrind检测内存泄漏。不过根据用户之前的示例,可能更倾向于命令示例而不是代码,但需要看情况。 还要注意引用对应的参考资料,比如在内存部分提到内存泄漏工具时引用2中的valgrind和AddressSanitizer,或者在磁盘IO部分引用3中的pidstat用法。 最后,总结部分需要归纳关键工具和流程,方便用户快速查阅。可能还需要考虑添加相关问题,比如如何预防这些问题,或者自动化监控的方法,但用户现在主要需要的是排查指南,所以相关问题可以围绕这四个方面展开,比如如何分析CPU负载的原因,或者内存泄漏的检测步骤等。 需要确保每个步骤逻辑连贯,从整体检查到具体进程分析,再到解决方案,结构清晰。同时,避免使用过于专业的术语,保持易懂,但又要准确。可能还需要注意命令的参数,比如pidstat -d 1 中的1表示间隔时间,需要说明清楚。 现在需要把这些思考整理成结构化的文档,分四个部分,每个部分包括现象、排查步骤、常用命令、解决方案,最后总结工具和流程,并生成相关问题。</think># 服务器性能问题排查指南 ## 一、CPU使用率 ### 现象特征 - 系统响应变慢,`top`命令显示`%Cpu(s)`超过80% - `load average`持续于CPU核心数2倍[^2] ### 排查步骤 1. **快速定位进程** ```bash top -H -p <PID> # 查看线程级CPU占用 pidstat -u 1 # 统计进程CPU使用率(间隔1秒) ``` 2. **分析CPU类型** ```bash perf top # 查看内核/用户态热点函数 mpstat -P ALL 2 # 查看各核CPU使用分布 ``` 3. **常见原因** - 代码死循环/递归调用 - 不合理的线程池配置 - 加密解密等计算密集型操作 ### 解决方案 - Java应用使用`jstack`生成线程栈分析 - C/C++程序使用`gperftools`进行性能剖析[^2] - 优化算法复杂度(如O(n²)→O(nlogn)) --- ## 二、内存占用 ### 排查流程 1. **全局分析** ```bash free -m # 查看内存总量/缓存 sar -r 1 # 监控内存变化趋势 ``` 2. **进程级分析** ```bash pmap -x <PID> # 查看进程内存映射 valgrind --leak-check=full ./program # 检测内存泄漏 ``` 3. **特殊类型内存** ```bash slabtop # 查看内核对象缓存 echo 3 > /proc/sys/vm/drop_caches # 清理PageCache ``` ### 典型案例 - JVM堆内存溢出:调整`-Xmx`参数 - 内存泄漏:通过`AddressSanitizer`检测[^2] - 缓存膨胀:如Redis未设置过期策略 --- ## 三、磁盘IO ### 诊断方法 1. **设备级监控** ```bash iostat -x 1 # 查看await/util%指标 iotop -oP # 实时显示IO进程 ``` 2. **文件级分析** ```bash pidstat -d 1 # 统计进程IO数据[^3] lsof -p <PID> # 查看进程打开的文件 ``` ### 优化策略 | 问题类型 | 解决方案 | |---------|---------| | 大量小文件IO | 合并写入/使用内存缓存 | | SQL慢查询 | 添加索引/优化查询语句 | | 日志频繁刷盘 | 改为异步写入模式 | --- ## 四、磁盘空间不足 ### 快速排查 1. **空间概览** ```bash df -hT --total # 显示文件系统类型及使用率 du -sh /* 2>/dev/null | sort -hr # 查找大目录 ``` 2. **特殊场景处理** ```bash lsof +L1 # 查找被删除未释放的文件 find / -type f -size +100M # 定位大文件 ``` ### 清理策略 1. 日志文件:`logrotate`定期归档 2. 临时文件:清理`/tmp`目录 3. 容器残留:`docker system prune` --- ## 总结:性能分析工具矩阵 | 指标类型 | 核心工具 | 关键参数 | |---------|---------|---------| | CPU | `perf`, `pidstat` | `-u`, `-T` | | 内存 | `pmap`, `valgrind` | `--leak-check` | | 磁盘IO | `iostat`, `blktrace` | `-x`, `-w` | | 存储空间| `ncdu`, `fdisk` | `-h`, `-l` |
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值