Firebird触发器死锁

时间:2011-10-16 16:34:26

标签: sql triggers firebird database-deadlocks

我对数据库的经验很少,并且遇到了导致firebird 2.5数据库死锁的触发器问题。

数据库中有两个表。当在ITEMS表中添加或删除项目时,触发器会更新STATS.ITEMCOUNT& STATS.SIZE。总共有4个触发器2增量&两个减少。

统计信息表有一行,用于跟踪数据库中的内容。我这样做是错误的吗?如果不是,那就有工作。

在启动应用程序的前几分钟内发生死锁。

UPDATE1:发布所有触发器。

UPDATE2:发布了ExecuteNonQuery方法

UPDATE3:即使使用 pilcrow 友好建议的视图方法,仍会发生死锁。事实上,我甚至尝试过使用一个再次陷入僵局的存储过程。在事务中包装select语句也失败了,因为Firebird Ado提供程序不支持并行事务。

public void ExecuteNonQuery(string NonQuery)
        {
            try
            {
                FbCommand FBC = new FbCommand(NonQuery, DBConnection);
                FBC.ExecuteNonQuery();
                FBC.Dispose();
            }
            catch (FbException e)
            {
                Log.FatalException("Database NonQuery Error", e);
            }

        }

        }

数据库

** Tables **

CREATE TABLE ITEMS (
    ID              ID NOT NULL /* ID = VARCHAR(36) NOT NULL */,
    EXPIRYTIME      EXPIRYTIME NOT NULL /* EXPIRYTIME = BIGINT NOT NULL */,
    ITEMSIZE        ITEMSIZE /* ITEMSIZE = BIGINT NOT NULL */,
    ACCESSCOUNT     ACCESSCOUNT DEFAULT 1 NOT NULL /* ACCESSCOUNT = INTEGER DEFAULT 1 NOT NULL */,
    LASTACCESSTIME  LASTACCESSTIME /* LASTACCESSTIME = TIMESTAMP NOT NULL */
);


CREATE TABLE STATS (
    INSTANCE            SMALLINT,
    SIZE                BIGINT DEFAULT 0,
    ITEMCOUNT  BIGINT DEFAULT 0,
    HITS       BIGINT DEFAULT 0,
    MISSES     BIGINT DEFAULT 0
);

**触发**

   /* Trigger: TRG_INCREMENT_ITEMCOUNT_STATS */
CREATE OR ALTER TRIGGER TRG_INCREMENT_ITEMCOUNT_STATS FOR ITEMS
ACTIVE AFTER INSERT POSITION 1
AS
begin
 UPDATE STATS SET ITEMCOUNT = ITEMCOUNT + 1 WHERE INSTANCE = '0';
end

/* Trigger: TRG_DECREMENT_ITEMCOUNT_STATS */
CREATE OR ALTER TRIGGER TRG_DECREMENT_ITEMCOUNT_STATS FOR ITEMS
ACTIVE AFTER DELETE POSITION 2
AS
begin
UPDATE STATS SET ITEMCOUNT = ITEMCOUNT - 1 WHERE INSTANCE = '0';
end

/* Trigger: TRG_INCREMENT_HITS_STATS */
CREATE OR ALTER TRIGGER TRG_INCREMENT_HITS_STATS FOR ITEMS
ACTIVE AFTER UPDATE POSITION 3
AS
begin
UPDATE STATS SET HITS = HITS + 1 WHERE INSTANCE = '0';
end

/* Trigger: TRG_INCREMENT_SIZE_STATS */
CREATE OR ALTER TRIGGER TRG_INCREMENT_SIZE_STATS FOR ITEMS
ACTIVE AFTER INSERT POSITION 4
AS
BEGIN
  UPDATE STATS SET SIZE = SIZE + NEW.ITEMSIZE WHERE INSTANCE = 0;
END

/* Trigger: TRG_DECREMENT_CACHESIZE_STATS */
CREATE OR ALTER TRIGGER TRG_DECREMENT_CACHESIZE_STATS FOR ITEMS
ACTIVE AFTER DELETE POSITION 5
AS
BEGIN
  UPDATE STATS SET SIZE = SIZE - OLD.ITEMSIZE WHERE INSTANCE = 0;
END

4 个答案:

答案 0 :(得分:1)

发生死锁是因为两个线程同时尝试更新同一行。在这种情况下,最简单的解决方案是使用单个事务和关键部分来防止同时更新。它需要几行代码。

其他方式需要重新设计一个包含全部信息的表格。

答案 1 :(得分:0)

你写道:

  

......有工作回合。

至少还有一种方法。您可以在VIEW中按需计算它们,而不是使用触发器预先计算聚合的计数和大小:

CREATE VIEW stats (instance, size, itemcount, hits, misses) AS
   SELECT CAST(0 AS SMALLINT),
          CAST(COALESCE(SUM(items.itemsize), 0) AS BIGINT),
          CAST(COUNT('x') AS BIGINT),
          CAST(COALESCE(SUM(items.accesscount), 0) AS BIGINT), -- just guessing here
          0                                                    -- but see below
     FROM items;

(注意:我猜你的HITS是ITEMS.ACCESSCOUNT的总和,正如UPDATE触发器和列名所示。你现在没有告诉我们你是如何录制MISSES的,但是,如果应用程序是直接的目前递增STATS.MISSES,你可以为此目的引入一个 new 表,然后将该表加入上面的VIEW中。)

当然,您仍需要适当地提交事务。上面建议的STATS视图与它可以看到的已完成的已提交事务一样准确。

答案 2 :(得分:0)

这些问题通常可以通过使用+1或-1的每次更改记录更容易解决,并且偶尔(每天,每周)处理整个表以汇总所有内容并且只有一个记录。接下来的更改将再次为+1或-1记录,并查询总和。

所以你会有类似的东西:

ITEM  COUNT
item1 10
item2 10
item1 1
item2 -1
item2 -1
item1 -1

在计划合并之后,您将获得:

ITEM   COUNT
item1  10
item2  8

然后,您可以添加一个视图,该视图只是对每个项目的记录求和。

答案 3 :(得分:0)

使用

修复
.IsolationLevel = IsolationLevel.ReadUncommitted;

在连接字符串中。