Database
 sql >> Cơ Sở Dữ Liệu >  >> RDS >> Database

Cờ theo dõi mới để sửa hiệu suất biến bảng

Từ lâu, các biến bảng có số lượng hàng lớn có thể là vấn đề, vì trình tối ưu hóa luôn coi chúng là một hàng. Nếu không có biên dịch lại sau khi biến bảng đã được điền (kể từ trước đó là trống), không có bản số cho bảng và quá trình biên dịch lại tự động không xảy ra vì các biến bảng thậm chí không phải tuân theo ngưỡng biên dịch lại. Do đó, các kế hoạch dựa trên số lượng bảng là 0, không phải một, nhưng mức tối thiểu được tăng lên một như Paul White (@SQL_Kiwi) mô tả trong câu trả lời dba.stackexchange này.

Cách chúng tôi thường có thể giải quyết vấn đề này là thêm OPTION (RECOMPILE) đối với truy vấn tham chiếu đến biến bảng, buộc trình tối ưu hóa phải kiểm tra bản chất của biến bảng sau khi nó đã được điền. Để tránh phải thực hiện và thay đổi thủ công mọi truy vấn để thêm gợi ý biên dịch lại rõ ràng, một cờ theo dõi mới (2453) đã được giới thiệu trong SQL Server 2012 Gói Dịch vụ 2 và SQL Server 2014 Cập nhật tích lũy # 3:

    KB # 2952444:Khắc phục:Hiệu suất kém khi bạn sử dụng các biến bảng trong SQL Server 2012 hoặc SQL Server 2014

Khi cờ theo dõi 2453 hoạt động, trình tối ưu hóa có thể thu được hình ảnh chính xác về số lượng bảng sau khi biến bảng đã được tạo. Đây có thể là Một điều tốt ™ cho rất nhiều truy vấn, nhưng có lẽ không phải tất cả và bạn nên biết cách hoạt động của nó khác với OPTION (RECOMPILE) . Đáng chú ý nhất, tối ưu hóa nhúng tham số mà Paul White nói đến trong bài đăng này xảy ra trong OPTION (RECOMPILE) , nhưng không theo cờ theo dõi mới này.

Một thử nghiệm đơn giản

Thử nghiệm ban đầu của tôi chỉ bao gồm việc điền một biến bảng và chọn từ nó; điều này mang lại tổng số hàng ước tính quá quen thuộc là 1. Đây là bài kiểm tra tôi đã chạy (và tôi đã thêm gợi ý biên dịch lại để so sánh):

DBCC TRACEON(2453);
 
DECLARE @t TABLE(id INT PRIMARY KEY, name SYSNAME NOT NULL UNIQUE);
 
INSERT @t SELECT TOP (1000) [object_id], name FROM sys.all_objects;
 
SELECT t.id, t.name
  FROM @t AS t;
 
SELECT t.id, t.name
  FROM @t AS t OPTION (RECOMPILE);
 
DBCC TRACEOFF(2453);

Sử dụng SQL Sentry Plan Explorer, chúng ta có thể thấy rằng kế hoạch đồ họa cho cả hai truy vấn trong trường hợp này là giống hệt nhau, có lẽ ít nhất một phần vì đây là một kế hoạch tầm thường theo nghĩa đen:


Kế hoạch đồ họa để quét chỉ mục tầm thường đối với @t

Tuy nhiên, các ước tính không giống nhau. Ngay cả khi cờ theo dõi được bật, chúng tôi vẫn nhận được ước tính về 1 từ quá trình quét chỉ mục nếu chúng tôi không sử dụng gợi ý biên dịch lại:


So sánh các ước tính cho một kế hoạch nhỏ trong lưới câu lệnh


So sánh ước tính giữa cờ theo dõi (trái) và biên dịch lại (phải)

Nếu bạn đã từng gặp tôi trực tiếp, bạn có thể hình dung ra khuôn mặt tôi đã tạo ra vào thời điểm này. Tôi nghĩ chắc chắn rằng bài viết KB đã liệt kê sai số cờ theo dõi hoặc tôi cần bật một số cài đặt khác để nó thực sự hoạt động.

Benjamin Nevarez (@BenjaminNevarez) đã nhanh chóng chỉ ra với tôi rằng tôi cần xem xét kỹ hơn bài viết "Các lỗi được khắc phục trong SQL Server 2012 Gói Dịch vụ 2" KB. Mặc dù họ đã che khuất văn bản đằng sau dấu đầu dòng ẩn trong Mục nổi bật> Công cụ quan hệ, bài viết danh sách sửa chữa thực hiện công việc tốt hơn một chút trong việc mô tả hành vi của cờ theo dõi so với bài viết gốc (nhấn mạnh của tôi):

Nếu biến bảng được kết hợp với các bảng khác trong SQL Server, nó có thể dẫn đến hiệu suất chậm do lựa chọn kế hoạch truy vấn không hiệu quả vì SQL Server không hỗ trợ thống kê hoặc theo dõi số hàng trong một biến bảng trong khi biên dịch kế hoạch truy vấn.

Vì vậy, nó sẽ xuất hiện từ mô tả này rằng cờ theo dõi chỉ có nghĩa là để giải quyết vấn đề khi biến bảng tham gia vào một phép nối. (Tại sao sự khác biệt đó không được thực hiện trong bài viết gốc, tôi không biết.) Nhưng nó cũng hoạt động nếu chúng ta làm cho các truy vấn hoạt động nhiều hơn một chút - truy vấn trên được trình tối ưu hóa coi là tầm thường và cờ theo dõi thì không ' t thậm chí cố gắng làm bất cứ điều gì trong trường hợp đó. Nhưng nó sẽ phát huy tác dụng nếu tối ưu hóa dựa trên chi phí được thực hiện, ngay cả khi không tham gia; cờ theo dõi chỉ đơn giản là không có tác dụng đối với các kế hoạch tầm thường. Dưới đây là một ví dụ về một kế hoạch không tầm thường không liên quan đến sự tham gia:

DBCC TRACEON(2453);
 
DECLARE @t TABLE(id INT PRIMARY KEY, name SYSNAME NOT NULL UNIQUE);
 
INSERT @t SELECT TOP (1000) [object_id], name FROM sys.all_objects;
 
SELECT TOP (100) t.id, t.name
  FROM @t AS t ORDER BY NEWID();
 
SELECT TOP (100) t.id, t.name
  FROM @t AS t ORDER BY NEWID() OPTION (RECOMPILE);
 
DBCC TRACEOFF(2453);

Kế hoạch này không còn tầm thường; tối ưu hóa được đánh dấu là đầy đủ. Phần lớn chi phí được chuyển sang toán tử sắp xếp:


Sơ đồ đồ họa đơn giản hơn

Và các ước tính xếp hàng cho cả hai truy vấn (tôi sẽ lưu cho bạn các mẹo công cụ lần này, nhưng tôi có thể đảm bảo với bạn rằng chúng giống nhau):


Lưới câu lệnh cho các kế hoạch nhỏ hơn có và không có gợi ý biên dịch lại

Vì vậy, có vẻ như bài viết KB không hoàn toàn chính xác - tôi đã có thể ép buộc hành vi được mong đợi của cờ theo dõi mà không cần giới thiệu một phép nối. Nhưng tôi cũng muốn thử nghiệm nó với một tham gia.

Thử nghiệm tốt hơn

Hãy lấy ví dụ đơn giản này, có và không có cờ theo dõi:

--DBCC TRACEON(2453);
 
DECLARE @t TABLE(id INT PRIMARY KEY, name SYSNAME NOT NULL UNIQUE);
 
INSERT @t SELECT TOP (1000) [object_id], name FROM sys.all_objects;
 
SELECT t.name, c.name
  FROM @t AS t
  LEFT OUTER JOIN sys.all_columns AS c
  ON t.id = c.[object_id];
 
--DBCC TRACEOFF(2453);

Nếu không có cờ theo dõi, trình tối ưu hóa ước tính rằng một hàng sẽ đến từ quá trình quét chỉ mục so với biến bảng. Tuy nhiên, với cờ theo dõi được bật, nó sẽ đạt được 1.000 hàng:


So sánh các ước tính quét chỉ mục (không có cờ theo dõi ở bên trái, cờ theo dõi ở bên phải)

Sự khác biệt không dừng lại ở đó. Nếu xem xét kỹ hơn, chúng ta có thể thấy nhiều quyết định khác nhau mà trình tối ưu hóa đã đưa ra, tất cả đều xuất phát từ những ước tính tốt hơn sau:


So sánh các kế hoạch (không có cờ theo dõi ở bên trái, cờ theo dõi ở bên phải)

Tóm tắt nhanh về sự khác biệt:

  • Truy vấn không có cờ theo dõi đã thực hiện 4.140 thao tác đọc, trong khi truy vấn có ước tính được cải thiện chỉ thực hiện 424 (giảm gần 90%).
  • Trình tối ưu hóa ước tính rằng toàn bộ truy vấn sẽ trả về 10 hàng mà không có cờ theo dõi và 2.318 hàng chính xác hơn nhiều khi sử dụng cờ theo dõi.
  • Không có cờ theo dõi, trình tối ưu hoá đã chọn thực hiện phép nối các vòng lồng nhau (điều này có ý nghĩa khi một trong các đầu vào được ước tính là rất nhỏ). Điều này dẫn đến toán tử ghép và cả hai chỉ mục đều tìm cách thực thi 1.000 lần, ngược lại với phép so khớp băm được chọn dưới cờ theo dõi, trong đó toán tử ghép và cả hai lần quét chỉ được thực thi một lần.
  • Tab Table I / O cũng hiển thị 1.000 lần quét (quét phạm vi được ngụy trang dưới dạng tìm kiếm chỉ mục) và số lần đọc logic cao hơn nhiều so với syscolpars (bảng hệ thống đằng sau sys.all_columns ).
  • Mặc dù thời lượng không bị ảnh hưởng đáng kể (24 mili giây so với 18 mili giây), nhưng bạn có thể hình dung loại tác động của những khác biệt khác này có thể có đối với một truy vấn nghiêm trọng hơn.
  • Nếu chuyển sơ đồ sang chi phí ước tính, chúng ta có thể thấy biến bảng có thể đánh lừa trình tối ưu hóa mà không có cờ theo dõi khác nhau đến mức nào:


So sánh số lượng hàng ước tính (không có cờ theo dõi ở bên trái, theo dõi cờ ở bên phải)

Rõ ràng và không gây sốc khi trình tối ưu hóa thực hiện công việc tốt hơn trong việc lựa chọn phương án phù hợp khi nó có một cái nhìn chính xác về bản số liên quan. Nhưng với chi phí nào?

Biên dịch lại và Chi phí

Khi chúng tôi sử dụng OPTION (RECOMPILE) với lô ở trên, mà không bật cờ theo dõi, chúng tôi nhận được kế hoạch sau - khá giống với kế hoạch có cờ theo dõi (sự khác biệt đáng chú ý duy nhất là các hàng ước tính là 2.316 thay vì 2.318):


Cùng một truy vấn với TÙY CHỌN (RECOMPILE)

Vì vậy, điều này có thể khiến bạn tin rằng cờ theo dõi đạt được kết quả tương tự bằng cách kích hoạt biên dịch lại cho bạn mọi lúc. Chúng tôi có thể điều tra điều này bằng cách sử dụng phiên Sự kiện mở rộng rất đơn giản:

CREATE EVENT SESSION [CaptureRecompiles] ON SERVER 
ADD EVENT sqlserver.sql_statement_recompile
  (
    ACTION(sqlserver.sql_text)
  ) 
  ADD TARGET package0.asynchronous_file_target
  (
    SET FILENAME = N'C:\temp\CaptureRecompiles.xel'
  );
GO
ALTER EVENT SESSION [CaptureRecompiles] ON SERVER STATE = START;

Tôi đã chạy bộ lô sau, thực thi 20 truy vấn với (a) không có tùy chọn biên dịch lại hoặc cờ theo dõi, (b) tùy chọn biên dịch lại và (c) cờ theo dõi cấp phiên.

/* default - no trace flag, no recompile */
 
DECLARE @t TABLE(id INT PRIMARY KEY, name SYSNAME NOT NULL UNIQUE);
 
INSERT @t SELECT TOP (1000) [object_id], name FROM sys.all_objects;
 
SELECT t.name, c.name
  FROM @t AS t
  LEFT OUTER JOIN sys.all_columns AS c
  ON t.id = c.[object_id];
 
GO 20
 
/* recompile */
 
DECLARE @t TABLE(id INT PRIMARY KEY, name SYSNAME NOT NULL UNIQUE);
 
INSERT @t SELECT TOP (1000) [object_id], name FROM sys.all_objects;
 
SELECT t.name, c.name
  FROM @t AS t
  LEFT OUTER JOIN sys.all_columns AS c
  ON t.id = c.[object_id] OPTION (RECOMPILE);
 
GO 20
 
/* trace flag */
 
DBCC TRACEON(2453);  
 
DECLARE @t TABLE(id INT PRIMARY KEY, name SYSNAME NOT NULL UNIQUE);
 
INSERT @t SELECT TOP (1000) [object_id], name FROM sys.all_objects;
 
SELECT t.name, c.name
  FROM @t AS t
  LEFT OUTER JOIN sys.all_columns AS c
  ON t.id = c.[object_id];
 
DBCC TRACEOFF(2453);
 
GO 20

Sau đó, tôi xem dữ liệu sự kiện:

SELECT 
  sql_text = LEFT(sql_text, 255),
  recompile_count = COUNT(*)
FROM 
(
  SELECT 
    x.x.value(N'(event/action[@name="sql_text"]/value)[1]',N'nvarchar(max)')
  FROM 
    sys.fn_xe_file_target_read_file(N'C:\temp\CaptureRecompiles*.xel',NULL,NULL,NULL) AS f
    CROSS APPLY (SELECT CONVERT(XML, f.event_data)) AS x(x)
) AS x(sql_text)
GROUP BY LEFT(sql_text, 255);

Kết quả cho thấy không có biên dịch lại nào xảy ra theo truy vấn chuẩn, câu lệnh tham chiếu đến biến bảng đã được biên dịch lại một lần dưới cờ theo dõi và như bạn có thể mong đợi, mọi lúc với RECOMPILE tùy chọn:

sql_text regmpile_count
/ * biên dịch lại * / DECLARE @t TABLE (tôi INT… 20
/ * dấu vết cờ * / DBCC TRACEON (2453); DECLARE @t… 1

Kết quả của truy vấn dựa trên dữ liệu XEvents

Tiếp theo, tôi tắt phiên Sự kiện mở rộng, sau đó thay đổi lô thành đo lường trên quy mô lớn. Về cơ bản, mã đo lường 1.000 lần lặp lại việc tạo và điền một biến bảng, sau đó chọn kết quả của nó vào bảng #temp (một cách để loại bỏ đầu ra của nhiều tập kết quả khác), sử dụng mỗi phương pháp trong số ba phương pháp.

SET NOCOUNT ON;
 
/* default - no trace flag, no recompile */
 
SELECT SYSDATETIME();
GO
DECLARE @t TABLE(id INT PRIMARY KEY, name SYSNAME NOT NULL UNIQUE);
 
INSERT @t SELECT TOP (1000) [object_id], name FROM sys.all_objects;
 
SELECT t.id, c.name
  INTO #x
  FROM @t AS t
  LEFT OUTER JOIN sys.all_columns AS c
  ON t.id = c.[object_id];
 
DROP TABLE #x;
 
GO 1000
SELECT SYSDATETIME();
GO
 
 
/* recompile */
 
DECLARE @t TABLE(id INT PRIMARY KEY, name SYSNAME NOT NULL UNIQUE);
 
INSERT @t SELECT TOP (1000) [object_id], name FROM sys.all_objects;
 
SELECT t.id, c.name
  INTO #x
  FROM @t AS t
  LEFT OUTER JOIN sys.all_columns AS c
  ON t.id = c.[object_id] OPTION (RECOMPILE);
 
DROP TABLE #x;
 
GO 1000
SELECT SYSDATETIME();
GO
 
 
/* trace flag */
 
DBCC TRACEON(2453);  
 
DECLARE @t TABLE(id INT PRIMARY KEY, name SYSNAME NOT NULL UNIQUE);
 
INSERT @t SELECT TOP (1000) [object_id], name FROM sys.all_objects;
 
SELECT t.id, c.name
  INTO #x
  FROM @t AS t
  LEFT OUTER JOIN sys.all_columns AS c
  ON t.id = c.[object_id];
 
DROP TABLE #x;
 
DBCC TRACEOFF(2453);  
 
GO 1000
SELECT SYSDATETIME();
GO

Tôi đã chạy lô này 10 lần và lấy giá trị trung bình; họ là:

Phương pháp Thời lượng trung bình
(mili giây)
Mặc định 23.148,4
Biên dịch lại 29,959,3
Cờ theo dõi 22.100,7

Thời lượng trung bình cho 1.000 lần lặp

Trong trường hợp này, việc nhận các ước tính đúng mỗi khi sử dụng gợi ý biên dịch lại chậm hơn nhiều so với hành vi mặc định, nhưng sử dụng cờ theo dõi thì nhanh hơn một chút. Điều này có ý nghĩa bởi vì - trong khi cả hai phương pháp đều sửa hành vi mặc định của việc sử dụng ước tính giả (và kết quả là nhận được một kế hoạch xấu), các biên dịch lại sẽ lấy tài nguyên và khi chúng không hoặc không thể mang lại một kế hoạch hiệu quả hơn, có xu hướng đóng góp vào thời lượng lô tổng thể.

Có vẻ đơn giản, nhưng chờ đã…

Thử nghiệm trên là một chút - và có chủ đích - thiếu sót. Chúng tôi đang chèn cùng một số hàng (1.000) vào biến bảng mọi lúc . Điều gì xảy ra nếu dân số ban đầu của biến bảng thay đổi đối với các lô khác nhau? Chắc chắn chúng ta sẽ thấy các bản biên dịch lại sau đó, ngay cả dưới cờ theo dõi, phải không? Thời gian cho một bài kiểm tra khác. Hãy thiết lập phiên Sự kiện mở rộng hơi khác, chỉ với một tên tệp mục tiêu khác (để không trộn lẫn bất kỳ dữ liệu nào từ phiên khác):

CREATE EVENT SESSION [CaptureRecompiles_v2] ON SERVER 
ADD EVENT sqlserver.sql_statement_recompile
  (
    ACTION(sqlserver.sql_text)
  ) 
  ADD TARGET package0.asynchronous_file_target
  (
    SET FILENAME = N'C:\temp\CaptureRecompiles_v2.xel'
  );
GO
ALTER EVENT SESSION [CaptureRecompiles_v2] ON SERVER STATE = START;

Bây giờ, hãy kiểm tra lô này, thiết lập số lượng hàng cho mỗi lần lặp khác nhau đáng kể. Chúng tôi sẽ chạy điều này ba lần, xóa các nhận xét thích hợp để chúng tôi có một lô không có cờ theo dõi hoặc biên dịch lại rõ ràng, một lô có cờ theo dõi và một lô có OPTION (RECOMPILE) (có nhận xét chính xác ở đầu giúp xác định các lô này dễ dàng hơn ở những nơi như đầu ra Sự kiện mở rộng):

/* default, no trace flag or recompile */
/* recompile */
/* trace flag */
 
DECLARE @i INT = 1;
 
WHILE @i <= 6
BEGIN
  --DBCC TRACEON(2453); -- uncomment this for trace flag
 
  DECLARE @t TABLE(id INT PRIMARY KEY);
 
  INSERT @t SELECT TOP (CASE @i 
      WHEN 1 THEN 24
	  WHEN 2 THEN 1782
	  WHEN 3 THEN 1701
	  WHEN 4 THEN 12
	  WHEN 5 THEN 15
	  WHEN 6 THEN 1560 
	END) [object_id]
    FROM sys.all_objects;
 
  SELECT t.id, c.name
    FROM @t AS t
    INNER JOIN sys.all_objects AS c
    ON t.id = c.[object_id]
    --OPTION (RECOMPILE); -- uncomment this for recompile
 
  --DBCC TRACEOFF(2453); -- uncomment this for trace flag
 
  DELETE @t;
  SET @i += 1;
END

Tôi đã chạy các lô này trong Management Studio, mở chúng riêng lẻ trong Plan Explorer và lọc cây câu lệnh chỉ trên SELECT truy vấn. Chúng ta có thể thấy các hành vi khác nhau trong ba lô bằng cách xem các hàng ước tính và thực tế:


So sánh ba lô, xem hàng ước tính so với hàng thực tế
Trong lưới ngoài cùng bên phải, bạn có thể thấy rõ nơi không xảy ra biên dịch lại dưới cờ theo dõi

Chúng tôi có thể kiểm tra dữ liệu XEvents để xem điều gì đã thực sự xảy ra với các bản biên dịch lại:

SELECT 
  sql_text = LEFT(sql_text, 255),
  recompile_count = COUNT(*)
FROM 
(
  SELECT 
    x.x.value(N'(event/action[@name="sql_text"]/value)[1]',N'nvarchar(max)')
  FROM 
    sys.fn_xe_file_target_read_file(N'C:\temp\CaptureRecompiles_v2*.xel',NULL,NULL,NULL) AS f
    CROSS APPLY (SELECT CONVERT(XML, f.event_data)) AS x(x)
) AS x(sql_text)
GROUP BY LEFT(sql_text, 255);

Kết quả:

sql_text regmpile_count
/ * biên dịch lại * / DECLARE @i INT =1; VÌ SAO… 6
/ * dấu vết cờ * / DECLARE @i INT =1; VÌ SAO… 4

Kết quả của truy vấn dựa trên dữ liệu XEvents

Rất thú vị! Dưới cờ theo dõi, chúng ta * do * thấy các bản biên dịch lại, nhưng chỉ khi giá trị tham số thời gian chạy thay đổi đáng kể so với giá trị được lưu trong bộ nhớ cache. Khi giá trị thời gian chạy khác nhau, nhưng không quá nhiều, chúng tôi không nhận được biên dịch lại và các ước tính tương tự được sử dụng. Vì vậy, rõ ràng là cờ theo dõi giới thiệu một ngưỡng biên dịch lại cho các biến bảng và tôi đã xác nhận (thông qua một thử nghiệm riêng) rằng điều này sử dụng thuật toán tương tự như thuật toán được mô tả cho các bảng #temp trong bài báo "cổ" nhưng vẫn có liên quan này. Tôi sẽ chứng minh điều này trong một bài đăng tiếp theo.

Một lần nữa, chúng tôi sẽ kiểm tra hiệu suất, chạy lô 1.000 lần (với phiên Sự kiện mở rộng bị tắt) và đo thời lượng:

Phương pháp Thời lượng trung bình
(mili giây)
Mặc định 101.285,4
Biên dịch lại 111.423,3
Cờ theo dõi 110.318,2

Thời lượng trung bình cho 1.000 lần lặp

Trong trường hợp cụ thể này, chúng tôi mất khoảng 10% hiệu suất bằng cách buộc biên dịch lại mỗi lần hoặc bằng cách sử dụng cờ theo dõi. Không chắc chắn chính xác cách vùng châu thổ được phân bổ:Các kế hoạch dựa trên các ước tính tốt hơn không đáng kể tốt hơn? Các bản biên dịch lại có bù đắp cho bất kỳ mức tăng hiệu suất nào không bằng nhiêu đó ? Tôi không muốn mất quá nhiều thời gian cho việc này, và đó là một ví dụ tầm thường, nhưng nó sẽ cho bạn thấy rằng việc xử lý cách thức hoạt động của trình tối ưu hóa có thể là một chuyện không thể đoán trước. Đôi khi bạn có thể tốt hơn với hành vi mặc định của cardinality =1, biết rằng bạn sẽ không bao giờ gây ra bất kỳ biên dịch lại quá mức nào. Trường hợp cờ theo dõi có thể có ý nghĩa nếu bạn có các truy vấn trong đó bạn liên tục điền các biến bảng với cùng một tập dữ liệu (giả sử như bảng tra cứu Mã Bưu điện) hoặc bạn luôn sử dụng 50 hoặc 1.000 hàng (giả sử điền một biến bảng để sử dụng trong phân trang). Trong mọi trường hợp, bạn chắc chắn nên kiểm tra tác động của điều này đối với bất kỳ khối lượng công việc nào mà bạn định giới thiệu cờ theo dõi hoặc các bản biên dịch lại rõ ràng.

TVP và các loại bảng

Tôi cũng tò mò rằng điều này sẽ ảnh hưởng như thế nào đến các loại bảng và liệu chúng ta có thấy bất kỳ cải tiến nào về cardinality cho TVP, nơi có cùng một triệu chứng này hay không. Vì vậy, tôi đã tạo một loại bảng đơn giản bắt chước biến bảng đang được sử dụng cho đến nay:

USE MyTestDB;
GO
 
CREATE TYPE dbo.t AS TABLE 
(
  id INT PRIMARY KEY
);

Sau đó, tôi lấy lô ở trên và chỉ cần thay thế DECLARE @t TABLE(id INT PRIMARY KEY); với DECLARE @t dbo.t; - mọi thứ khác vẫn y như cũ. Tôi đã chạy ba lô giống nhau và đây là những gì tôi thấy:


So sánh ước tính và thực tế giữa hành vi mặc định, biên dịch lại tùy chọn và cờ theo dõi 2453

Vì vậy, có, có vẻ như cờ theo dõi hoạt động theo cùng một cách với TVP - việc biên dịch lại tạo ra các ước tính mới cho trình tối ưu hóa khi số lượng hàng vượt qua ngưỡng biên dịch lại và bị bỏ qua khi số lượng hàng "đủ gần".

Ưu, nhược điểm và lưu ý

Một lợi thế của cờ theo dõi là bạn có thể tránh một số biên dịch lại và vẫn thấy số lượng bảng - miễn là bạn mong đợi số lượng hàng trong biến bảng ổn định hoặc không quan sát thấy sai lệch kế hoạch đáng kể do số lượng bảng khác nhau. Một cách khác là bạn có thể kích hoạt nó trên toàn cầu hoặc ở cấp phiên và không phải giới thiệu các gợi ý biên dịch lại cho tất cả các truy vấn của bạn. Và cuối cùng, ít nhất là trong trường hợp số lượng biến trong bảng là ổn định, các ước tính thích hợp dẫn đến hiệu suất tốt hơn mặc định và cũng tốt hơn so với việc sử dụng tùy chọn biên dịch lại - tất cả những biên dịch đó chắc chắn có thể cộng lại.

Tất nhiên cũng có một số nhược điểm. Một cái mà tôi đã đề cập ở trên là so với OPTION (RECOMPILE) bạn bỏ lỡ một số tối ưu hóa nhất định, chẳng hạn như nhúng tham số. Một điều khác là cờ theo dõi sẽ không có tác động như bạn mong đợi đối với các kế hoạch tầm thường. Và một điều tôi đã khám phá ra trong quá trình sử dụng QUERYTRACEON gợi ý để thực thi cờ theo dõi ở cấp truy vấn không hoạt động - theo như tôi có thể nói, cờ theo dõi phải được đặt khi biến bảng hoặc TVP được tạo và / hoặc được điền để trình tối ưu hóa nhìn thấy bản số ở trên 1.

Hãy nhớ rằng việc chạy cờ theo dõi trên toàn cầu giới thiệu khả năng hồi quy kế hoạch truy vấn cho bất kỳ truy vấn nào liên quan đến biến bảng (đó là lý do tại sao tính năng này được giới thiệu dưới cờ theo dõi ngay từ đầu), vì vậy hãy đảm bảo kiểm tra toàn bộ khối lượng công việc của bạn bất kể bạn sử dụng cờ theo dõi như thế nào. Ngoài ra, khi bạn đang kiểm tra hành vi này, hãy làm như vậy trong cơ sở dữ liệu người dùng; một số tối ưu hóa và đơn giản hóa mà bạn thường mong đợi sẽ không xảy ra khi ngữ cảnh được đặt thành tempdb, vì vậy bất kỳ hành vi nào bạn quan sát được ở đó có thể không nhất quán khi bạn di chuyển mã và cài đặt sang cơ sở dữ liệu người dùng.

Kết luận

Nếu bạn sử dụng các biến bảng hoặc TVP với số lượng hàng lớn nhưng tương đối nhất quán, bạn có thể thấy hữu ích khi bật cờ theo dõi này cho các lô hoặc quy trình nhất định để có được số lượng bảng chính xác mà không buộc biên dịch lại theo cách thủ công trên các truy vấn riêng lẻ. Bạn cũng có thể sử dụng cờ theo dõi ở cấp cá thể, cờ này sẽ ảnh hưởng đến tất cả các truy vấn. Nhưng giống như bất kỳ thay đổi nào, trong cả hai trường hợp, bạn sẽ cần phải siêng năng kiểm tra hiệu suất của toàn bộ khối lượng công việc của mình, xem xét rõ ràng bất kỳ hồi quy nào và đảm bảo rằng bạn muốn hành vi của cờ theo dõi vì bạn có thể tin tưởng vào sự ổn định của biến bảng của mình số lượng hàng.

Tôi rất vui khi thấy cờ theo dõi được thêm vào SQL Server 2014, nhưng sẽ tốt hơn nếu điều đó chỉ trở thành hành vi mặc định. Không phải là có bất kỳ lợi thế đáng kể nào khi sử dụng các biến bảng lớn thay vì các bảng #temp lớn, nhưng sẽ rất tuyệt khi thấy sự chẵn lẻ giữa hai kiểu cấu trúc tạm thời này có thể được quy định ở cấp cao hơn. Chúng ta càng có nhiều vật ngang giá, thì mọi người càng ít phải cân nhắc xem họ nên sử dụng cái nào (hoặc ít nhất là có ít tiêu chí để cân nhắc khi lựa chọn). Martin Smith có phần Hỏi &Đáp tuyệt vời tại dba.stackexchange có lẽ hiện tại là do cập nhật:Sự khác biệt giữa bảng tạm thời và biến bảng trong SQL Server là gì?

Lưu ý quan trọng

Nếu bạn định cài đặt SQL Server 2012 Service Pack 2 (cho dù có sử dụng cờ theo dõi này hay không), vui lòng xem bài đăng của tôi về hồi quy trong SQL Server 2012 và 2014 có thể - trong một số trường hợp hiếm gặp - giới thiệu mất mát hoặc hỏng dữ liệu tiềm ẩn trong quá trình xây dựng lại chỉ mục trực tuyến. Có sẵn các bản cập nhật tích lũy cho SQL Server 2012 SP1 và SP2 và cả SQL Server 2014. Sẽ không có bản sửa lỗi nào cho nhánh RTM 2012.

Thử nghiệm thêm

Tôi có những thứ khác trong danh sách của tôi để kiểm tra. Đối với một, tôi muốn xem liệu cờ theo dõi này có bất kỳ ảnh hưởng nào đối với các loại bảng Trong Bộ nhớ trong SQL Server 2014. Tôi cũng sẽ chứng minh vượt ra ngoài sự nghi ngờ rằng cờ theo dõi 2453 sử dụng cùng một ngưỡng biên dịch lại cho bảng biến và TVP như đối với bảng #temp.


  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Cách cài đặt Kubernetes bằng Kubeadm

  2. Tìm các cột được trả về bởi một hàm có giá trị bảng (Ví dụ T-SQL)

  3. Các cải tiến tiềm năng cho ASPState

  4. Hiểu các kiểu dữ liệu SQL - Tất cả những gì bạn cần biết về kiểu dữ liệu SQL

  5. Tiện ích xác minh cụm tạo ra số lượng lớn tệp xml trên hệ thống tệp “/ u01”.