0

私はデータベースの経験がほとんどなく、firebird2.5データベースでデッドロックを引き起こすトリガーの問題に遭遇しました。

データベースには2つのテーブルがあります。アイテムがITEMSテーブルに追加または削除されると、トリガーはSTATS.ITEMCOUNTおよびSTATS.SIZEを更新します。合計2つのインクリメントと2つのデクリメントの4つのトリガーがあります。

統計テーブルには1つの行があり、データベースの内容を追跡するために使用されます。私はこれを間違った方法でやっていますか?そうでない場合は、回避策があります。

デッドロックは、アプリケーションを開始してから最初の数分以内に発生します。

UPDATE1:すべてのトリガーを投稿しました。

UPDATE2:投稿されたExecuteNonQueryメソッド

UPDATE3: pilcrowによって親切に提案されたviewメソッドを使用している場合でも、デッドロックが発生します。実際、私は再びデッドロックしたストアドプロシージャを使用しようとしました。Firebird Adoプロバイダーは並列トランザクションをサポートしていないため、selectステートメントをトランザクションでラップすることも失敗しました。

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

2つのスレッドが同じ行を同時に更新しようとするため、デッドロックが発生します。この場合の最も簡単な解決策は、単一のトランザクションとクリティカルセクションを使用して、同時更新を防ぐことです。文字通り数行のコードが必要になります。

他の方法では、完全な情報を使用してテーブルを再設計する必要があります。

于 2011-10-16T18:11:13.780 に答える
0

を使用して修正

.IsolationLevel = IsolationLevel.ReadUncommitted;

接続文字列で。

于 2011-10-24T12:47:17.240 に答える
0

あなたが書く:

...回避策はありますか。

他に少なくとも1つのアプローチがあります。トリガーを使用して集計されたカウントとサイズを事前に計算する代わりに、次のように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;

(注:UPDATEトリガーと列名が示すように、HITSはITEMS.ACCESSCOUNTの合計であると推測しています。MISSESを現在どのように記録しているかはわかりませんが、アプリケーションがSTATSを直接インクリメントしている場合。現在のところ、MISSESでは、その目的のために新しいテーブルを導入してから、そのテーブルを上のビューに結合することができます。)

もちろん、トランザクションを適切にコミットする必要があります。 上で提案されたSTATSビューは、完了したコミット済みのトランザクションと同じくらい正確です。

于 2011-10-17T03:27:01.107 に答える
0

これらの問題は通常、変更ごとに+1または-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 に答える