Tại sao một truy vấn CHỌN gây ra ghi?


34

Tôi đã nhận thấy rằng trên máy chủ chạy SQL Server 2016 SP1 CU6 đôi khi phiên Sự kiện mở rộng hiển thị truy vấn CHỌN gây ra ghi. Ví dụ:

nhập mô tả hình ảnh ở đây

Kế hoạch thực hiện cho thấy không có nguyên nhân rõ ràng nào cho việc ghi, chẳng hạn như bảng băm, bộ đệm hoặc sắp xếp có thể tràn sang TempDB:

nhập mô tả hình ảnh ở đây

Việc gán biến cho loại MAX hoặc cập nhật thống kê tự động cũng có thể gây ra điều này, nhưng cả hai đều không phải là nguyên nhân của việc ghi trong trường hợp này.

Những gì khác có thể được viết từ?

Câu trả lời:


8

Hậu đậu

Tôi không thể nhớ nếu tôi đưa những điều này vào câu trả lời ban đầu của mình , thì đây là một cặp vợ chồng khác.

Ống cuốn!

SQL Server có rất nhiều cuộn chỉ khác nhau, đó là các cấu trúc dữ liệu tạm thời được lưu trữ trong tempdb. Hai ví dụ là các cuộn bảng và Index.

Khi chúng xảy ra trong một kế hoạch truy vấn, việc ghi vào các cuộn đó sẽ được liên kết với truy vấn.

QUẢ HẠCH

Những điều này cũng sẽ được đăng ký như viết trong DMV, profiler, XE, v.v.

Chỉ mục Spool

QUẢ HẠCH

Bảng Spool

QUẢ HẠCH

Rõ ràng, số lượng ghi được thực hiện sẽ tăng lên với kích thước của dữ liệu được lưu trữ, rõ ràng.

Tràn

Khi SQL Server không có đủ bộ nhớ cho các toán tử nhất định, nó có thể tràn một số trang vào đĩa. Điều này chủ yếu xảy ra với các loại và băm. Bạn có thể thấy điều này trong các kế hoạch thực hiện thực tế và trong các phiên bản mới hơn của máy chủ SQL, sự cố tràn cũng được theo dõi trong dm_exec_query_stats .

SELECT deqs.sql_handle,
       deqs.total_spills,
       deqs.last_spills,
       deqs.min_spills,
       deqs.max_spills
FROM sys.dm_exec_query_stats AS deqs
WHERE deqs.min_spills > 0;

QUẢ HẠCH

QUẢ HẠCH

Theo dõi

Bạn có thể sử dụng một phiên XE tương tự như phiên tôi đã sử dụng ở trên để xem những điều này trong bản demo của riêng bạn.

CREATE EVENT SESSION spools_and_spills
    ON SERVER
    ADD EVENT sqlserver.sql_batch_completed
    ( ACTION ( sqlserver.sql_text ))
    ADD TARGET package0.event_file
    ( SET filename = N'c:\temp\spools_and_spills' )
    WITH ( MAX_MEMORY = 4096KB,
           EVENT_RETENTION_MODE = ALLOW_SINGLE_EVENT_LOSS,
           MAX_DISPATCH_LATENCY = 1 SECONDS,
           MAX_EVENT_SIZE = 0KB,
           MEMORY_PARTITION_MODE = NONE,
           TRACK_CAUSALITY = OFF,
           STARTUP_STATE = OFF );
GO

38

Trong một số trường hợp, Kho lưu trữ truy vấn có thể khiến việc ghi xảy ra như một hiệu ứng của câu lệnh chọn và trong cùng một phiên.

Điều này có thể được sao chép như sau:

USE master;
GO
CREATE DATABASE [Foo];
ALTER DATABASE [Foo] SET QUERY_STORE (OPERATION_MODE = READ_WRITE, 
  CLEANUP_POLICY = (STALE_QUERY_THRESHOLD_DAYS = 30), 
  DATA_FLUSH_INTERVAL_SECONDS = 900, 
  INTERVAL_LENGTH_MINUTES = 60, 
  MAX_STORAGE_SIZE_MB = 100, 
  QUERY_CAPTURE_MODE = ALL, 
  SIZE_BASED_CLEANUP_MODE = AUTO);
USE Foo;
CREATE TABLE Test (a int, b nvarchar(max));
INSERT INTO Test SELECT 1, 'string';

Tạo một phiên Sự kiện mở rộng để theo dõi:

CREATE EVENT SESSION [Foo] ON SERVER 
ADD EVENT sqlserver.rpc_completed(SET collect_data_stream=(1)
    ACTION(sqlserver.client_app_name,sqlserver.client_hostname,sqlserver.client_pid,sqlserver.database_name,sqlserver.is_system,sqlserver.server_principal_name,sqlserver.session_id,sqlserver.session_server_principal_name,sqlserver.sql_text)
    WHERE ([writes]>(0))),
ADD EVENT sqlserver.sql_batch_completed(SET collect_batch_text=(1)
    ACTION(sqlserver.client_app_name,sqlserver.client_hostname,sqlserver.client_pid,sqlserver.database_name,sqlserver.is_system,sqlserver.server_principal_name,sqlserver.session_id,sqlserver.session_server_principal_name,sqlserver.sql_text)
    WHERE ([writes]>(0)))
ADD TARGET package0.event_file(SET filename=N'C:\temp\FooActivity2016.xel',max_file_size=(11),max_rollover_files=(999999))
WITH (MAX_MEMORY=32768 KB,EVENT_RETENTION_MODE=ALLOW_MULTIPLE_EVENT_LOSS,MAX_DISPATCH_LATENCY=30 SECONDS,MAX_EVENT_SIZE=0 KB,MEMORY_PARTITION_MODE=NONE,TRACK_CAUSALITY=ON,STARTUP_STATE=OFF);

Tiếp theo chạy như sau:

WHILE @@TRANCOUNT > 0 COMMIT
SET IMPLICIT_TRANSACTIONS ON;
SET NOCOUNT ON;
GO
DECLARE @b nvarchar(max);
SELECT @b = b FROM dbo.Test WHERE a = 1;
WAITFOR DELAY '00:00:01.000';
GO 86400

Một giao dịch ngầm có thể hoặc không cần thiết để tái tạo điều này.

Theo mặc định, vào đầu giờ tiếp theo, công việc thu thập số liệu thống kê của Cửa hàng Truy vấn sẽ ghi dữ liệu. Điều này dường như (đôi khi?) Xảy ra như một phần của truy vấn người dùng đầu tiên được thực hiện trong giờ. Phiên Sự kiện mở rộng sẽ hiển thị một cái gì đó tương tự như sau:

nhập mô tả hình ảnh ở đây

Nhật ký giao dịch cho thấy việc ghi đã xảy ra:

USE Foo;
SELECT [Transaction ID], [Begin Time], SPID, Operation, 
  [Description], [Page ID], [Slot ID], [Parent Transaction ID] 
FROM sys.fn_dblog(null,null) 
/* Adjust based on contents of your transaction log */
WHERE [Transaction ID] IN ('0000:0000042c', '0000:0000042d', '0000:0000042e')
OR [Parent Transaction ID] IN ('0000:0000042c', '0000:0000042d', '0000:0000042e')
ORDER BY [Current LSN];

nhập mô tả hình ảnh ở đây

Kiểm tra trang với các DBCC PAGEchương trình cho thấy sys.plan_persist_runtime_stats_interval.

USE Foo;
DBCC TRACEON(3604); 
DBCC PAGE(5,1,344,1); SELECT
OBJECT_NAME(229575856);

Lưu ý rằng các mục nhật ký hiển thị ba giao dịch lồng nhau nhưng chỉ có hai bản ghi cam kết. Trong một tình huống tương tự trong sản xuất, điều này dẫn đến một thư viện khách hàng bị lỗi được cho là đã sử dụng các giao dịch ngầm bất ngờ bắt đầu một giao dịch ghi, ngăn chặn nhật ký giao dịch bị xóa. Thư viện được viết để chỉ phát hành một cam kết sau khi chạy cập nhật, chèn hoặc xóa câu lệnh, vì vậy nó không bao giờ ban hành lệnh cam kết và để lại một giao dịch ghi.


25

Có một thời điểm khác khi điều này có thể xảy ra, và đó là với một bản cập nhật thống kê tự động.

Đây là phiên XE chúng ta sẽ xem xét:

CREATE EVENT SESSION batches_and_stats
    ON SERVER
    ADD EVENT sqlserver.auto_stats
    ( ACTION ( sqlserver.sql_text )),
    ADD EVENT sqlserver.sql_batch_completed
    ( ACTION ( sqlserver.sql_text ))
    ADD TARGET package0.event_file
    ( SET filename = N'c:\temp\batches_and_stats' )
    WITH ( MAX_MEMORY = 4096KB,
           EVENT_RETENTION_MODE = ALLOW_SINGLE_EVENT_LOSS,
           MAX_DISPATCH_LATENCY = 30 SECONDS,
           MAX_EVENT_SIZE = 0KB,
           MEMORY_PARTITION_MODE = NONE,
           TRACK_CAUSALITY = OFF,
           STARTUP_STATE = OFF );
GO

Sau đó, chúng tôi sẽ sử dụng điều này để thu thập thông tin:

USE tempdb

DROP TABLE IF EXISTS dbo.SkewedUp

CREATE TABLE dbo.SkewedUp (Id INT NOT NULL, INDEX cx_su CLUSTERED (Id))

INSERT dbo.SkewedUp WITH ( TABLOCK ) ( Id )
SELECT CASE WHEN x.r % 15 = 0 THEN 1
            WHEN x.r % 5 = 0 THEN 1000
            WHEN x.r % 3 = 0 THEN 10000
            ELSE 100000
       END AS Id
FROM   (   SELECT     TOP 1000000 ROW_NUMBER() OVER ( ORDER BY @@DBTS ) AS r
           FROM       sys.messages AS m
           CROSS JOIN sys.messages AS m2 ) AS x;


ALTER EVENT SESSION [batches_and_stats] ON SERVER STATE = START

SELECT su.Id, COUNT(*) AS records
FROM dbo.SkewedUp AS su
WHERE su.Id > 0
GROUP BY su.Id

ALTER EVENT SESSION [batches_and_stats] ON SERVER STATE = STOP

Một số kết quả thú vị từ Phiên XE:

QUẢ HẠCH

Bản cập nhật thống kê tự động không hiển thị bất kỳ ghi nào, nhưng truy vấn hiển thị một ghi ngay sau khi cập nhật thống kê.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.