TEMPDBデータベースのトランザクションログを実際にいっぱいにしている正確なクエリまたはストアドプロシージャを識別する方法を知りたいのですが。
から http://www.sqlservercentral.com/scripts/tempdb/72007/
;WITH task_space_usage AS (
-- SUM alloc/delloc pages
SELECT session_id,
request_id,
SUM(internal_objects_alloc_page_count) AS alloc_pages,
SUM(internal_objects_dealloc_page_count) AS dealloc_pages
FROM sys.dm_db_task_space_usage WITH (NOLOCK)
WHERE session_id <> @@SPID
GROUP BY session_id, request_id
)
SELECT TSU.session_id,
TSU.alloc_pages * 1.0 / 128 AS [internal object MB space],
TSU.dealloc_pages * 1.0 / 128 AS [internal object dealloc MB space],
EST.text,
-- Extract statement from sql text
ISNULL(
NULLIF(
SUBSTRING(
EST.text,
ERQ.statement_start_offset / 2,
CASE WHEN ERQ.statement_end_offset < ERQ.statement_start_offset
THEN 0
ELSE( ERQ.statement_end_offset - ERQ.statement_start_offset ) / 2 END
), ''
), EST.text
) AS [statement text],
EQP.query_plan
FROM task_space_usage AS TSU
INNER JOIN sys.dm_exec_requests ERQ WITH (NOLOCK)
ON TSU.session_id = ERQ.session_id
AND TSU.request_id = ERQ.request_id
OUTER APPLY sys.dm_exec_sql_text(ERQ.sql_handle) AS EST
OUTER APPLY sys.dm_exec_query_plan(ERQ.plan_handle) AS EQP
WHERE EST.text IS NOT NULL OR EQP.query_plan IS NOT NULL
ORDER BY 3 DESC;
[〜#〜]編集[〜#〜]
Martinがコメントで指摘したように、これはtempdbのスペースを占有しているアクティブなtransactionsを見つけられず、アクティブなクエリが現在スペースを使用しています(現在のログ使用の原因となる可能性があります)。したがって、開いているトランザクションが存在する可能性がありますが、問題を引き起こす実際のクエリは実行されていません。
inner join
のsys.dm_exec_requests
をleft outer join
に変更すると、現在クエリをアクティブに実行していないセッションの行が返されます。
マーティンが投稿したクエリ...
SELECT database_transaction_log_bytes_reserved,session_id
FROM sys.dm_tran_database_transactions AS tdt
INNER JOIN sys.dm_tran_session_transactions AS tst
ON tdt.transaction_id = tst.transaction_id
WHERE database_id = 2;
...ログ領域を占有しているアクティブなトランザクションがあるsession_id
sを識別しますが、現在実行されていない場合はキャプチャされないため、必ずしも問題の原因となった実際のクエリを特定できるとは限りません。上記のアクティブなリクエストのクエリ。 DBCC INPUTBUFFER
を使用すると、最新のクエリを事後的にチェックできる可能性がありますが、聞きたい情報が得られない場合があります。同様に外部結合して、アクティブに実行されているものをキャプチャできます。
SELECT tdt.database_transaction_log_bytes_reserved,tst.session_id,
t.[text], [statement] = COALESCE(NULLIF(
SUBSTRING(
t.[text],
r.statement_start_offset / 2,
CASE WHEN r.statement_end_offset < r.statement_start_offset
THEN 0
ELSE( r.statement_end_offset - r.statement_start_offset ) / 2 END
), ''
), t.[text])
FROM sys.dm_tran_database_transactions AS tdt
INNER JOIN sys.dm_tran_session_transactions AS tst
ON tdt.transaction_id = tst.transaction_id
LEFT OUTER JOIN sys.dm_exec_requests AS r
ON tst.session_id = r.session_id
OUTER APPLY sys.dm_exec_sql_text(r.plan_handle) AS t
WHERE tdt.database_id = 2;
DMV sys.dm_db_session_space_usage
を使用して、セッションごとの全体的なスペース使用率を確認することもできます(ただし、クエリに対して有効な結果が返されない場合があります。クエリがアクティブでない場合、返されるのは実際の原因ではない可能性があります) 。
;WITH s AS
(
SELECT
s.session_id,
[pages] = SUM(s.user_objects_alloc_page_count
+ s.internal_objects_alloc_page_count)
FROM sys.dm_db_session_space_usage AS s
GROUP BY s.session_id
HAVING SUM(s.user_objects_alloc_page_count
+ s.internal_objects_alloc_page_count) > 0
)
SELECT s.session_id, s.[pages], t.[text],
[statement] = COALESCE(NULLIF(
SUBSTRING(
t.[text],
r.statement_start_offset / 2,
CASE WHEN r.statement_end_offset < r.statement_start_offset
THEN 0
ELSE( r.statement_end_offset - r.statement_start_offset ) / 2 END
), ''
), t.[text])
FROM s
LEFT OUTER JOIN
sys.dm_exec_requests AS r
ON s.session_id = r.session_id
OUTER APPLY sys.dm_exec_sql_text(r.plan_handle) AS t
ORDER BY s.[pages] DESC;
これらすべてのクエリを自由に使用できるので、tempdbを使用しているユーザーとその方法を絞り込むことができるはずです。特に、それらを行為で見つけた場合はそうです。
SORT_IN_TEMPDB
オプションを回避するTempdbログの使用は、ほとんど制御できない、またはまったく制御できない内部プロセスが原因である可能性があると考えることもできます。たとえば、データベースメール、イベント通知、クエリ通知、Service Brokerはすべて、何らかの方法でtempdbを使用します。これらの機能の使用を停止できますが、それらを使用している場合は、tempdbをいつどのように使用するかを指示することはできません。
SELECT tst.[session_id],
s.[login_name] AS [Login Name],
DB_NAME (tdt.database_id) AS [Database],
tdt.[database_transaction_begin_time] AS [Begin Time],
tdt.[database_transaction_log_record_count] AS [Log Records],
tdt.[database_transaction_log_bytes_used] AS [Log Bytes Used],
tdt.[database_transaction_log_bytes_reserved] AS [Log Bytes Rsvd],
SUBSTRING(st.text, (r.statement_start_offset/2)+1,
((CASE r.statement_end_offset
WHEN -1 THEN DATALENGTH(st.text)
ELSE r.statement_end_offset
END - r.statement_start_offset)/2) + 1) AS statement_text,
st.[text] AS [Last T-SQL Text],
qp.[query_plan] AS [Last Plan]
FROM sys.dm_tran_database_transactions tdt
JOIN sys.dm_tran_session_transactions tst
ON tst.[transaction_id] = tdt.[transaction_id]
JOIN sys.[dm_exec_sessions] s
ON s.[session_id] = tst.[session_id]
JOIN sys.dm_exec_connections c
ON c.[session_id] = tst.[session_id]
LEFT OUTER JOIN sys.dm_exec_requests r
ON r.[session_id] = tst.[session_id]
CROSS APPLY sys.dm_exec_sql_text (c.[most_recent_sql_handle]) AS st
OUTER APPLY sys.dm_exec_query_plan (r.[plan_handle]) AS qp
WHERE DB_NAME (tdt.database_id) = 'tempdb'
ORDER BY [Log Bytes Used] DESC
GO
この投稿をありがとう、おそらくその種の唯一のもの。私のテストは単純で、一時テーブルを作成し、この投稿のクエリを実行したときに表示されることを確認します。本当に成功したのは1つまたは2つだけです。 T-SQLに結合するように修正し、長時間実行できるように最適化して、かなり使いやすくしました。私が何かを逃したかどうか私に知らせてください、しかし今のところあなたは自動化された/ループされたスクリプトを得ました。以下の標準偏差(STDEV)クエリを使用して、どのクエリ/ SPIDが一定期間攻撃者であるかを評価する方法を提供します。
これは3分ごとに40回実行されるため、2時間かかります。必要に応じてパラメーターを変更します。
小さなテーブルがたくさんある場合に備えて、人々はその下にWHERE> 50ページのフィルターをクリアしたいと思うかもしれません。さもなければ、あなたはそのままで以下でそのニュアンスをキャッチしません...
楽しい!
DECLARE @minutes_apart INT; SET @minutes_apart = 3
DECLARE @how_many_times INT; SET @how_many_times = 40
--DROP TABLE tempdb..TempDBUsage
--SELECT * FROM tempdb..TempDBUsage
--SELECT session_id, STDEV(pages) stdev_pages FROM tempdb..TempDBUsage GROUP BY session_id HAVING STDEV(pages) > 0 ORDER BY stdev_pages DESC
DECLARE @delay_string NVARCHAR(8); SET @delay_string = '00:' + RIGHT('0'+ISNULL(CAST(@minutes_apart AS NVARCHAR(2)), ''),2) + ':00'
DECLARE @counter INT; SET @counter = 1
SET NOCOUNT ON
if object_id('tempdb..TempDBUsage') is null
begin
CREATE TABLE tempdb..TempDBUsage (
session_id INT, pages INT, num_reads INT, num_writes INT, login_time DATETIME, last_batch DATETIME,
cpu INT, physical_io INT, hostname NVARCHAR(64), program_name NVARCHAR(128), text NVARCHAR (MAX)
)
end
else
begin
PRINT 'To view the results run this:'
PRINT 'SELECT * FROM tempdb..TempDBUsage'
PRINT 'OR'
PRINT 'SELECT session_id, STDEV(pages) stdev_pages FROM tempdb..TempDBUsage GROUP BY session_id HAVING STDEV(pages) > 0 ORDER BY stdev_pages DESC'
PRINT ''
PRINT ''
PRINT 'Otherwise manually drop the table by running the following, then re-run the script:'
PRINT 'DROP TABLE tempdb..TempDBUsage'
RETURN
end
--GO
TRUNCATE TABLE tempdb..TempDBUsage
PRINT 'To view the results run this:'; PRINT 'SELECT * FROM tempdb..TempDBUsage'
PRINT 'OR'; PRINT 'SELECT session_id, STDEV(pages) stdev_pages FROM tempdb..TempDBUsage GROUP BY session_id HAVING STDEV(pages) > 0 ORDER BY stdev_pages DESC'
PRINT ''; PRINT ''
while @counter <= @how_many_times
begin
INSERT INTO tempdb..TempDBUsage (session_id,pages,num_reads,num_writes,login_time,last_batch,cpu,physical_io,hostname,program_name,text)
SELECT PAGES.session_id, PAGES.pages, r.num_reads, r.num_writes, sp.login_time, sp.last_batch, sp.cpu, sp.physical_io, sp.hostname, sp.program_name, t.text
FROM sys.dm_exec_connections AS r
LEFT OUTER JOIN master.sys.sysprocesses AS sp on sp.spid=r.session_id
OUTER APPLY sys.dm_exec_sql_text(r.most_recent_sql_handle) AS t
LEFT OUTER JOIN (
SELECT s.session_id, [pages] = SUM(s.user_objects_alloc_page_count + s.internal_objects_alloc_page_count)
FROM sys.dm_db_session_space_usage AS s
GROUP BY s.session_id
HAVING SUM(s.user_objects_alloc_page_count + s.internal_objects_alloc_page_count) > 0
) PAGES ON PAGES.session_id = r.session_id
WHERE PAGES.session_id IS NOT NULL AND PAGES.pages > 50
ORDER BY PAGES.pages DESC;
PRINT CONVERT(char(10), @counter) + ': Ran at: ' + CONVERT(char(30), GETDATE())
SET @counter = @counter + 1
waitfor delay @delay_string
end
残念ながら、実行中のプロセスを表示してtempDBログを直接sessionIDにトレースすることはできません。
TempDBログファイルを再度大きくなるポイントまで縮小します。次に、拡張イベントを作成して、ログの増加をキャプチャします。再び大きくなると、拡張イベントを展開して、パッケージイベントファイルを表示できます。ファイルを開き、時間フィルター、ファイルタイプフィルター(データファイルの結果を含めたくない)を追加し、SSMSでセッションIDでグループ化します。これは、グループIDが最も多いセッションIDを探しているときに、原因を特定するのに役立ちます。もちろん、セッションIDで実行されているものを別のプロセスまたはツールを介して収集する必要があります。多分誰かがquery_hash列からクエリを取得する方法を知っていて、ソリューションを投稿するのに十分親切でしょう。
拡張イベントの結果:
拡張イベントを作成するスクリプト:
CREATE EVENT SESSION [tempdb_file_size_changed] ON SERVER ADD EVENT
sqlserver.database_file_size_change(SET collect_database_name=(1)ACTION(sqlserver.client_app_name,sqlserver.client_hostname,sqlserver.is_system,sqlserver.query_hash,sqlserver.session_id,sqlserver.session_nt_username,sqlserver.sql_text,sqlserver.username) WHERE ([database_id]=(2))) ADD TARGETpackage0.event_file(SET filename=N'C:\ExtendedEvents\TempDBGrowth.xel',max_file_size=(100),max_rollover_files=(25)) WITH (MAX_MEMORY=4096 KB,EVENT_RETENTION_MODE=ALLOW_SINGLE_EVENT_LOSS,MAX_DISPATCH_LATENCY=1 SECONDS,MAX_EVENT_SIZE=0 KB,MEMORY_PARTITION_MODE=NONE,TRACK_CAUSALITY=OFF,STARTUP_STATE=ON)