现象:
程序启动后正常运行,使用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
……(此处我省略了…)
可以发现:
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的文章。