0

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

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

stats 表只有一行,用于跟踪数据库中的内容。我这样做是错误的吗?如果没有,是否有工作回合。

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

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

4 回答 4

1

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

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

于 2011-10-16T18:11:13.780 回答
0

通过使用修复

.IsolationLevel = IsolationLevel.ReadUncommitted;

在连接字符串中。

于 2011-10-24T12:47:17.240 回答
0

你写:

... 有工作轮次吗?

至少还有另一种方法。除了使用触发器来预先计算聚合计数和大小,您可以简单地在视图中按需计算它们,如下所示:

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 目前,您可以为此目的引入一个表,然后将该表 JOIN 到上面的 VIEW 中。)

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

于 2011-10-17T03:27:01.107 回答
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

然后,您可以添加一个简单地汇总每个项目的记录的视图。

于 2011-10-20T18:30:27.347 回答