程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 數據庫知識 >> Oracle數據庫 >> Oracle教程 >> 被Oracle觸發器給坑了

被Oracle觸發器給坑了

編輯:Oracle教程

被Oracle觸發器給坑了


例行檢查數據庫AWR報告,有一條update語句執行多次,每次執行時間30多秒,這條SQL語句很簡單,就是根據主鍵條件修改數據,主鍵個數是1到100之間。這個問題由來已久,只是偶爾出現。主鍵是varchar2,類似序列,由於之前有遷移過數據,特別在主鍵上為遷移的這部分數據加過標記,用肉眼看主鍵的分布是不均勻的。

第一次診斷:這個表有150萬的數據,執行慢是因為update的時候沒走到主鍵索引,於是去看了下直方圖的分布,只有兩個桶,於是重新收集了主鍵的直方圖信息,有250個桶了。准備觀察一天,第二天再看AWR,發現反而越來越慢了。

第二次診斷:聽開發人員說此表上有觸發器,測試發現果然是觸發器的問題,觸發器消耗的資源統統記在update語句上,讓人感到莫名其妙。修改方法是將觸發器的業務通過SQL實現,整個功能快了不少。下面對問題進行抽象、實驗:

1.初始化數據及建立觸發器

drop table test1 purge;

drop table test2 purge;
create table test1 as select * from dba_objects;
insert into test1 select * from dba_objects;
commit;
create table test2 as select * from dba_objects;
create index ind_t1_object_id on test1(object_id) nologging;
create index ind_t2_object_id on test2(object_id) nologging;
exec dbms_stats.gather_table_stats(user,'test1',cascade => true);

exec dbms_stats.gather_table_stats(user,'test2',cascade => true);

CREATE OR REPLACE TRIGGER t_trigger
BEFORE update ON test1
FOR EACH ROW
BEGIN
update test2 t
set t.object_name = :old.object_name
where t.object_id = :old.object_id;
END;

SQL> set autotrace traceonly
SQL> set timing on

2.執行update語句會觸發觸發器
SQL> update test1 set object_name=''||object_name;
已更新140300行。
已用時間: 00: 00: 15.21
執行計劃
----------------------------------------------------------
Plan hash value: 160929213
----------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
----------------------------------------------------------------------------
| 0 | UPDATE STATEMENT | | 140K| 4110K| 384 (1)| 00:00:06 |
| 1 | UPDATE | TEST1 | | | | |
| 2 | TABLE ACCESS FULL| TEST1 | 140K| 4110K| 384 (1)| 00:00:06 |
----------------------------------------------------------------------------
統計信息
----------------------------------------------------------
140739 recursive calls
427013 db block gets
282079 consistent gets

0 physical reads
120365752 redo size
718 bytes sent via SQL*Net to client
498 bytes received via SQL*Net from client
4 SQL*Net roundtrips to/from client
3 sorts (memory)
0 sorts (disk)
140300 rows processed
SQL> commit;
提交完成。


3.disable觸發器
SQL> alter trigger t_trigger disable;

4.執行update語句不會觸發觸發器
SQL> update test1 set object_name=''||object_name;
已更新140300行。
已用時間: 00: 00: 01.67
執行計劃
----------------------------------------------------------
Plan hash value: 160929213
----------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
----------------------------------------------------------------------------
| 0 | UPDATE STATEMENT | | 140K| 3425K| 384 (1)| 00:00:06 |
| 1 | UPDATE | TEST1 | | | | |
| 2 | TABLE ACCESS FULL| TEST1 | 140K| 3425K| 384 (1)| 00:00:06 |
----------------------------------------------------------------------------
統計信息
----------------------------------------------------------
389 recursive calls
144840 db block gets
2216 consistent gets

0 physical reads
50003740 redo size
721 bytes sent via SQL*Net to client
498 bytes received via SQL*Net from client
4 SQL*Net roundtrips to/from client
8 sorts (memory)
0 sorts (disk)
140300 rows processed

總結:通過兩次實驗可以看到資源消耗差別非常大,觸發器消耗的資源都算在update上。觸發器是每行觸發,如果要高效,處理得有批量的思想。本次問題的解決,如果不是開發人員告訴我有觸發器,這個問題真的很難找出來。

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved