TEMPDB 데이터베이스의 트랜잭션 로그를 실제로 채우는 정확한 쿼리 또는 저장된 proc을 식별하는 방법을 알고 싶습니다.
TEMPDB 데이터베이스의 트랜잭션 로그를 실제로 채우는 정확한 쿼리 또는 저장된 proc을 식별하는 방법을 알고 싶습니다.
답변:
에서 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에서 공간을 차지하는 활성 트랜잭션 을 찾지 못하고 현재 공간을 사용하는 활성 쿼리 만 찾습니다 (현재 로그 사용에 대한 범인 일 가능성이 있음). 따라서 열린 트랜잭션이있을 수 있지만 문제를 일으키는 실제 쿼리는 더 이상 실행되지 않습니다.
당신은 변경할 수 inner join
에 sys.dm_exec_requests
A를 left outer join
다음 현재 적극적으로 쿼리를 실행하지 않는 세션에 대한 행을 반환합니다.
검색어 Martin이 게시했습니다 ...
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
로그 공간을 차지하는 활성 트랜잭션 이있는을 식별 할 수 있지만 문제점을 발생시킨 실제 쿼리를 반드시 판별 할 수있는 것은 아닙니다. 지금 실행되지 않으면 위의 쿼리에서 캡처되지 않기 때문입니다. 활성 요청. 다음을 사용하여 가장 최근의 쿼리를 반응 적으로 확인할 수 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 로그 사용은 데이터베이스 메일, 이벤트 알림, 쿼리 알림 및 서비스 브로커와 같은 거의 제어 할 수없는 내부 프로세스로 인해 발생할 수 있다고 생각할 수 있습니다. 이러한 기능의 사용을 중지 할 수 있지만 사용중인 경우 tempdb를 사용하는 방법과시기를 지정할 수 없습니다.
session_id
다음 쿼리와 함께 표시 되지만 내 위반 세션을 찾지 못했습니다 SELECT database_transaction_log_bytes_reserved,session_id FROM sys.dm_tran_database_transactions tdt JOIN sys.dm_tran_session_transactions tst ON tdt.transaction_id = tst.transaction_id WHERE database_id = 2
. 내가 찾은 쿼리는 다음을 실행 한 후였습니다BEGIN TRAN CREATE TABLE #T(X CHAR(8000)) INSERT INTO #T SELECT name FROM sys.objects
@@SPID
것입니다 <>
하지 =
. 나를 위해 모든 열에 대한 공개 트랜잭션이있는 spid에 대한 dm_db_task_space_usage
보고서 0
. 오픈 트랜잭션으로 유휴 상태가 아닌 요청이 실제로 실행될 때 쿼리해야하는지 궁금합니다.
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
이 게시물에 대해 감사합니다. 아마 유일한 종류 일 것입니다. 내 테스트는 간단하고 임시 테이블을 만들고이 게시물의 쿼리를 실행할 때 표시되는지 확인하십시오. 한두 가지만 실제로 성공했습니다. T-SQL에 가입하도록 수정하고 더 오래 실행되도록 최적화했으며 매우 유용했습니다. 내가 놓친 것이 있으면 알려주세요. 지금까지 자동화 된 / 루프 된 스크립트가 있습니다. 아래 표준 편차 (STDEV) 쿼리를 사용하여 일정 기간 동안 어떤 쿼리 / SPID가 가해자인지 평가하는 방법을 제공합니다.
이것은 3 분마다 40 번씩 실행되므로 2 시간이 걸립니다. 원하는대로 매개 변수를 수정하십시오.
작은 테이블이 많은 경우에 대비하여 사람들이 지우고 싶을 수있는 50 페이지보다 큰 WHERE 필터가 있습니다. 그렇지 않으면 아래 내용을 그대로 뉘앙스로 잡을 수 없습니다.
즐겨!
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에서 실행중인 것을 수집해야합니다. 어쩌면 누군가 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)