sys.schemasおよびsys.synonymsに対するクエリの実行が1人のユーザーに対して非常に遅い


8

シナリオ: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上記はありません「DBO」。

SSMSでこのクエリをUserAとして実行すると、クエリは1秒未満で返されます。

UserB(.NETサービスの接続方法)として実行すると、クエリは3〜6 かかり、CPU使用率は(4コアの)25%になります。

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。
テーブル「ワークファイル」。スキャンカウント0、論理読み取り0、物理読み取り0、先読み読み取り0、LOB論理読み取り0、LOB物理読み取り0、LOB先読み読み取り0。
テーブル「ワークテーブル」。スキャンカウント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と同じ(高速)速度で実行されます。

db_ownerUserB に追加すると、クエリは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に追加して、にDENY関連付けられていることがわかっているすべての特権を使用してみましたdb_owner。結果は高速なクエリです。何かを逃した(完全に可能)か、db_owner役割の特別なチェックがあります。

回答:


5

次のように(私が使用しているクエリを再書きたいことdboではなく、XXXX私がやるというのが私のテストデータベースにいくつかの同義語を見つけます。)。これは、より効率的であることがわかった書き換えに似ていますが、変数を宣言して2つのクエリを使用する必要がありません。

SELECT name, base_object_name 
FROM sys.synonyms 
WHERE schema_id = SCHEMA_ID(N'dbo')
ORDER BY name

これにより、次のような計画が生成されます。

ここに画像の説明を入力してください

Filterこの計画の演算子について非常に興味深いのは、内部has_access()チェックを実行する述語があることです。このフィルターは、現在のアカウントに表示するための十分な権限がないオブジェクトを削除します。ただし、db_ownerロールのメンバーである場合、このチェックは短絡されます(つまり、はるかに迅速に完了します)。これにより、パフォーマンスの違いが説明される場合があります。

ここに画像の説明を入力してください

これが元のクエリのクエリプランです。データベース上のすべての類義語(1,126私の場合は、多くの場合はさらに多くの類義語)は、類義語has_access()だけ2がスキーマに一致するにもかかわらず、非常に高価なフィルターを通過することに注意してください。上記の簡略化されたクエリを使用することhas_access()で、データベース内のすべての同義語ではなく、クエリに一致する同義語に対してのみが呼び出されるようにすることができます。

ここに画像の説明を入力してください


sys.dm_exec_query_profilesを使用してさらに探索する

Martinが示唆しているように、sys.dm_exec_query_profilesSQL Server 2014以降を使用することで、has_access()チェックが重大なボトルネックであることを確認できます。db_owner〜700Kオブジェクトのデータベースのアカウントを使用して次のクエリを実行すると、クエリは次のようになります~500ms

SELECT COUNT(*)
FROM sys.objects

以外のアカウントで実行するとdb_owner、この同じクエリは約8分かかります。出力をより簡単に解析できるように書いたp_queryProgressプロシージャを実際の計画で実行して使用するとsys.dm_exec_query_profiles、ほとんどすべての処理時間がチェックをFilter実行しているオペレーターに費やされていることがわかりますhas_access()

ここに画像の説明を入力してください


TokenAndPermUserStoreの問題?この場合、このKB記事はsupport.microsoft.com/en-gb/kb/955644
Martin Smith、

@MartinSmith非常に興味深いですが、以前はaccess check cache bucket countおよびaccess check cache quota構成オプションを認識していませんでした。少し遊んでみてください。
Geoff Patterson

このキャッシュがここのケースに関連しているかどうかはわかりません。過去に問題を引き起こしたことを覚えています。
マーティン・スミス

1
@MartinSmithこれらの設定は影響しませんでしたが、キャッシュで興味深いことが起こっています。キャッシュの存在は有害なようです。たとえば、WHILE(1=1) BEGIN DBCC FREESYSTEMCACHE ('TokenAndPermUserStore') WAITFOR DELAY '00:00:05' ENDループを永遠に実行すると、クエリは通常2分未満で完了しますが、通常は8分で完了します。
Geoff Patterson

1
皆さんに感謝します-Microsoftからの応答は上記のコメントに反響します。クエリの書き換えが最善の解決策です。has_access()は最初にdb_ownerまたはsysadminをテストするためのショートサーキットがあり、その結果大きな時間差が生じたことがわかりました。
ジェームズ

0

これがまだライブである場合-同じ問題が発生しています-あなたがdboまたはsysadminのいずれかである場合、sys.objects(またはそのようなもの)へのすべてのアクセス-それは、個々のオブジェクトに対するチェックなしで瞬時に発生します。

低いdb_datareaderの場合は、各オブジェクトを順番にチェックする必要があります...これらはビュー/テーブルではなく関数のように動作するため、クエリプランでは非表示になっています。

計画は同じに見えますが、フードの背後で別のことをしています


弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.