あまり一般的ではないデッドロックの1つは、1人のユーザーがいて、システムリソースでデッドロックしている場合です。私が最近遭遇したのは、エイリアスタイプを作成し、同じトランザクション内でそのタイプの変数を宣言することです。単体テストまたは展開前テストを実行し、障害をチェックし、どのような場合でもロールバックして、実行したことの痕跡を残さないようにしようとしていると想像してください。パターンは次のようになります:
BEGIN TRANSACTION; GO CREATE TYPE EmailAddress FROM VARCHAR(320); GO DECLARE @x TABLE (e EmailAddress); GO ROLLBACK TRANSACTION;
または、おそらくもう少し複雑です:
BEGIN TRANSACTION; GO CREATE TYPE EmailAddress FROM VARCHAR(320); GO CREATE PROCEDURE dbo.foo @param EmailAddress AS BEGIN SET NOCOUNT ON; DECLARE @x TABLE (e EmailAddress); INSERT @x SELECT @param; END GO DECLARE @x EmailAddress; SET @x = N'whatever'; EXEC dbo.foo @param = N'whatever'; GO ROLLBACK TRANSACTION;
このコードを最初に試したのはSQLServer2012でしたが、どちらの例も次のエラーで失敗しました。
メッセージ1205、レベル13、状態55、行14トランザクション(プロセスID 57)は、別のプロセスとのロックリソースでデッドロックされ、デッドロックの犠牲者として選択されました。トランザクションを再実行します。
そして、デッドロックグラフから学ぶことはほとんどありません:
数年前にさかのぼると、SQL Server 2000(ユーザー定義データ型と呼ばれていたとき)でエイリアスの種類について最初に学んだときのことを思い出します。そのとき、私が最近遭遇したこのデッドロックは発生しませんでした(ただし、これは少なくとも部分的には、エイリアスタイプでテーブル変数を宣言できなかったためです。こことここを参照してください)。 SQL Server 2000 RTM(8.0.194)およびSQL Server 2000 SP4(8.0.2039)で次のコードを実行しましたが、問題なく実行されました。
BEGIN TRANSACTION; GO EXEC sp_addtype @typename = N'EmailAddress', @phystype = N'VARCHAR(320)'; GO CREATE PROCEDURE dbo.foo @param EmailAddress AS BEGIN SET NOCOUNT ON; SELECT @param; END GO EXEC dbo.foo @param = N'whatever'; GO DECLARE @x EmailAddress; SET @x = N'whatever'; EXEC dbo.foo @param = @x; GO ROLLBACK TRANSACTION;
もちろん、このシナリオは当時あまり普及していませんでした。結局のところ、そもそもエイリアスタイプを使用する人はあまりいなかったからです。メタデータをより自己文書化してデータ定義のようにすることができますが、メタデータを変更したい場合は非常に苦痛です。これは別の投稿のトピックになる可能性があります。
SQL Server 2005が登場し、エイリアスタイプを作成するための新しいDDL構文が導入されました:CREATE TYPE
。これは、型を変更する際の問題を実際には解決しませんでした。構文が少しわかりやすくなっただけです。 RTMでは、上記のコードサンプルはすべて、デッドロックなしで問題なく機能しました。ただし、SP4では、それらはすべてデッドロックします。したがって、RTMとSP4の間のどこかで、エイリアスタイプを使用するテーブル変数を含むトランザクションの内部処理を変更しました。
数年早送りしてSQLServer2008に進み、テーブル値のパラメーターが追加されました(ここで適切な使用例を参照してください)。これにより、これらのタイプの使用がはるかに普及し、そのようなタイプを作成して使用しようとしたトランザクションがデッドロックする別のケースが発生しました:
BEGIN TRANSACTION; GO CREATE TYPE dbo.Items AS TABLE(Item INT); GO DECLARE @r dbo.Items; GO ROLLBACK TRANSACTION;
Connectを確認したところ、いくつかの関連項目が見つかりました。そのうちの1つは、この問題がSQL Server2008SP2および2008R2SP1で修正されたと主張しています。
接続#365876:ユーザー定義のデータ型とそれを使用するオブジェクトを作成するときにデッドロックが発生します
これが実際に参照したのは、次のシナリオでした。テーブル変数の型を参照するストアドプロシージャを作成するだけで、SQL Server 2008 RTM(10.0.1600)およびSQL Server 2008 R2 RTM(10.50.1600)でデッドロックが発生します。
BEGIN TRANSACTION; GO CREATE TYPE EmailAddress FROM VARCHAR(320); GO CREATE PROCEDURE dbo.foo @param EmailAddress AS BEGIN SET NOCOUNT ON; DECLARE @x TABLE (e EmailAddress); INSERT @x SELECT @param; END GO ROLLBACK TRANSACTION;
ただし、これはSQL Server 2008 SP3(10.0.5846)または2008 R2 SP2(10.50.4295)ではデッドロックしません。そのため、Connectアイテムに関するコメントを信じる傾向があります。バグのこの部分は、2008SP2および2008R2 SP1で修正されており、最新バージョンでは問題になりませんでした。
しかし、これでも、実際にエイリアスタイプをあらゆる種類の真のテストにかける機能はありません。したがって、プロシージャを作成できるかどうかをテストするだけであれば、単体テストは成功します。型をローカル変数またはローカルテーブル変数の列として宣言することを忘れてください。
これを解決する唯一の方法は、トランザクションを開始する前にテーブルタイプを作成し、後で明示的にドロップする(または複数のトランザクションに分割する)ことです。多くの場合、自動化されたテストフレームワークとハーネスを使用して、この制限を説明するために動作方法を完全に変更することは、非常に面倒であり、不可能でさえあります。
そこで、すべてのメジャーバージョンの初期ビルドと最新ビルドでいくつかのテストを行うことにしました:SQL Server 2005 RTM、2005 SP4、2008 RTM、2008 SP3、2008 R2 RTM、2008 R2 SP2、2012 RTM、2012 SP1および2014CTP2(はい、すべてインストールしています)。いくつかのConnectアイテムとさまざまなコメントを確認したところ、どのユースケースがどこでサポートされているのか疑問に思いました。そして、この問題のどの側面が実際に修正されたかを知るために奇妙な衝動に駆られました。これらすべてのビルドに対して、エイリアスタイプ、テーブル変数、およびテーブル値パラメーターを含むさまざまな潜在的なデッドロックシナリオをテストしました。コードは次のとおりです。
/* alias type - declare in local table variable always deadlocks on 2005 SP4 -> 2014, except in 2005 RTM */ BEGIN TRANSACTION; GO CREATE TYPE EmailAddress FROM VARCHAR(320) GO DECLARE @r TABLE(e EmailAddress); GO ROLLBACK TRANSACTION; /* alias type - create procedure with param & table var sometimes deadlocks - 2005 SP4, 2008 RTM & SP1, 2008 R2 RTM */ BEGIN TRANSACTION; GO CREATE TYPE EmailAddress FROM VARCHAR(320); GO CREATE PROCEDURE dbo.foo @param EmailAddress AS BEGIN SET NOCOUNT ON; DECLARE @x TABLE (e EmailAddress); INSERT @x SELECT @param; END GO ROLLBACK TRANSACTION; /* alias type - create procedure, declare & exec always deadlocks on 2005 SP4 -> 2014, except on 2005 RTM */ BEGIN TRANSACTION; GO CREATE TYPE EmailAddress FROM VARCHAR(320); GO CREATE PROCEDURE dbo.foo @param EmailAddress AS BEGIN SET NOCOUNT ON; DECLARE @x TABLE (e EmailAddress); INSERT @x SELECT @param; END GO DECLARE @x EmailAddress; SET @x = N'whatever'; EXEC dbo.foo @param = N'whatever'; GO ROLLBACK TRANSACTION; /* obviously did not run these on SQL Server 2005 builds */ /* table type - create & declare local variable always deadlocks on 2008 -> 2014 */ BEGIN TRANSACTION; GO CREATE TYPE dbo.Items AS TABLE(Item INT); GO DECLARE @r dbo.Items; GO ROLLBACK TRANSACTION; /* table type - create procedure with param and SELECT never deadlocks on 2008 -> 2014 */ BEGIN TRANSACTION; GO CREATE TYPE dbo.Items AS TABLE(Item INT); GO CREATE PROCEDURE dbo.foo @param dbo.Items READONLY AS BEGIN SET NOCOUNT ON; SELECT Item FROM @param; END GO ROLLBACK TRANSACTION; /* table type - create procedure, declare & exec always deadlocks on 2008 -> 2014 */ BEGIN TRANSACTION; GO CREATE TYPE dbo.Items AS TABLE(Item INT); GO CREATE PROCEDURE dbo.foo @param dbo.Items READONLY AS BEGIN SET NOCOUNT ON; SELECT Item FROM @param; END GO DECLARE @x dbo.Items; EXEC dbo.foo @param = @x; GO ROLLBACK TRANSACTION;
そして、結果は上記の私の話を反映しています。SQLServer 2005 RTMはどのシナリオでもデッドロックしませんでしたが、SP4がロールアラウンドするまでに、すべてがデッドロックしました。これは、2008SP2および2008R2 SP1で、「タイプの作成とプロシージャの作成」シナリオで修正されましたが、他のシナリオでは修正されませんでした。すべての結果を示す表を次に示します。
SQLServerバージョン/ビルド番号 | ||||||||||
SQL 2005 | SQL 2008 | SQL 2008 R2 | SQL 2012 | SQL 2014 | ||||||
RTM 9.0.1399 | SP4 9.0.5324 | RTM 10.0.1600 | SP3 10.0.5846 | RTM 10.50.1600 | SP2 10.50.4295 | RTM 11.0.2100 | SP1 11.0.3381 | CTP2 12.0.1524 | ||
テーブル変数で宣言 | ||||||||||
プロシージャの作成 | ||||||||||
procの作成と実行 | ||||||||||
ローカル変数を宣言する | N / A | |||||||||
プロシージャの作成 | ||||||||||
procの作成と実行 |
結論
したがって、話の教訓は、テーブルタイプを作成し、そのタイプを使用するプロシージャまたは関数を作成し、タイプを宣言し、モジュールをテストし、ロールする上記のユースケースの修正はまだないということです。すべてが戻ってきました。いずれにせよ、ここにあなたが見るべき他のConnectアイテムがあります。うまくいけば、あなたはそれらに投票し、このデッドロックシナリオがあなたのビジネスに直接どのように影響するかを説明するコメントを残すことができます:
近い将来、これらのConnectアイテムにいくつかの説明が追加されることを完全に期待していますが、いつプッシュスルーされるかは正確にはわかりません。