现在的位置: 首页 > 综合 > 正文

如何判断程序处于I/O等待

2013年04月30日 ⁄ 综合 ⁄ 共 2606字 ⁄ 字号 评论关闭

 

现象:

程序启动后正常运行,使用prstat -p <PID>看到内存在逐步增加。使用tail -f
<logfile>看到不停的有log输出。突然在log打印一条SQL被送往DB Server以后,程序似乎停下来了。没有报错,没有core
dump。

 

 

诊断:

程序PID=6208。要求MIT做pfile,pstack,pflags。XuXu帮助我将现场保存到/var/tmp下

 

-rw-r–r–   1 root     other       1788 Jul 18 02:31 /var/tmp/pfiles.6208

-rw-r–r–   1 root     other        668 Jul 18 02:31 /var/tmp/pflags.6208

-rw-r–r–   1 root     other        759 Jul 18 02:31 /var/tmp/prstat.6208

-rw-r–r–   1 root     other       4222 Jul 18 02:30 /var/tmp/pstack.6208

 

PD虽然没有root权限,但是prstat -p还是可以用的

 

bash-2.03$ cat prstat.6208
  PID USERNAME  SIZE   RSS STATE  PRI NICE 
TIME    CPU  PROCESS/LWPID
  6208 user        633M  614M sleep   59  -20  
0:00.00 0.0% scrubber/5
  6208 user        633M  614M sleep   59  -20  
0:00.00 0.0% scrubber/10
  ……(此处我省略了…)

可以发现:

  1. 内存Size和驻留内存RSS稳定在一个固定的值上面,没有变化–>程序没有在进行内存分配和释放
  2. 程序状态一直处于Sleep,CPU=0.0%,程序似乎什么事都不在干
  3. 程序并没有退出,没有类似SEG FAULT– Exit之类的信息显示–>程序正在正常运行。

 

bash-2.03$ cat pflags.6208
6208:   …(此处省略)
  /1:   flags =
PR_PCINVAL|PR_ASLEEP [ accept(0x5,0xfd109a28,0xfd109a20,0x1) ]
  /2:   flags
= PR_PCINVAL|PR_ASLWP|PR_ASLEEP [ signotifywait() ]
  /3:   flags =
PR_PCINVAL|PR_ASLEEP [ read(0xd,0x22738716,0x2010) ]


  /5:   flags =
PR_PCINVAL|PR_ASLEEP [ lwp_sema_wait(0xfd20be60) ]
  /8:   flags =
PR_PCINVAL|PR_ASLEEP [ lwp_cond_wait(0xfe1d34f0,0xfe1d3500,0xfe1ccd88) ]
 
/9:   flags = PR_PCINVAL|PR_ASLEEP [ lwp_cond_wait(0xfe1ccd30,0xfe1ccd18,0x0)
]
  sigmask = 0xffbffeff,0x00001fff
  /10:  flags = PR_PCINVAL|PR_ASLEEP [
door(0x0,0x0,0x0,0x0,0xfc60bd38,0x4) ]

 

所有线程处于Sleep状态,注意到read操作正在I/O等待。LWP号码是3,于是查看pstack找出对应的thread call stack:

 

—————–  lwp# 3 / thread# 1  ——————–
fe29ecc0 read     (d, 22738716,
2010)

 

(具体的调用栈我就不在这里贴出来了,但是这里的call
stack已经能够帮助PD方便的定位程序源代码了,可以用gc++filt进行unmangle便于阅读), 于是基本肯定是在DB
Read的时候发生I/O等待。这一点也和我们从log中看到的信息符合。XuXu接着帮我确认了这一点。

 

在进行Oracle DB链接的时候,应用程序需要知道链接信息,其中包括Oracle DB的VIP Name和Port。于是我们查看pfiles.
假设端口号是2000。数据库服务器的名字是arrowpigDB.vip.com

 

bash-2.03$ cat pfiles.6208 | grep 2000

        peername: AF_INET 11.11.11.11  port: 2000 
(此处的IP和端口是我杜撰的,但是不影响理解)
        peername: AF_INET 11.12.12.12  port: 2000

 

bash-2.03$ /usr/sbin/nslookup 11.12.12.12
Server:   
–>此处显示dns服务器的名字
Address:  –>此处显示dns服务器的IP

Name:     arrowpigDB.vip.com  –>说明应用程序到DB的链接已经正确建立,I/O等待发生在DB
Server一侧。由此可确定是SQL执行速度太慢。

Address:  11.12.12.12

 

 

解决:

该程序确实使用了一条新的SQL,虽然我觉得SQL的改动很小,只是改动了where:原来的where是:

last_modified between :start_time and :end_time order by last_modified

现在改为:

last_modified=:start_time and item_id>:last_id order by item_id

 

Oracle可以方便的找到应用程序发往DB的SQL. 然后查看执行计划:

 

SQL> explain plan for

select ……. <此处我省略具体的SQL>

Explained.
SQL> @?/rdbms/admin/utlxpls

PLAN_TABLE_OUTPUT

<此处我省略具体的执行计划>

初步结果是在做DB Join的时候,有一张130GB的表,原来的访问是使用Table Range Scan,而新的SQL却使用Table Access
Full。于是我发了一个DB Review Ticket让DBA帮我看怎么解决,是不是需要添加Hint,是不是需要代码改动。等这个Ticket
resolve以后,我争取能够理解DBA的Tuning技术,然后补一下关于这个SQL Tuning的文章。

 

 

抱歉!评论已关闭.