【问题标题】:Deadlock scenario in AFTER UPDATE trigger in OracleOracle 中的 AFTER UPDATE 触发器中的死锁场景
【发布时间】:2014-04-25 07:09:06
【问题描述】:

作为审计/历史记录功能的一部分,我想使用 AFTER UPDATE 触发器或任何其他触发器来处理以下场景,请告诉我。 情景——

  1. 将有 2 个表 -- 基表和历史表
  2. 更新基表中的任何记录时,第一次在历史表中插入相同的记录(要更新),并使用旧值。
  3. 使用基表中的新值更新记录。

我正在使用以下触发器,出现死锁情况。请建议解决此问题。

create table Base_table(
SYMBOL_ID            NUMBER(9)  primary key,  
SYMBOL_NAME         VARCHAR2(20) ,
PRICE           NUMBER(9) ,   
VERSION              NUMBER(1)
)
organization index;

create table base_table_hist(
ID               NUMBER(9) primary key,
SYMBOL_ID            NUMBER(9) ,   
SYMBOL_NAME          VARCHAR2(20) ,
PRICE             NUMBER(9),    
VERSION              NUMBER(1) ,
constraint other_symbolid  foreign key(symbol_id) references test_symbol(symbol_id)
)
organization index;
************************************************************

create or replace Trigger Symbol_Ver
AFTER UPDATE ON Base_table
REFERENCING NEW AS New OLD AS Old
FOR EACH ROW

DECLARE
new_version number(5);
--Pragma AUTONOMOUS_TRANSACTION;
Sid number(9);
begin
  if (:New.symbol_id <> :Old.symbol_id)    OR (:New.price <> :Old.price)  then


    new_version:= :Old.version+1;

    --insert into history table
    insert into base_table_hist (id, symbol_id, symbol_name,price,version) 
      values (symbol_seq.nextval, :OLD.symbol_id, :OLD.symbol_name, :OLD.price, :OLD.version);
  commit;
  DBMS_OUTPUT.put_line('new_version..'||new_version);
end if;

if (:New.symbol_id <> :Old.symbol_id)    OR (:New.price <> :Old.price)  then
    update base_table set version=new_version where symbol_id=:Old.symbol_id;
end if;

end;

【问题讨论】:

  • 真的是死锁吗?不是变异表错误,也不是资源不足(因为触发器会从其中的递归更新中重复触发)?
  • 显示死锁错误 (ORA-00060)。为避免重复触发条件更改为 (if :New.version = Old.Version) 然后 INSERT 和 UPDATE 操作。
  • 但是你仍然在触发一个额外的更新,这很丑陋,我很惊讶工作。为什么不在before 触发器中调整版本(设置:new.version)并创建历史记录,而不是做额外的DML?

标签: oracle triggers deadlock


【解决方案1】:

一个自治事务创建一个新的、独立的事务。所以你用两个事务更新同一行,导致死锁。

这里不需要自动触发器。事实上,您不想在触发器中使用 DML 来触及基表。这总是有问题的。

幸运的是,在这里您可以使用常规的BEFORE 触发器(因为您正在更新字段):

CREATE OR REPLACE TRIGGER Symbol_Ver
   BEFORE UPDATE ON Base_table
   FOR EACH ROW
BEGIN
   IF (:New.symbol_id <> :Old.symbol_id) OR (:New.price <> :Old.price) THEN

      -- this will change the value in the row being updated
      :new.version := :Old.version + 1;

      --insert into history table
      INSERT INTO base_table_hist
         (id, symbol_id, symbol_name, price, version)
      VALUES
         (symbol_seq.nextval, :OLD.symbol_id, 
          :OLD.symbol_name, :OLD.price, :OLD.version);
      -- COMMIT <-- don't commit in a trigger!
      DBMS_OUTPUT.put_line('new_version..' || new_version);
   END IF;
END;

对基表的额外更新既是多余的又是有问题的,因为这会导致循环无限递归。

您也不能在触发器中提交。无论如何,您都不想提交,这会破坏事务逻辑。 不提交允许主事务在一个不错的原子块中回滚历史表和主表。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2023-03-18
    • 1970-01-01
    • 1970-01-01
    • 2013-03-29
    • 1970-01-01
    • 1970-01-01
    • 2022-01-05
    相关资源
    最近更新 更多