数据库触发器和线程安全

时间:2016-08-29 19:09:44

标签: oracle triggers thread-safety

我遇到的情况是我正在为数据库使用Oracle物化视图。我正在执行基于计划的物化视图刷新,以获取源数据库中发生的所有更改。

为了识别发生的变化,我在物化视图上使用触发器。因此,当我刷新视图时,视图将发生新的插入/更新。这些添加/更新/删除触发器将在日志表中插入/更新相同的记录。 示例 - DB中的Employee和相关表。在单独的位置,Employee_view和相关视图。在DB中添加,更新或删除Employee时,刷新时,Employee_view将更新。将为Employee_view执行触发器。对于一个emplyee ID 1,一行插入LOG表。现在因为多个表正在更新,它们都会在员工ID 1的日志表中触发插入(如果不存在)和更新(如果存在)。

我应该担心线程安全情况。例如。 Empoyee 1名称已更改且年龄发生变化。 Bothare不同的表。两者都将在日志表中插入新记录,因为它们是并行的。

1 个答案:

答案 0 :(得分:0)

我重新考虑您的设计,因为您所描述的内容明确反对Oracle对创建触发器的文档限制:

  

注意:如果在物化视图的基表上创建触发器,   那么你必须确保在刷新期间不触发触发器   物化视图。在刷新期间,DBMS_MVIEW过程   I_AM_A_REFRESH返回TRUE。

请参阅:https://docs.oracle.com/database/121/LNPLS/create_trigger.htm#LNPLS01374

替代方法

以下是评论中提到的替代想法的充实版本。我不知道是否支持针对物化视图更新日志的直接DELETE /一个好主意。但它适用于12c。

-- Create a base table (you'd already have these...)
CREATE TABLE matt_base (id number not null PRIMARY KEY, d VARCHAR2(30));

-- Create materialized view logs (you should already have these if you're
-- using FAST refresh, which you must be or else your question makes no 
-- sense...)
CREATE MATERIALIZED VIEW LOG ON matt_base 
  WITH PRIMARY KEY (d) INCLUDING NEW VALUES 
  FOR FAST REFRESH;

-- Create the materialized view (you have this -- this is the MV you wanted
-- to create a trigger on)
-- The key addition here is the REPLICATED_FLAG column.
CREATE MATERIALIZED VIEW matt_mv USING INDEX REFRESH FAST ON DEMAND 
  WITH PRIMARY KEY FOR UPDATE AS 
  SELECT id, d, 'N' replicated_flag FROM matt_base;

-- Now for the process.  Put some data into the table and refresh MV.    
insert into matt_base values (1, 'A');
insert into matt_base values (2, 'B');
BEGIN  DBMS_MVIEW.refresh (list => 'MATT_MV', method => 'F'); END;

-- To replicate data, select everything from the MV that has not been
-- replicated
SELECT * FROM matt_mv WHERE replicated_flag = 'N'; 
-- Mark the rows as replicated.  (This is sloppy... in real life you'd want
-- to ensure you update the same set of rows that you replicated).
UPDATE matt_mv SET replicated_flag = 'Y';
-- Now, here's the weird part.  Oracle made a note of the rows affected
-- in the MV by the above update.  We have to make Oracle forget about
-- them!  You can get the update log's name (USLOG$_MATT_MV, below) by
-- querying DBA_SNAPSHOTS.
-- CAVEAT: I don't know if this is safe / a good idea!!! Ask Oracle Support.
DELETE FROM uslog$_matt_mv;
COMMIT;

-- Now do some updates on the base table to simulate additional activity to 
-- replicate.
update matt_base set d = 'A_NEW' where id = 1;
insert into matt_base values (3, 'C');
COMMIT;

-- Now refresh your materialized view again to pick up the new activity
BEGIN  DBMS_MVIEW.refresh (list => 'MATT_MV', method => 'F'); END;

-- Look at your materialized view.
-- You'll see that only the recently changed rows are flagged as
-- not replicated.  ID = 2 should be flagged as replicated because
-- it was not changed.
SELECT * FROM matt_mv;