我对数据库的经验很少,并且遇到了导致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
答案 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;
在连接字符串中。