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


8

この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
  • リモートサーバーを再起動しました(ローカルサーバーでは簡単なオプションではありません)

ご質問

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

回答:


11

トレースフラグ7300をオンにすると、より詳細なエラーメッセージが表示される可能性があります

代表的なクエリは何行を返しますか?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として実行した場合、Northwindには注文IDが20000を超える注文がないため、推定値は1行でした。これは正しい数値です。しかし、通常のユーザーとして実行した場合、推定値は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_ddladminのメンバーである必要があります。

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

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

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

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

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

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

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


約14万件のレコードを返します。しかし、日付値がハードコードされている場合は問題なく機能するため、パラメーター化されたバージョンに非常に影響を与えるI / Oまたはネットワークの問題は考えられません。私の直感は、クエリがリモートサーバーに渡されており、リモートオプティマイザーがパラメーターを理解できない場合に、どういうわけか不正なクエリプランを選択すると言っています。しかし、キャッシュ/バッファのインデックスを再作成してパージすることで、それを修正できるはずです(私はそう思います)。タイムアウトを調べて、少なくとも復帰できるかどうかを確認します。ありがとう

1
すばらしい回答であり、私が抱えていた問題を正確に説明してくれてありがとう。さらに、MSDNよると、 SQL2012 SP1以降では、SELECTアクセス許可を持つユーザーがアクセスできるようになり、DBCC SHOW_STATISTICSセキュリティを損なうことなく、リンクサーバーの読み取り専用パフォーマンスが向上します。
スティーブペティファー2014年

2

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

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

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


1

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


1

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

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

exec linkedserver.database.dbo.Get_Accounts

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

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

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