web-dev-qa-db-ja.com

リンクサーバー全体でのリモートクエリに関するSQLパフォーマンスの問題

このsproc

create proc dbo.Get_Accounts as
begin
  declare @current_date datetime
  set @current_date = dbo.fn_currdate()

  select [fields]
  into dbo.current_accounts
  from linkedserver.database.dbo.accounts
  where date = @current_date
end

10分後に継続的に失敗し、次のエラーメッセージが表示されます。

サーバー:メッセージ7399、レベル16、状態1、行1 OLE DBプロバイダー 'SQLOLEDB'がエラーを報告しました。リソース制限に達したため、プロバイダーによって実行が終了しました。[OLE/DBプロバイダーが返されましたメッセージ:タイムアウトが発生しました] OLE DBエラートレース[OLE/DBプロバイダー 'SQLOLEDB' ICommandText :: Executeが0x80040e31を返しました:リソースの制限に達したため、プロバイダーによって実行が終了しました。].

ただし、日付がハードコードされた対話型クエリウィンドウで、同じデータベース(リモートデータベースではない)から同じクエリを実行すると、次のようになります。

  select [fields]
  into dbo.current_accounts
  from linkedserver.database.dbo.accounts
  where date = '1/20/2012'

30秒で戻ります。

ローカルサーバーはSQLSERVER 2008、リモートサーバーはSQLSERVER 2000です。

無駄に次のことを行いました:

  • ストアドプロシージャを再作成しました。
  • ストアドプロシージャのsp_recompile
  • dbo.accountsの統計を更新する
  • dbo.accountsのインデックスを削除して再作成
  • dbo.accountsにインデックスを削除して、
  • ローカルサーバーとリモートサーバーの両方でのDBCC FREEPROCCACHEおよびDBCC DROPCLEANBUFFERS
  • リモートサーバーを再起動しました(ローカルサーバーでは簡単なオプションではありません)

ご質問

  • 誰かがこの奇妙な行動を説明できますか?
  • それを修正するための他のオプションに関する提案はありますか?
8
Bob Probst

あなたはオンにすることができます トレースフラグ73 これはmightより詳細なエラーメッセージを表示します

代表的なクエリは何行を返しますか? 2つのサーバー間のネットワーク接続はどのくらいの速さ/信頼性がありますか?

大きなデータセットの転送に時間がかかりすぎる可能性があります(実際のクエリ時間に加えて)。タイムアウト値を上げることができます。

次のようにして、タイムアウト設定を再構成することができます。

リモートログインタイムアウトを300秒に設定します。

sp_configure 'remote login timeout', 300
go 
reconfigure with override 
go 

リモートクエリのタイムアウトを0(無限待機)に設定します。

sp_configure 'remote query timeout', 0 
go 
reconfigure with override 
go 

更新SQL Server 2012 SP1以降SELECT権限はDBCC SHOW_STATISTICSにアクセスできるようになり、リンクサーバーでの読み取り専用のパフォーマンスが向上します。参照: https://msdn.Microsoft.com/en-us/library/ms174384(v = sql.110).aspx

更新:あなたはそれがデータのサイズや接続速度ではないと言って正しいです。それは私の霧のメモリでベルを鳴らし、私はそれをどこで見たかを思い出しました: アプリケーションで遅い、SSMSで速い? (リンクサーバーの問題)。これはパラメータースニッフィングではなく、(権限が原因で)欠落している統計自体であり、不正なクエリプランが使用される原因になります。

見積もりが異なることがわかります。 sysadminとして実行した場合、推定値は1行でした。これは、Northwindに注文IDが20000を超える注文がないためです。しかし、通常のユーザーとして実行した場合、推定値は249行でした。この特定の数値は、830注文の30%、またはオプティマイザに情報がない場合の不等式操作の推定値として認識されます。以前は、これは不明な変数値が原因でしたが、この場合、不明である可能性のある変数はありません。いいえ、欠けているのは統計そのものです。

クエリがローカルサーバーのテーブルのみにアクセスする限り、オプティマイザは常にクエリ内のすべてのテーブルの統計にアクセスできます。追加の権限チェックはありません。ただし、これはリンクサーバー上のテーブルとは異なります。 SQL Serverがリンクサーバーにアクセスする場合、サーバー間通信にのみ使用されるシークレットプロトコルはありません。いいえ、代わりにSQL Serverはリンクサーバーの標準のOLE DBインターフェイスを使用し、他のSQL Serverインスタンス、Oracle、テキストファイル、または自作のデータソースであり、他のユーザーと同じように接続します。統計情報の正確な取得方法は、データソースとOLE=問題のDBプロバイダーによって異なります。この場合、プロバイダーはSQL Server Native Clientであり、2つの手順で統計情報を取得します。(これは、リモートサーバーに対してプロファイラーを実行することにより実行されます。最初に、プロバイダーは、sp_table_statistics2_rowsetプロシージャを実行します。これにより、列の統計情報とそのカーディナリティおよび密度情報が返されます。2番目の手順では、プロバイダーがコマンドDBCC SHOW_STATISTICSを実行します。完全な分散統計を返します(この記事の後半でこのコマンドについて詳しく見ていきます)。ここにキャッチがあります。DBCCSHOW_STATISTICSを実行するには、サーバーロールsysadminまたはデータベースロールdb_ownerまたはdb_ddladmのメンバーである必要があります。に。

そして、これが私が異なる結果を得た理由です。 sysadminとして実行すると、注文ID> 20000の行がないことを示す完全な分散統計が得られ、見積もりは1行でした。 (オプティマイザが統計からゼロ行を想定することは決してないことを思い出してください。)しかし、プレーンユーザーとして実行すると、DBCC SHOW_STATISTICSはアクセス許可エラーで失敗しました。このエラーは伝搬されませんでしたが、代わりにオプティマイザは統計がないことを受け入れ、デフォルトの仮定を使用しました。カーディナリティ情報を取得したため、リモートテーブルには830行あり、推定では249行あることがわかりました。

リンクサーバーへのアクセスを含むクエリがアプリケーションで低速であるが、SSMSからテストすると高速で実行されるというパフォーマンスの問題が発生した場合は、リモートデータベースに対する不十分なアクセス許可が原因であるかどうかを常に調査する必要があります。 (リンクサーバーへのアクセスはクエリでは明白ではないかもしれませんが、ビューでは非表示である可能性があることに注意してください。)リモートデータベースのアクセス許可が問題であると判断した場合、どのようなアクションを実行できますか?

  • ユーザーをロールdb_ddladminに追加できますが、これにより、ユーザーにテーブルを追加および削除する権利が与えられるため、これはお勧めできません。

  • デフォルトでは、ユーザーがリモートサーバーに接続するとき、ユーザーは自分自身として接続しますが、sp_addlinkedsrvloginを使用してログインマッピングを設定し、ユーザーがdb_ddladminのメンバーシップを持つプロキシアカウントにマップするようにすることができます。このプロキシアカウントはSQLログインである必要があるため、リモートサーバーでSQL認証が有効になっていない場合、これはオプションではありません。この解決策もセキュリティの観点からは少し疑わしいですが、以前の提案の方が優れています。

  • 場合によっては、OPENQUERYを使用してクエリを書き換え、リモートサーバーでの評価を強制することができます。これは、クエリに複数のリモートテーブルが含まれている場合に特に便利です。 (ただし、オプティマイザがリモートサーバーから取得する統計情報がさらに少なくなるため、逆効果になることもあります。)

  • もちろん、ヒントと計画ガイドの完全なバッテリーを使用して、必要な計画を取得することもできます。

  • 最後に、リンクサーバーへのアクセスが必要かどうかを自問する必要があります。データベースが同じサーバー上にある可能性はありますか?データを複製できますか?他の解決策はありますか?

11
Mitch Wheat

これを試すとどうなりますか(リモートサーバーで何を実行するかを明示的に示します):

select [fields]
into dbo.current_accounts
from OPENQUERY(linkedserver, 'SELECT [fields] FROM database.dbo.accounts where date = ''1/20/2012''');

私はあなたのケースでSQL Serverがリモートサーバーからテーブル全体をプルし、ローカルでクエリを実行しているだけだと思います(これは過去に何度も発生しました)。 OPENQUERYを使用するか、またはSPをリモートサーバー上に作成することによって)明示的にすることを好むので、混乱の可能性はありません。

2
Gareth

これはリソースの問題であるため、SQLサーバー外のメモリプールが外部ドライバーのロードに使用され、CLRがその制限に近い可能性があります。デフォルトは256MBです。これを回避するには、SQLサーバー構成マネージャーの[詳細設定]タブに移動し、起動パラメーターの最後に-gオプションを追加することをお勧めします。多くのリンクサーバーを使用しているため、通常はこれを行います。 http://msdn.Microsoft.com/en-us/library/ms190737.aspx

1
nopol

役立つかもしれない2つのアイデアがあります。また、リンクサーバーに対してクエリを実行するパフォーマンスに不運があったこともお伝えします。ですから、私の最初の推奨は、可能であればそれを避けることです。

私の最初のアイデアは、ストアドプロシージャをSQL Server 2000ボックスにインストールし、ローカルサーバーを参照させることです。その後、リモートでストアドプロシージャを実行できます。

exec linkedserver.database.dbo.Get_Accounts

この方法を使用できる場合は、パフォーマンスが大幅に向上するはずです。

私の2番目のアイデアは、ストアドプロシージャを実行するときに推定クエリプランを取得することです。時間がかかっていることを示していますか?リンクサーバーで使用しているアカウントには、テーブル統計を取得するための十分な権限がない可能性があります(ローカルサーバーよりもリンクサーバーの方がより多くの権限が必要です)。そしてそれはクエリを信じられないほど遅くする可能性があります。あなたはその特定の問題についてもっと読むことができます ここ

1
Jeff Siver