记一次Oracle数据故障排除过程

前天在Oracle生产环境中,自己的存储过程运行时间超过1小时,怀疑是其他job运行时间过长推迟了自己job运行时间,遂重新跑job,发现同测试环境的确不同,运行了25分钟。

之后准备在测试环境中制造同数量级的数据进行分析,写了大概如下的存储过程,

create or replace PROCEDURE PERFORMANCE_TEST AS 

v_date date;
v_start_date date;
v_end_date date;
v_start_date_str varchar2(10) := '2017-01-31';
v_end_date_str varchar2(10) := '2017-07-31';
v_date_str varchar2(10);

BEGIN
  v_start_date := to_date(v_start_date_str, 'yyyy-mm-dd');
  v_end_date := to_date(v_end_date_str, 'yyyy-mm-dd');
  v_date := v_start_date;
  while v_date < v_end_date loop
    v_date_str := to_char(v_date, 'yyyy-mm-dd');
    insert into datacore.df_customer_static_report
    (
      data_date,
      cty_code,
      party_id,
      party_name,
      ho_domicile_cty,
      rm_code,
      rm_name,
      business_division
    )(
      select 
        v_date_str,
        cty_code,
        party_id,
        party_name,
        ho_domicile_cty,
        rm_code,
        rm_name,
        business_division
      from datacore.df_customer_static_report
      where data_date = v_end_date_str
    );
    commit;
  end loop;
  
END PERFORMANCE_TEST;

犯了个致命错误,丢了v_date := v_date + 1; 存储过程陷入无限循环!在过了1个多小时后,意识到不对劲,遂查询了数据量,发现2017-01-31的数据量竟然达到了千万级。。。赶紧停止运行找原因,才发现无限循环插入数据。

剩下就是怎么删掉这些数据,毕竟千万级的数据占据存储空间太大了。简单的删除肯定不起作用,遂尝试分批删除,先试着删除1w条,结果运行很长时间后还是没有结束。这个时候,感觉之前那个无限循环应该还没有结束,在后台还在运行。因为其他事情捣乱,没来得及修正这个问题。第二天来,再次查询,发现数据量达到了快5千万条,欲哭无泪啊!赶紧删数据,分批次,1百万条的删,(这次加上累加条件了)

create or replace PROCEDURE DELETE_TEMP AS 

v_number number := 1;
v_number_end number := 50;

BEGIN

  while v_number <= v_number_end loop
    delete from datacore.df_customer_static_report nologging
    where data_date = '2017-01-31'
    and rownum < 1000000;
    commit;
    v_number := v_number + 1;
    dbms_output.put_line(v_number || ' end');
  end loop;
  
END DELETE_TEMP;

本想用TRUNCATE把所有数据都删掉,但是我这里只需要删掉表中‘2017-01-31’的数据,而且只是把千万条降低到万条。查了delete语句的优化,发现加上nologging会更快些(数据不做恢复)。

运行了大概1个小时后,感觉差不多了,遂手动终止了delete的运行。再次查询,‘2017-01-31’的数据降到9千多条。窃喜~

不过又想起昨天想到的“是否无限循环还在后台运行”?过了10几分钟后查询,发现数据又多了,欲哭无泪。。

怎么让这个讨厌的无限循环终止呢?因为使用的账号没有dba权限,所以想通过更改表的结构,让包含无限循环的存储过程异常终止。但估计不可行,因为数据库表一直被占用了。抱着试一试的想法,执行以下sql,

alter table datacore.df_customer_static_report
drop column rds_spread_code

报错“资源正忙, 但指定以 NOWAIT 方式获取资源, 或者超时失效;resource busy and acquire with NOWAIT specified”。

搜索后,果真有解决方案 - here,但还是需要dba权限(厚脸皮要吧)。

1. 用dba权限的用户查看数据库都有哪些锁

SELECT T2.USERNAME,T2.SID,T2.SERIAL#,T2.LOGON_TIME
FROM V$LOCKED_OBJECT T1,V$SESSION T2
WHERE T1.SESSION_ID=T2.SID ORDER BY T2.LOGON_TIME;

2. 根据sid查看具体的sql语句,如果sql不重要,可以kill

SELECT SQL_TEXT FROM V$SESSION A,V$SQLTEXT_WITH_NEWLINES B
WHERE DECODE(A.SQL_HASH_VALUE, 0, PREV_HASH_VALUE, SQL_HASH_VALUE)=B.HASH_VALUE
AND A.SID=&SID ORDER BY PIECE;

3. kill该事务

ALTER SYSTEM KILL SESSION '590,20839';

4. 再次查看数据库锁,发现锁消失。再次查询表数据,不再增加。

教训:以后写存储过程中的循环,千万注意条件的累加!

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

-- index(索引)
select * from all_indexes;
select * from user_indexes;

select * from all_ind_columns;
select * from user_ind_columns;

select t.*,i.index_type
from user_ind_columns t,user_indexes i
where t.index_name = i.index_name
and t.table_name = i.table_name and t.table_name = 'DM_RR_GQ_FIN_FEDS';

select t.*,i.index_type
from all_ind_columns t,all_indexes i
where t.index_name = i.index_name
and t.table_name = i.table_name
and owner = 'FISP'
and t.table_name = 'FIS_OUT_FLEXI';

-- tables(表)
select * from user_tab_columns;
select * from all_tab_columns;

select * from user_col_comments;
select * from all_col_comments;

select * from user_tables;
select * from all_tables;

原文地址:https://www.cnblogs.com/hello-yz/p/9923057.html