シナリオ:SQL Server 2014(v12.0.4100.1)
.NETサービスは次のクエリを実行します。
SELECT name, base_object_name
FROM sys.synonyms
WHERE schema_id IN (SELECT schema_id
FROM sys.schemas
WHERE name = N'XXXX')
ORDER BY name
...約6500行を返しますが、3分以上経過するとタイムアウトすることがよくあります。上記のXXXX
はnot 'dbo'です。
このクエリをSSMSでUserAとして実行すると、クエリは1秒未満で返されます。
UserB(.NETサービスの接続方法)として実行すると、クエリは3-6分かかり、CPU%は25%( 4コアの)全体の時間。
UserAは、sysadminロールのドメインログインです。
UserBはSQLログインです。
EXEC sp_addrolemember N'db_datareader', N'UserB'
EXEC sp_addrolemember N'db_datawriter', N'UserB'
EXEC sp_addrolemember N'db_ddladmin', N'UserB'
GRANT EXECUTE TO [UserB]
GRANT CREATE SCHEMA TO [UserB]
GRANT VIEW DEFINITION TO [UserB]
上記のSQLをExecute as...Revert
ブロックでラップすることで、SSMSでこれを複製できます。そのため、.NETコードは見えなくなります。
実行計画は同じに見えます。私はXMLを比較しましたが、わずかな違い(CompileTime、CompileCPU、CompileMemory)しかありません。
IO Statsはすべて物理的な読み取りを示していません。
テーブル 'sysobjvalues'。スキャンカウント0、論理読み取り19970、物理読み取り0、先読み読み取り0、lob論理読み取り0、lob物理読み取り0、lob先読み読み取り0。 テーブル 'Workfile'。スキャンカウント0、論理読み取り0、物理読み取り0、先読み読み取り0、lob論理読み取り0、lob物理読み取り0、lob先読み読み取り0。 テーブル 'Worktable'。スキャンカウント0、論理読み取り0、物理読み取り0、先読み読み取り0、LOB論理読み取り0、LOB物理読み取り0、LOB先読み読み取り0。 テーブル 'sysschobjs'。スキャンカウント1、論理読み取り9122、物理読み取り0、先読み読み取り0、LOB論理読み取り0、LOB物理読み取り0、LOB先読み読み取り0。 テーブル 'sysclsobjs'。スキャンカウント0、論理読み取り2、物理読み取り0、先読み読み取り0、lob論理読み取り0、lob物理読み取り0、lob先読み読み取り0。
XEventの待機ステータス(〜3分のクエリの場合)は次のとおりです。
+ --------------------- + ------------ + --------- ------------- + ------------------------------ + ----- ------------------------ + |待機タイプ|待機数|合計待機時間(ミリ秒)|総リソース待機時間(ミリ秒)|合計信号待機時間(ミリ秒)| + --------------------- + ------------ +- --------------------- + ---------------------------- --- + ----------------------------- + | SOS_SCHEDULER_YIELD | 37300 | 427 | 20 | 407 | | NETWORK_IO | 5 | 26 | 26 | 0 | | IO_COMPLETION | 3 | 1 | 1 | 0 | + --------------------- + ------------ + ------- --------------- + ------------------------------- +- --------------------------- +
クエリを書き換えると(SSMSでは、アプリコードにアクセスできません)、次のようになります。
declare @id int
SELECT @id=schema_id FROM sys.schemas WHERE name = N'XXXX'
SELECT a.name, base_object_name FROM sys.synonyms a
WHERE schema_id = @id
ORDER BY name
次に、UserBはUserAと同じ(高速)速度で実行されます。
UserBにdb_owner
を追加すると、クエリは1秒未満で実行されます。
このテンプレートを介して作成されたスキーマ:
DECLARE @TranName VARCHAR(20)
SELECT @TranName = 'MyTransaction'
BEGIN TRANSACTION @TranName
GO
IF NOT EXISTS (SELECT SCHEMA_NAME FROM INFORMATION_SCHEMA.SCHEMATA
WHERE SCHEMA_NAME = '{1}')
BEGIN
EXEC('CREATE SCHEMA [{1}]')
EXEC sp_addextendedproperty @name='User', @value='{0}', @level0type=N'Schema', @level0name=N'{1}'
END
GO
{2}
COMMIT TRANSACTION MyTransaction;
GO
そして、{2}は、そのスキーマで作成されたシノニムのリストだと思います。
クエリへの2つのポイントでのクエリプロファイル:
マイクロソフトでチケットをオープンしました。
また、UserBをdb_owner
に追加してから、db_owner
に関連付けられている既知のすべての権限をDENY
ingしました。結果は高速なクエリです。何かを逃した(完全に可能)、またはdb_owner
ロールの特別なチェックがあります。
クエリを次のように書き直すこともできます(テストデータベースでいくつかの同義語を見つけるために、dbo
ではなくXXXX
を使用しています)。これは、より効率的であることがわかった書き換えに似ていますが、変数を宣言して2つのクエリを使用する必要がありません。
_SELECT name, base_object_name
FROM sys.synonyms
WHERE schema_id = SCHEMA_ID(N'dbo')
ORDER BY name
_
これにより、次のような計画が生成されます。
この計画のFilter
演算子について非常に興味深いことの1つは、内部has_access()
チェックを実行する述語があることです。このフィルターは、現在のアカウントに表示するための十分な権限がないオブジェクトを削除します。ただし、_db_owner
_ロールのメンバーである場合、このチェックは短絡されます(つまり、はるかに迅速に完了します)。これにより、パフォーマンスの違いが説明される場合があります。
これが元のクエリのクエリプランです。データベース上のすべての同義語(私の場合は_1,126
_ですが、場合によってはもっと多い可能性があります)は、スキーマに一致する_2
_の同義語のみであっても、非常に高価なhas_access()
フィルターを通過することに注意してください。上記の簡略化されたクエリを使用すると、has_access()
がデータベース内のすべてのシノニムではなく、クエリに一致するシノニムに対してのみ呼び出されるようにすることができます。
sys.dm_exec_query_profilesを使用してさらに探索する
Martinが示唆しているように、SQL Server 2014以降で_sys.dm_exec_query_profiles
_を使用することにより、has_access()チェックが重大なボトルネックであることを確認できます。 〜700Kオブジェクトのデータベースで_db_owner
_アカウントを使用して次のクエリを実行すると、クエリは_~500ms
_を取得します。
_SELECT COUNT(*)
FROM sys.objects
_
_db_owner
_ではないアカウントで実行すると、この同じクエリは約8分かかります。実際の計画を実行し、_sys.dm_exec_query_profiles
_の出力をより簡単に解析するために記述した p_queryProgress プロシージャを使用すると、ほぼすべての処理時間がFilter
has_access()
チェックを実行している演算子:
これがまだライブである場合-同じ問題が発生しています-あなたがdboまたはsysadminのいずれかである場合、sys.objects(またはそのようなもの)へのアクセスはすべて、個々のオブジェクトに対するチェックなしで瞬時に行われるようです。
低いdb_datareaderの場合、各オブジェクトを順番にチェックする必要があります...これらはビュー/テーブルではなく関数のように動作するため、クエリプランでは非表示になっています。
計画は同じに見えますが、フードの背後で別のことをしています