web-dev-qa-db-ja.com

あるデータベースのプランキャッシュから別のデータベースのクエリプランを再利用することはできますか?

たとえば、sys動的ビューをヒットして特定のクエリプランを選択した場合、そのクエリプランを別のデータベースのプランキャッシュに挿入して、まったく同じクエリを実行できますか? (クエリがハッシュされ、新しいプランをいつ生成するかを決定するために比較されることを知っているので、私の例では、クエリが文字ごとにまったく同じであることを確認します。)

9
J.D.

番号

さて、それがあなたの質問への答えですが、おそらくなぜを知りたいでしょう。

じゃあ、どうして?

データベースごとにデータが異なる可能性があるため、異なる計画が必要です。

WideWorldImportersデータベースを同じサーバーに復元するとします[〜#〜] twice [〜#〜](_WWI_1_および_WWI_2_としましょう)。 2つの同一のデータベースがあります。 SQL Serverは1つのプランを作成し、それを両方のデータベースのクエリで使用できます。

しかし問題は、これら2つのデータベースがオンラインになった瞬間に、「同じ」フォークが発生することです。それらは個別に変更できます。同じスキーマを維持していても、dataは独立して変更できます。したがって、SQL Serverは、計画をコンパイルするときにデータベースを個別に考慮する必要があります。 _WWI_1_には、_WWI_2_とは異なる statistics が含まれる可能性があり、異なるプランになる可能性があります。 SQL Serverが両方のデータベースに同じプランを使用するためには、SQL Serverはフォーク後の違いを追跡する必要があります。これは、別々のプランをコンパイル/維持するよりも複雑でコストがかかります。

実際の例を見てみましょう

あなたがソフトウェア会社であり、顧客のためのホストソフトウェアであるとしましょう。すべての顧客が独自のデータベースを取得します。ホスティング環境では、すべてのサーバーに数百のデータベースがあり、すべてのデータベースには同じスキーマがありますが、データは各クライアントに固有です。

1つのクライアントには、95%カリフォルニアのクライアントの顧客ベースがあります。カリフォルニアのすべての顧客の住所テーブルをクエリすると、テーブルscanが生成されます。

_-- For CustomerA this query returns 95% of the table, so it scans
SELECT CustomerID
FROM dbo.Addresses
WHERE StateCode = 'CA';
_

別のクライアントには、米国のすべての州に均等に分散された顧客ベースがあり、重要な国際ビジネスも持っています。比較的少数のクライアントがカリフォルニア出身です。この場合、カリフォルニアのすべての顧客の住所テーブルをクエリすると、テーブルseekが生成されます。

_-- For CustomerB this query returns <1% of the table, so it seeks
SELECT CustomerID
FROM dbo.Addresses
WHERE StateCode = 'CA';
_

統計が異なるために計画が根本的に異なる、同一のスキーマを持つデータベースに対する同一のクエリ。これら2つのデータベースが元々同じソースバックアップから復元された場合でも、SQL Serverは個別の計画をコンパイルする必要があります。

2つのクエリが100%同一であることは十分ではなく、スキーマは100%同一です。これらの基準のみに基づいて再利用されたプランは非常に間違っている可能性があります。そのため、SQL Serverはそれを行いません。

本当にやりたかったらやってくれませんか?

ええと…。

同じ「ソフトウェア会社であり、顧客のためのソフトウェアをホストする」の例を使用してみましょう。 SQL Serverの目的に関係なく、ホストされているすべてのデータベースで同じ計画をforceしたい。 計画ガイド を使用して、サーバー上のすべてのデータベースに同じガイダンスを適用できます。これはquite「insert [one] query plan into another database's plan cache」と同じではありません...しかし、事実上同じです。

完全に取るに足らない計画はどうですか?

SELECT COUNT(*) FROM dbo.SomeTableのようなものは、2つのデータベースの両方で同じプランを使用するのに十分簡単でしょう。いいえ、それでもそうではありません!

例を作ってみましょう:

  • サンプルデータベースを作成する
  • テーブルを作成してデータを入力します
    • テーブルにはクラスター化PKと非クラスター化インデックスがあることに注意してください
  • 同じサーバーに2番目のコピーをバックアップおよび復元する

これを行うためのコードを次に示します。

_CREATE DATABASE Sample1;
GO

USE Sample1
GO

CREATE TABLE dbo.SomeTable (
    SomeID int IDENTITY(1,1) PRIMARY KEY CLUSTERED,
    StuffType int,
    OtherStuff varchar(100),
    INDEX OtherStuff(OtherStuff)
);
GO

SET NOCOUNT ON;
INSERT INTO dbo.SomeTable (StuffType,OtherStuff)
SELECT  object_id%50,
        name 
FROM sys.objects;
GO 1000


BACKUP DATABASE Sample1 TO DISK = '/var/opt/mssql/data/Sample1.bak' WITH INIT;


RESTORE DATABASE Sample2 FROM DISK = '/var/opt/mssql/data/Sample1.bak'
    WITH MOVE 'Sample1' TO '/var/opt/mssql/data/Sample2.mdf',
         MOVE 'Sample1_log' TO '/var/opt/mssql/data/Sample2_log.ldf';
_

それでは、COUNT(*)クエリを実行してみましょう。両方で同じ計画ですか?はい!私のラップトップでは、クラスター化されたPKをスキャンしてカウントを実行しています QueryPlansScanningPK

SQL Serverは、PKが小さいため、PKのスキャンを選択しています。非クラスター化インデックスは非常に断片化されているため、大きくなります。

現在、インデックスのメンテナンスは_Sample1_データベースで行われます(ただし、_Sample2_では行われません)。誰かが_dbo.SomeTable_のインデックスを再構築します:

_ALTER INDEX ALL ON Sample1.dbo.SomeTable REBUILD;
_

それはクエリプランにどのように影響しますか? QueryPlansScanningDifferentIndexes

_Sample1_では、非クラスター化インデックスはナイス&コンパクトになりました。 PKよりも小さいです。非クラスタ化インデックスのスキャンは、Sample1で適切な選択です。これは、インデックスが小さく、IOが少なく、高速であるためです。

_Sample2_では、PKのスキャンが適切な選択です。これは、PKが小さく、IOが少なく、したがって高速であるためです。

2番目が最初の最新のバックアップから分岐された、同じデータを持つ同一のスキーマ。これらのクエリはどちらも実行プランが異なりますが、シナリオにはbestクエリプランもあります。

11
AMtwo