Sql-Server

是否可以在另一個數據庫中重用來自一個數據庫的計劃記憶體中的查詢計劃?

  • November 4, 2019

例如,如果我點擊 sys 動態視圖來選擇特定的查詢計劃,我是否能夠將該查詢計劃插入到另一個數據庫的計劃記憶體中,以執行相同的確切查詢?(我知道查詢經過雜湊處理並進行比較以確定何時生成新計劃,因此在我的範例中,我將確保查詢確實是逐個字元完全相同的。)

好的,這就是您問題的答案,但您可能想知道為什麼

好的,那為什麼不呢?

不同的數據庫可能有不同的數據,因此需要不同的計劃。

假設您將WideWorldImporters數據庫還原到同一台伺服器TWICE(我們稱之為WWI_1and WWI_2)。你有兩個相同的數據庫。SQL Server 可能會創建一個計劃並將其用於兩個數據庫的查詢。

但問題是,這兩個數據庫一上線,它們的“相同性”就會分叉。它們可以獨立更改。即使它們保持相同的模式,它們的數據也可以獨立更改。因此,SQL Server在編譯計劃時必須獨立考慮數據庫。WWI_1可能有不同的統計數據WWI_2這可能導致不同的計劃。為了讓 SQL Server 對兩個數據庫使用相同的計劃,SQL Server 需要在分叉後跟踪差異——這將比編譯/維護單獨的計劃更複雜和更昂貴。

讓我們舉一個現實世界的例子

假設您是一家軟體公司,並為您的客戶託管軟體。每個客戶都有自己的數據庫。在您的託管環境中,您可能在每台伺服器上都有數百個數據庫,其中每個數據庫都有相同的架構,但每個客戶端的數據都是唯一的。

一位客戶擁有95% 的加州客戶群。查詢加利福尼亞州所有客戶的地址表將導致表掃描

-- 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';

相同的查詢,在具有相同架構的數據庫上,由於不同的統計數據而具有完全不同的計劃。即使這兩個數據庫最初都是從同一個源備份中恢復的,SQL Server 也需要編譯單獨的計劃。

兩個查詢 100% 相同並且架構 100% 相同是不夠的。僅基於這些標準的重用計劃可能是非常錯誤的——因此 SQL Server 不會這樣做。

如果你真的想做,你能做到嗎?

呃……有點。

讓我們使用相同的“您是一家軟體公司,並為您的客戶託管軟體”範例。無論 SQL Server 想要做什麼,您都希望在每個託管數據庫中**強制執行相同的計劃。**您可以使用計劃指南並將相同的指南應用於伺服器上的每個數據庫。這與“插入

$$ one $$查詢計劃到另一個數據庫的計劃記憶體中”……但這實際上是同一回事。 一個完全微不足道的計劃呢?

類似的東西SELECT COUNT(*) FROM dbo.SomeTable很簡單,可以在兩個數據庫上使用相同的計劃,對吧?不,即使那時也沒有!

讓我們創建一個範例:

  • 創建範例數據庫

  • 創建一個表並用一些數據填充它

    • 請注意,該表有一個聚集 PK 和一個非聚集索引
  • 備份和恢復第二個副本到同一台伺服器

這裡有一些程式碼可以做到這一點:

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 來執行計數 查詢計劃掃描PK

SQL Server 選擇掃描 PK,因為它更小。非聚集索引較大,因為它高度分散。

現在索引維護髮生在Sample1數據庫上(但不是Sample2)。有人在以下位置重建索引dbo.SomeTable

ALTER INDEX ALL ON Sample1.dbo.SomeTable REBUILD;

這對查詢計劃有何影響? 查詢計劃掃描不同索引

在 上Sample1,非聚集索引現在很好且緊湊。它比PK小。掃描非聚集索引將是 `Sample1 上的正確選擇,因為它更小,IO 更少,並且速度更快。

Sample2,掃描PK是正確的選擇,因為它更小,IO更少,因此更快。

具有相同數據的相同模式,第二個來自第一個的最近備份。這兩個查詢都有不同的執行計劃,但也都有針對其場景的最佳查詢計劃。

引用自:https://dba.stackexchange.com/questions/252394