如何检查Application送往Oracle DB的Query

来源:互联网 发布:投屏软件 编辑:程序博客网 时间:2024/05/17 10:28

昨天,有一个Bug耗掉了我近一天的时间,写这篇学习笔记不是说那个Bug的,而是在解决的过程中,从shenglin那里(shenglin是COC的Senior Oracle DBA)学到了一些基本命令,命令都不复杂,不过我觉得对我挺有用的,至少以后我再碰到类似的问题,至少可以自己先试试而不是老要去麻烦DBA。

现象:
应用程序往数据库送了一跳insert语句。应用程序log文件中记录了这条SQL执行成功了:
insert into tableX(field1,field2,…fieldn) values(:val1,:val2,…:valn); tableX的主键是field1。我们知道val1 = XX。数据库那边没有报任何错误,可是select field1 from tableX where field1=XX 却返回空!

Trouble Shooting Steps:

1. 怀疑这条SQL是不是没有送到目标数据库

-bash-3.00$ hostname
arrowpig0.dev.ebay.com   –>我的应用程序运行的机器名

然后我在另外一台dev机器上登录Oracle DB:
-bash-3.00$ sqlplus arrowpig/arrowpig@arrowpig  –>关于登录Oracle数据库,参见安装Oracle 10g Express
SQL> set linesize 132 pagesize 10000
SQL> select sid,sql_hash_value,prev_hash_value,machine from v$session where machine like ‘%arrowpig0%’;

SQL> /        -->当应用程序执行时,使用 / 反复执行查询语句
no rows selected
SQL> /
       SID SQL_HASH_VALUE PREV_HASH_VALUE MACHINE
———- ————– ————— —————————————————————-
        27     686872531       686872531 arrowpig0.dev.ebay.com

SQL> select sql_text from v$sqltext where hash_value=686872531 order by piece;
SQL_TEXT
—————————————————————-
insert into tableX (field1,field2,…fieldn) values(:val1,:val2,…:valn) 

可以确定,SQL确实送到DB Server了。

2. 怀疑是不是没有Submit

SQL> select a.value ,b.name from v$sesstat a,v$statname b where a.STATISTIC#=b.STATISTIC# and b.name like ‘user commits’ and a.sid=27;
     VALUE NAME
———- —————————————————————-
        32 user commits

SQL> /
     VALUE NAME
———- —————————————————————-
        34 user commits

我们看到Value在增加,说明Commit成功了。

3. 怀疑是不是数据有错,手工输入SQL

于是我们想是不是有什么数据错。
数据库端shenglin做了下面的操作(下面的操作需要sys权限):
drop trigger error_log_trigger ;
drop table sys.error_log ;

create table sys.error_log (
error_date date,
error_no number,
machine varchar2(64),
sqltext varchar2(4000)
) tablespace &tablespace_name;

create trigger error_log_trigger AFTER SERVERERROR ON DATABASE
declare
n number;
stmt varchar2(4000);
sql_text ora_name_list_t;
BEGIN
n := ora_sql_txt(sql_text);   –ora_sql_text得到当前正在执行的DDL语句
FOR i IN 1..n LOOP  –将sql_text的内容拼装成完成的一个字符串,存在stmt中
     if(length(stmt||sql_text(i))> 4000) then
         stmt := substr(stmt||sql_text(i), 1, 4000);
        exit;
     end if;
    stmt := stmt || sql_text(i);  — ||用于字符串连接
END LOOP;
insert into sys.error_log (error_date,error_no,machine,sqltext)
values (sysdate,server_error(1),sys_context(‘userenv’,'host’),stmt);
END;
/

SQL> select * from sys.error_log where machine like ‘%arrowpig0%’;

–记得做完实验以后把上面创建的表和触发器drop掉。

应用程序端:
注意到我们送往DB的SQL都采用了绑定变量的方式,所以在现有的log中看不见具体的数据值。于是我在配置文件中加了一个选项DumpBindVariables,然后再修改一个源代码使得当DumpBindVariables=Enabled的时候打印所有绑定变量的值。

确定问题的Root Cause:

打印绑定变量以后,我们发现实际送往数据库的SQL中主键field1的值被绑定了数值YY,它应该是XX的。怪不得应用程序和数据库都没有报错,因为我们确实成功了插入了一条field1=YY的数据,但是我们select * from tableX where field1=XX当然就返回空了。我又花了不少时间,终于找到了出错的代码,这里我模拟一下大概的出错代码:
class record{
int32 meta_data;
int64 field1;

};

很不巧,给meta_data赋值的语句是这样的:
int32 tmp_meta_data=…;
memcpy(&record.meta_data, &tmp_meta_data,sizeof(int64));

完了,因为size错误,给meta_data赋值的时候覆盖了后面field1的前4个字节,导致field1的值被篡改。在茫茫代码中定位这样一个低级的错误很费时间。

我一直想知道有没有这样的断点,当程序试图修改某个pointer指向的内存时可以让程序停下来,并打印当前的调用堆栈,不知道GDB支持不支持这样的功能?

 

今天又碰到一个问题,发现我的SQL送到DB 以后Hold在那里:
SQL> select sid,sql_hash_value,lockwait,machine from v$session where machine like ‘%arrowpig0%’;SID SQL_HASH_VALUE LOCKWAIT MACHINE———- ————– —————- —————————————————————-5362 3075024559 0000000722B78180 arrowpig0.dev.ebay.com5487 834846608                             arrowpig0.dev.ebay.com9154 0                                          arrowpig0.dev.ebay.com
 
想来想去想不通为什么会又把锁在那里,后来 DBA帮我看了一下,原因是处了应用程序,我还开了一个sqlplus,为了干净,我在启动应用程序之前执行了一把:SQL> delete from tableX;
 
但是我没有执行commit,所以这些被delete的记录都被加了锁。
 
检查加锁的SQL是:
SQL> select * from v$locked_object where object_id in ( select object_id from dba_objects where object_name=’xTable’);