2017/07/09
下記3件の質問の内、1と2について回答が得られていないのですが、一旦質問をクローズいたします。
理由としましては、
・3の質問に関してご回答いただけたことで、対応策の事例収集という目的の一つは達せられたこと。
・排他ロックについて異なる事象が発生し、1、2の質問から派生して調査等をおこなったほうがよさそうであること。
・ただ、1、2に関して自分なりの調査・検証時間がしばらく確保できない見込みであること。
です。
ご回答いただいたお二方ありがとうございました。お礼が遅くなりすみません。
今後、自分での調査・検証を進めることができましたら、追記等させていただきたいと思います。
SQL Serverを使用したWebアプリで発生した事象です。
##現象
先発のセッションで排他ロックがかかり、後発のセッションで排他ロック検知後、正常にエラー処理がされるかというテストをしていました。
【方法】
Session1:単テーブル(ヘッダ)に対し、PKを条件に指定してロック(NOWAIT)。
Session2:複数テーブル(ヘッダとボディ)を結合しつつ、ヘッダテーブルのみロック(結合条件はヘッダのPK)(NOWAIT)。
【期待値】
Session1でロックした行は、Session2の検索範囲に含まれるはずなので、Session2においてSELECT時に排他エラーが発生する。
【結果】
排他エラーが発生しない。
デバックすると、Session2のSELECT箇所および排他エラー処理はすり抜けてしまい、後続のヘッダテーブル更新箇所でロック開放待ち→タイムアウトしていました。
##環境
SQL Server2014
ReadCommitted Snapshot
##問題のSQL等
発生した事象で使われているテーブルやSQLについて。
テーブルやインデックス定義、ロックSQLについては、こちらに書き込む用にかなり簡略化しています。そのため、事象の再現はできませんでした。あくまでイメージと思っていただければ……。
ヘッダテーブル
SQL
1CREATE TABLE [dbo].[受注情報ヘッダ]( 2 [支社コード] [nvarchar](4) NOT NULL, 3 [受注伝票番号] [nvarchar](10) NOT NULL, 4 [納品先コード] [nvarchar](10) NOT NULL, 5 [出荷日] [nvarchar](8) NOT NULL, 6 [納品日] [nvarchar](8) NOT NULL, 7 [処理ステータス] [nvarchar](1) NOT NULL, 8 CONSTRAINT [PK_受注情報ヘッダ] PRIMARY KEY CLUSTERED 9( 10 [支社コード] ASC, 11 [受注伝票番号] ASC 12)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = OFF) ON [PRIMARY] 13) ON [PRIMARY]
ボディテーブル
SQL
1CREATE TABLE [dbo].[受注情報ボディ]( 2 [支社コード] [nvarchar](4) NOT NULL, 3 [受注伝票番号] [nvarchar](10) NOT NULL, 4 [受注伝票行番号] [int] NOT NULL, 5 [商品コード] [nvarchar](10) NOT NULL, 6 [ロット番号] [nvarchar](8) NOT NULL, 7 [受注数量] [numeric](10, 0) NOT NULL, 8 [処理ステータス] [nvarchar](1) NOT NULL, 9 CONSTRAINT [PK_受注情報ボディ] PRIMARY KEY CLUSTERED 10( 11 [支社コード] ASC, 12 [受注伝票番号] ASC, 13 [受注伝票行番号] ASC 14)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = OFF) ON [PRIMARY] 15) ON [PRIMARY]
インデックス(※性能の関係で、PK以外にいくつかインデックスが定義されています)
SQL
1CREATE NONCLUSTERED INDEX [IDX_受注情報ヘッダ01] ON [dbo].[受注情報ヘッダ] 2( 3 [支社コード] ASC, 4 [出荷日] ASC 5)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = OFF) 6
アプリ内のロックSQL
SQL
1SELECT * 2FROM 受注情報ヘッダ H WITH(ROWLOCK,UPDLOCK,NOWAIT) 3INNER JOIN 受注情報ボディ B 4ON 1=1 5AND H.支社コード = B.支社コード 6AND H.受注伝票番号 = B.受注伝票番号 7WHERE 1=1 8AND B.支社コード = '1111' 9AND B.商品コード = 'ITEM0001'
##検証
事象の発生後、下記の調査をしました。
1)実行計画の調査
Session2のアプリ内SQLの実行計画を見てみたところ、ヘッダテーブルのスキャンにPK以外のインデックス(例:上記のIDX_受注情報ヘッダ01)が使われていました。
推定実行プラン
Session1:
Clustered Index Seek[PK] 受注情報ヘッダ
Session2:
Index Seek(NonClustered)[IDX_受注情報ヘッダ01] 受注情報ヘッダ 50%
Index Seek(NonClustered)[受注情報ボディのインデックス] 受注情報ボディ 50%
2)ロック状況の確認
いくつかサイトで紹介されていた、下記のロック状況確認SQLを実行しました。
結果、Session1とSession2それぞれ、「resource_associated_entity_id」の値が異なりました。
SQL
1SELECT 2T1.resource_type 3,T1.resource_associated_entity_id 4,(CASE 5 WHEN T1.resource_type = 'OBJECT' 6 THEN OBJECT_NAME(T1.resource_associated_entity_id) 7 ELSE ( 8 SELECT 9 OBJECT_NAME(OBJECT_ID) 10 FROM sys.partitions 11 WHERE 12 hobt_id = T1.resource_associated_entity_id 13 ) 14 END 15) AS object_name 16,T1.request_mode 17,T1.request_type 18,T1.request_status 19,T1.request_session_id 20,( 21SELECT hostname 22FROM sys.sysprocesses 23WHERE spid = T1.request_session_id 24) AS PROCESSNAME 25FROM 26sys.dm_tran_locks T1 27WHERE 1=1 28AND T1.resource_type <> 'DATABASE' 29ORDER BY 30T1.request_session_id 31
検索結果
Session1と2のSQLを実行後、確認SQLを実行したときの検索結果は下図のようになりました。
両セッション間で、resource_associated_entity_idの値が違います。
3)WITH句のヒント句にインデックス指定
WITH句に、PKのインデックスを指定したところ、期待値どおりになりました。
推定実行プラン
Session2
Clustered Index Seek[PK] 受注情報ヘッダ 51%
Index Seek(NonClustered)[受注情報ボディのインデックス] 受注情報ボディ 49%
※ちなみに、nowait指定を外すとSession2は待ちとなりますが、そのときの検索結果は次のようになりました。
##質問と仮説
1)resource_associated_entity_idとは何を指すのか?
sys.dm_tran_locksの「resource_associated_entity_id」の項目に表示される値は何なのでしょうか?
ロックされているオブジェクトのID(=ロックされているヘッダテーブルの行)だと思っていたのですが、調査2で、スキャンのインデックスが異なる場合に違うIDになっていたのを疑問に思いました。
2)仮説:SQL Serverはロック対象を取得できる行そのものではなくスキャンに使用したインデックスで判定している?
調査1~3の結果から、SELECT結果そのもので同一行をロックしているか判定するのではなく、スキャンに使用するインデックスで判定しているのかなと考えました。
3)SQL Serverにおけるロックの実装・設計はどうすべきか
上記仮説が正しい場合、厳密にロックを制御したければ、1行1行ループしながらロックするとか、インデックスをいちいちヒントにしていするとか、やはり必要なのでしょうか。
ロックエスカレーションの件もありますし、正直SQL Serverって難しいなという印象があります。
個人的にOracleのイメージが強いせいもあるのですが……。
結構困っているので、お知恵をお借りできれば幸いです。

回答2件
あなたの回答
tips
プレビュー
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
2017/06/12 16:21
2017/06/17 06:35
2017/06/17 07:07
2017/06/17 12:36
2017/07/09 12:22