SELECTクエリが書き込みを引き起こすのはなぜですか?


34

SQL Server 2016 SP1 CU6を実行しているサーバーで、拡張イベントセッションに書き込みを引き起こすSELECTクエリが表示されることがあることに気付きました。例えば:

ここに画像の説明を入力してください

実行計画には、TempDBに流出する可能性のあるハッシュテーブル、スプール、並べ替えなど、書き込みの明白な原因は示されていません。

ここに画像の説明を入力してください

MAXタイプへの変数の割り当てまたは自動統計更新もこれを引き起こす可能性がありますが、どちらもこの場合の書き込みの原因ではありませんでした。

書き込みは他に何ができますか?

回答:


8

不器用

これらを元の回答に含めたかどうか思い出せなかったので、別のカップルを紹介します。

スプール!

SQL Serverには、tempdbに格納されている一時的なデータ構造であるさまざまなスプールがたくさんあります。2つの例は、テーブルスプールとインデックススプールです。

クエリプランで発生すると、それらのスプールへの書き込みはクエリに関連付けられます。

ナッツ

これらは、DMV、プロファイラー、XEなどの書き込みとしても登録されます。

インデックススプール

ナッツ

テーブルスプール

ナッツ

実行される書き込みの量は、明らかにスプールされるデータのサイズとともに増加します。

流出

SQL Serverが特定のオペレーターに十分なメモリを取得できない場合、一部のページがディスクに流出する可能性があります。これは主にソートとハッシュで発生します。これは実際の実行計画で見ることができ、SQL Serverの新しいバージョンでは、流出は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;

ナッツ

ナッツ

追跡

上記で使用したセッションと同様の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

場合によっては、クエリストアにより、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

これを再現するために、暗黙のトランザクションが必要な場合とそうでない場合があります。

既定では、クエリストアの統計収集ジョブは次の1時間の上部にデータを書き出します。これは、1時間に実行される最初のユーザークエリの一部として(時々?)発生するようです。拡張イベントセッションには、次のようなものが表示されます。

ここに画像の説明を入力してください

トランザクションログには、発生した書き込みが表示されます。

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

ログエントリには3つのネストされたトランザクションが表示されますが、コミットレコードは2つだけであることに注意してください。本番環境での同様の状況では、これにより、間違いなく暗黙的なトランザクションを使用して書き込みトランザクションを開始し、トランザクションログのクリアを妨げる、間違いのあるクライアントライブラリが発生しました。ライブラリは、更新、挿入、または削除ステートメントの実行後にのみコミットを発行するように作成されているため、コミットコマンドを発行せず、書き込みトランザクションを開いたままにしました。


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セッションからの興味深い結果のいくつか:

ナッツ

自動統計更新では書き込みは表示されませんが、クエリでは統計更新の直後に1つの書き込みが表示されます。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.