Почему запрос SELECT вызывает запись?

34

Я заметил, что на сервере под управлением SQL Server 2016 SP1 CU6 иногда сеанс расширенных событий показывает запрос SELECT, вызывающий запись. Например:

введите описание изображения здесь

План выполнения не показывает очевидной причины для записи, такой как хеш-таблица, спул или сортировка, которые могут перетекать в TempDB:

введите описание изображения здесь

Присвоение переменной типу MAX или автоматическое обновление статистики также может быть причиной этого, но в данном случае это не является причиной записи.

От чего еще могут быть записи?

Джеймс Л
источник

Ответы:

8

топорный

Я не мог вспомнить, включил ли я их в свой первоначальный ответ , так что вот еще пара.

Катушки!

SQL Server имеет много разных катушек, которые являются временными структурами данных, хранящимися в базе данных tempdb. Два примера - это таблицы и катушки индекса.

Когда они происходят в плане запроса, записи в эти катушки будут связаны с запросом.

NUTS

Они также будут зарегистрированы как записи в DMV, профилировщике, XE и т. Д.

Index Spool

NUTS

Настольная шпуля

NUTS

Очевидно, что количество выполненных записей будет увеличиваться с размером данных в буфере.

Разливы

Когда SQL Server не получает достаточно памяти для определенных операторов, он может пролить некоторые страницы на диск. Это в первую очередь происходит с сортировками и хэшами. Это можно увидеть в реальных планах выполнения, а в более новых версиях сервера SQL разливы также отслеживаются в 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;

NUTS

NUTS

отслеживание

Вы можете использовать сеанс XE, подобный тому, который я использовал выше, чтобы увидеть их в своих собственных демонстрациях.

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

В некоторых случаях Query Store может вызывать запись как результат оператора select и в том же сеансе.

Это может быть воспроизведено следующим образом:

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

Создайте сеанс расширенных событий для мониторинга:

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

Далее запустите следующее:

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

Неявная транзакция может или не может быть необходимо воспроизвести это.

По умолчанию в начале следующего часа задание сбора статистики в Query Store будет записывать данные. Похоже, что это (иногда?) Происходит как часть первого пользовательского запроса, выполненного в течение часа. Сессия расширенных событий покажет что-то похожее на следующее:

введите описание изображения здесь

Журнал транзакций показывает записи, которые произошли:

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

введите описание изображения здесь

Проверка страницы с DBCC PAGEпоказывает, что записи должны sys.plan_persist_runtime_stats_interval.

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

Обратите внимание, что записи журнала показывают три вложенные транзакции, но только две записи фиксации. В аналогичной ситуации на производстве это привело к возможно неисправной клиентской библиотеке, которая использовала неявные транзакции, неожиданно запустив транзакцию записи, не позволяя очистить журнал транзакций. Библиотека была написана так, чтобы выдавать коммит только после выполнения оператора update, insert или delete, поэтому она никогда не выдавала команду commit и оставляла транзакцию записи открытой.

Джеймс Л
источник
25

В другой раз это может случиться, и это с автоматическим обновлением статистики.

Вот сеанс XE, который мы рассмотрим:

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

Тогда мы будем использовать это для сбора информации:

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

Некоторые из интересных результатов XE Session:

NUTS

Автоматическое обновление статистики не показывает никаких записей, но запрос показывает одну запись сразу после обновления статистики.

Эрик Дарлинг
источник