私は、同時実行中にアプリケーションでSQLデッドロックのシナリオを見つけました。次のSQLデッドロックにはどのような回避策を使用しますか?
exec sp_executesql N'INSERT INTO HQ.dbo.SynchronizingRows ([StudioId], [UpdatedRowId])
SELECT @p0, [t0].[Id] FROM [dbo].[UpdatedRows] AS [t0] WHERE NOT (EXISTS(
SELECT NULL AS [EMPTY] FROM [dbo].[ReceivedUpdatedRows] AS [t1] WHERE ([t1].[StudioId] = @p0)
AND ([t1].[UpdatedRowId] = [t0].[Id])))',N'@p0 uniqueidentifier',@p0='" + this.studioId.ToString() + "';
と: - (私はLINQ2SQLとDataContext.ExecuteCommandを使用しています()、それはthis.studioId.ToString()の出番だノート)私は、デッドロックが発生する二つの文がされていることを信じて
exec sp_executesql N'INSERT INTO HQ.dbo.ReceivedUpdatedRows ([UpdatedRowId], [StudioId], [ReceiveDateTime])
SELECT [t0].[UpdatedRowId], @p0, GETDATE() FROM [dbo].[SynchronizingRows] AS [t0]
WHERE ([t0].[StudioId] = @p0)',N'@p0 uniqueidentifier',@p0='" + this.studioId.ToString() + "';
私の(クライアントサーバー)アプリケーションの基本的なロジックはこれです:誰かがサーバー側で行を挿入または更新するたびに
- 、私はまた、トンに行を挿入彼は変更された行のRowIdを指定して、テーブルUpdatedRowを返します。
- クライアントがデータを同期しようとすると、ReceivedUpdatedRowsテーブル内の特定のクライアントの参照行を含まない、UpdatedRowsテーブルのすべての行が最初にSynchronizingRowsテーブルにコピーされますデッドロック中)。その後、同期中に、SynchronizingRowsテーブルを参照して変更された行が検索されます。このステップは必須です。そうしないと、誰かが新しいローを挿入したり、ローテーションをサーバ側で変更したりすると、それらを見逃してしまい、次の同期中に取得できなくなります。
- 同期が完了すると、ReceivedUpdatedRowsテーブルに行が挿入され、このクライアントがSynchronizingRowsテーブルに含まれるUpdatedRows(デッドロックに参加する2番目のステートメント)を受信したことを指定します。
- 最後に、現在のクライアントに属するSynchronizingRowsテーブルからすべての行を削除します。
私はそれを見る方法、デッドロックは、ステップ2と3の間テーブルSynchronizingRows(略称SR)とReceivedUpdatedRows(略語RUR)上で発生している(一方のクライアントは、ステップ2であり、SRに挿入し、RURから選択されます一方、別のクライアントはステップ3でRURに挿入し、SRから選択する)。
私はSQLデッドロックについて少し調べて、3つの選択肢があるという結論に達しました。 INORDERは、私は各オプション/回避策についての詳細の入力を必要とする決定作るために:回避策1
を:
SQLのデッドロックに関するウェブ上与えられた最初のアドバイス - 再構築テーブル/クエリのようにデッドロックドン」最初に起こることはない。私のIQでは、同期ロジックを何か別のやり方で行う方法はありません。誰かが私の現在の同期ロジックに深く関わりたいと思っている場合、それがどのように、なぜそれが設定されているのかについては、説明のためのリンクを投稿します。おそらく、私よりスマートな人の助けを借りて、デッドロックフリーのロジックを作成することは可能でしょう。
回避策2:
第二の最も一般的なアドバイスは、WITH(NOLOCK)ヒントを使用することであると思われます。この問題は、NOLOCKが一部の行を見落としたり重複したりする可能性があることです。重複は問題ではありませんが、欠落している行は壊滅的です!別のオプションは、WITH(READPAST)ヒントです。それにもかかわらず、これは完璧な解決策であるようです。私は実際には、各行が特定のクライアントにしか属していないので、他のクライアントが挿入/変更している行は気にしないので、ロックされた行を飛ばすことができます。しかし、MSDNのドキュメンテーションは私に少し気にしています - "READPASTが指定されていると、行レベルとページレベルの両方のロックがスキップされます"。私が言ったように、行レベルのロックは問題ではありませんが、ページには複数のクライアント(現在のものを含む)に属する行が含まれている可能性があります。
NOLOCKが行を見逃す可能性があるというブログ記事がたくさんありますが、READPAST(決して)行が見つからないことはありません。これは、テストする簡単な方法がないので実装に懐疑的で神経質になります(実装はケーキの一部であり、SELECT句とジョブ完了の両方にWITH READPASTをポップするだけです)。 誰かがREADPASTヒントが行を見逃す可能性があるかどうかを確認できますか?
回避策3:
最後のオプションは、ALLOW_SNAPSHOT_ISOLATIONとREAD_COMMITED_SNAPSHOTを使用することです。これは100%働くための唯一の選択肢だと思われます - 少なくとも私はそれと矛盾する情報を見つけることはできません。しかし、LINQを使用しているので、設定するのはちょっと面倒です(パフォーマンスヒットはあまり気にしません)。私の頭の上から私はおそらく手動でSQL接続を開いてLINQ2SQL DataContextなどに渡す必要があります...私は非常に深く詳細を見ていない。
somoneは、READPASTが現在のクライアントに関する行を見逃すことがないことを私に安心できる場合は、ほとんどの場合、オプション2を優先します(これまでのように、各クライアントには行があり、それ以外の場合はオプション1はおそらく不可能であるため、私はおそらく、オプション3を実装する必要があります...
私は念のために、同様に3つのテーブルのテーブル定義を投稿します:
CREATE TABLE [dbo].[UpdatedRows](
[Id] [uniqueidentifier] NOT NULL ROWGUIDCOL DEFAULT NEWSEQUENTIALID() PRIMARY KEY CLUSTERED,
[RowId] [uniqueidentifier] NOT NULL,
[UpdateDateTime] [datetime] NOT NULL,
) ON [PRIMARY]
GO
CREATE NONCLUSTERED INDEX IX_RowId ON dbo.UpdatedRows
([RowId] ASC) WITH (STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
GO
CREATE TABLE [dbo].[ReceivedUpdatedRows](
[Id] [uniqueidentifier] NOT NULL ROWGUIDCOL DEFAULT NEWSEQUENTIALID() PRIMARY KEY NONCLUSTERED,
[UpdatedRowId] [uniqueidentifier] NOT NULL REFERENCES [dbo].[UpdatedRows] ([Id]),
[StudioId] [uniqueidentifier] NOT NULL REFERENCES,
[ReceiveDateTime] [datetime] NOT NULL,
) ON [PRIMARY]
GO
CREATE CLUSTERED INDEX IX_Studios ON dbo.ReceivedUpdatedRows
([StudioId] ASC) WITH (STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
GO
CREATE TABLE [dbo].[SynchronizingRows](
[StudioId] [uniqueidentifier] NOT NULL
[UpdatedRowId] [uniqueidentifier] NOT NULL REFERENCES [dbo].[UpdatedRows] ([Id])
PRIMARY KEY CLUSTERED ([StudioId], [UpdatedRowId])
) ON [PRIMARY]
GO
を
PS! Studio =クライアント。
PS2!私は、インデックス定義にALLOW_PAGE_LOCK = ONがあることに気付きました。私がそれを無効にすると、それはREADPASTに何か変わるでしょうか?それをオフにするためのマイナスの短所はありますか?