【问题标题】:Capture insert error in Teradata捕获 Teradata 中的插入错误
【发布时间】:2017-03-01 18:25:34
【问题描述】:

我有一个存储过程,它使用以下语句将记录插入到我们的 Teradata 数据仓库中的表中:

INSERT INTO UAT_AUDIT_VIEWS.AUDIT_BATCH(
BATCH_KEY
,AUDIT_STATUS_KEY
,BATCH_START_DATETIME
,BATCH_END_DATETIME
,BATCH_OWNER
,BATCH_EXECUTION_START_DATETIME
,BATCH_EXECUTION_END_DATETIME
)
VALUES(
(SELECT COALESCE(MAX(BATCH_KEY),0)+1 FROM UAT_AUDIT_VIEWS.AUDIT_BATCH)
,5 --PENDING
,'1900-01-01 00:00:00'
,'2999-12-31 00:00:00'
,:P_BATCH_OWNER
,CURRENT_TIMESTAMP
,'2999-12-31 00:00:00'
);

请注意,我对主键 BATCH_KEY 有唯一性约束。在某些情况下,我的插入语句失败,因为表中已经存在主键。当它发生时,我希望我的存储过程循环并重试插入,直到它成功。

我使用以下方法尝试了多种解决方案,但均未成功:

  • DECLARE CONTINUE HANDLER FOR SQLEXCEPTION 避免插入失败时存储过程失败
  • WHILE DO 循环重试插入

您能否描述一下您将如何处理这种情况? 这是我为测试它而构建的测试存储过程的简化版本(不起作用):

REPLACE PROCEDURE DEV_AUDIT_NEW.ARO_TEST_INSERT()
BEGIN
      DECLARE V_BATCH_KEY_CREATED VARCHAR(100);
      DECLARE V_COUNTER SMALLINT DEFAULT 1;
      DECLARE CONTINUE HANDLER FOR SQLEXCEPTION

      SET V_BATCH_KEY_CREATED = NULL;
      WHILE V_BATCH_KEY_CREATED IS NULL
      DO
            INSERT INTO DEV_AUDIT_NEW.AUDIT_BATCH_TEST_LOG(LOG_DESC) VALUES(V_BATCH_KEY_CREATED);

            INSERT INTO DEV_AUDIT_NEW.AUDIT_BATCH_TEST(BATCH_KEY,BATCH_OWNER) VALUES(V_COUNTER,'B');
            SELECT BATCH_KEY
            INTO :V_BATCH_KEY_CREATED
            FROM DEV_AUDIT_NEW.AUDIT_BATCH_TEST 
            WHERE BATCH_KEY=V_COUNTER AND BATCH_OWNER='B';
            SET V_COUNTER=V_COUNTER+1;

            INSERT INTO DEV_AUDIT_NEW.AUDIT_BATCH_TEST_LOG(LOG_DESC) VALUES(V_BATCH_KEY_CREATED);
      END WHILE;
END;

【问题讨论】:

  • 只有在并行运行 SP 时,密钥才能存在。您应该简单地切换到 WRITE 锁,而不是处理并发问题:LOCK TABLE UAT_AUDIT_VIEWS.AUDIT_BATCH FOR WRITE INSERT ...
  • 我确实有多个团队并行调用存储过程。我们实现了一个 WRITE LOCK 作为第一个解决方案,但是当存在并发访问时它最终导致了死锁问题。这就是为什么我想删除它,捕获插入错误并重试。如果您有其他选择,我会感兴趣吗?
  • 当与表级别的 WRITE 锁并行运行时,您发布的插入不会导致死锁。另一种解决方案(扩展性更好)基于一个序列表,该表的单行存储最大值,您的 SP 读取当前值,将其加一并将其写回。
  • 感谢您的评论。我仍然认为 WRITE 锁是死锁问题的原因,因为我们能够重现该问题并将其删除,我们注意到该问题不再发生(但我们有 BATCH_KEY 唯一性约束错误)。我不明白为什么将 Max(BATCH_KEY) 存储在专用表中可以解决这两个问题中的任何一个?

标签: loops error-handling insert teradata


【解决方案1】:

评论太长了。

您当前的方法必须执行全表扫描才能获得 MAX(默认为表级别的读锁),但插入默认为行哈希级别的写锁。当您请求在表级别写入时,它应该防止死锁。

当您定义一种序列表时,每个访问都是 UPI 访问,例如

CREATE SET TABLE Sequences
 (
   SequenceName VARCHAR(128) CHARACTER SET Unicode NOT CaseSpecific NOT NULL,
   nextVal BIGINT NOT NULL DEFAULT 1
 )
UNIQUE PRIMARY INDEX ( SequenceName )
;

REPLACE PROCEDURE NextVal (IN SequenceName VARCHAR(128) CHARACTER SET Unicode, OUT NextVal BIGINT)
BEGIN
   BEGIN REQUEST
      LOCK ROW WRITE
      SELECT nextVal INTO :nextVal FROM Sequences 
      WHERE SequenceName = :SequenceName;

      UPDATE Sequences SET nextVal = nextVal + 1 
      WHERE SequenceName = :SequenceName;
   END REQUEST; 
END;

初始化一个新序列:

INSERT INTO sequences ('mytable', 1);

获取下一个值:

CALL nextVal('mytable', nextval);`

编辑:

您可以使用 BTEQ 并行记录多个会话轻松地对其进行测试,例如在 10 个会话中运行 1000 个 CALL:

.set session 10; 
.logon ...; 
select * from sequences where SequenceName = 'mytable';

.repeat 1000
CALL nextVal('mytable', nextval); 

select * from sequences where SequenceName = 'mytable';

观察按顺序返回的值,没有死锁:-)

【讨论】:

  • 谢谢,我想我在这里达到了我的技术极限...你知道如果两个进程同时调用 SP NextVal 会发生什么吗?这个实现是否保证它不会为这两个进程返回相同的 nextVal 并且它们都不会面临死锁?我试试看……
  • @Alexis.Rolland:编辑了我的答案,展示了如何测试它
猜你喜欢
  • 1970-01-01
  • 2021-01-19
  • 2012-05-22
  • 2021-11-23
  • 2013-11-23
  • 1970-01-01
  • 2020-12-04
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多