大規模で十分に調整されたSQL Server 2016 Enterprise Editionがあります。私たちが持っているコアの数を考えると、tempdb
は現在、16 GBのトランザクションログファイルを備えたRAMディスク上で実行される16個の2,950 MBファイルで構成されています。すべてのファイルは自動拡張しないように設定されており、原則としてこれは問題なく実行されています。
最近、ランダムな「tempdb
データベースのトランザクションログがいっぱいです」というエラーが発生し始めました。これが発生する時間は決まっていないため、おそらくユーザーの操作によるものです。すべてのやり取りはストアドプロシージャを介して行われるため、問題の原因となっているのは、いくつかの奇妙なパラメーターまたはデータのセットである可能性がありますが、原因を正確に追跡することはできません。犯人を特定するのに役立つ可能性のある提案はありがたいです。
tempdbトランザクションログがいっぱいになっているクエリを特定する方法 の情報を使用して、これを追跡しています。そして、これは貴重な情報を提供しますが、トランザクションログがいっぱいになる原因となっている疑わしい状況を実際に特定する方法を私に与えません。
実際に問題を引き起こしているものをスナップショットまたはログに記録する方法はありますか?最悪の場合、0.5秒ごとにサーバーにpingを送信し、それらのクエリのバリエーションを使用して巨大で開いているトランザクションがあるものをキャプチャする監視プログラムを作成できますが、それでも問題の原因となっているものを実際に検出できるとは限りません。
SQL Serverエージェントアラートを使用して、トランザクションログが使用率のしきい値を超えるたびに、いくつかの操作を自動的に実行できます。
例として、次の例は、tempdbトランザクションログがいっぱいになっているクエリを特定する方法についての質問に対する Aaron Bertrandの回答からのクエリの1つの結果を自動的にメールで送信します tempdbトランザクションログが80%になると必ず発生します。
USE [msdb]
GO
BEGIN TRANSACTION;
DECLARE @ReturnCode INT;
DECLARE @msg nvarchar(1000);
DECLARE @jobId BINARY(16);
DECLARE @DatabaseName sysname;
DECLARE @DBAEmailAddress nvarchar(100);
DECLARE @JobName sysname;
DECLARE @JobCommand nvarchar(max);
DECLARE @PerformanceCondition nvarchar(512);
/*
Change the parameters below to suit
*/
SET @DatabaseName = 'tempdb';
SET @DBAEmailAddress = '<email address here>';
SET @JobCommand = N'SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;
DECLARE @msg_body nvarchar(max) = N'''';
;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 @msg_body = @msg_body + N''<tr><td>'' + CONVERT(nvarchar(10), s.session_id) + N''</td>''
+ N''<td>'' + CONVERT(nvarchar(10), s.[pages]) + N''</td>''
+ N''<td>'' + COALESCE(t.[text], N'''') + N''</td>''
+ N''<td>'' + 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
)
, ''''
)
, COALESCE(t.[text], N'''')) + N''</td></tr>''
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;
SET @msg_body = N''<html><body><table><tr><th>session_id</th><th>pages</th><th>text</th><th>statement text</th></tr>'' + @msg_body + N''</table></body></html>'';
EXEC msdb.dbo.sp_send_dbmail @profile_name = N''DBA''
, @recipients = N''[email protected]''
, @subject = N''tempdb task space usage''
, @body_format = N''HTML''
, @body = @msg_body;
';
SET @ReturnCode = 0;
/*
Add an operator to receive email alerts
*/
IF NOT EXISTS (
SELECT 1
FROM dbo.sysoperators so
WHERE so.name = N'DBA'
)
BEGIN
EXEC msdb.dbo.sp_add_operator @name=N'DBA'
, @enabled = 1
, @email_address = @DBAEmailAddress
, @category_name = N'[Uncategorized]';
IF (@@ERROR <> 0 OR @ReturnCode <> 0) GOTO QuitWithRollback;
SET @msg = N'Added "DBA" operator.';
PRINT @msg;
END
ELSE
BEGIN
SET @msg = N'DBA operator already exists.';
PRINT @msg;
END
/* Add a job category*/
IF NOT EXISTS (
SELECT name
FROM msdb.dbo.syscategories
WHERE name = N'Reliability'
AND category_class = 1
)
BEGIN
EXEC @ReturnCode = msdb.dbo.sp_add_category @class = N'JOB'
, @type = N'LOCAL'
, @name = N'Reliability';
IF (@@ERROR <> 0 OR @ReturnCode <> 0) GOTO QuitWithRollback;
PRINT N'Added "Reliability" job category.';
END
ELSE
BEGIN
SET @msg = N'Job category "Reliability" already exists.';
PRINT @msg;
END
/*
Add a job that performs a backup of the target database's transaction log
This should free up space in the transaction log, assuming nothing else
is preventing re-use of virtual log files, such as transactional replication,
Database Mirroring, participation in an Availability Group, or an open
transaction.
*/
SET @JobName = @DatabaseName + N' - log space usage' ;
IF NOT EXISTS (
SELECT 1
FROM dbo.sysjobs sj
WHERE sj.name = @JobName
)
BEGIN
EXEC @ReturnCode = msdb.dbo.sp_add_job @job_name = @JobName
, @enabled = 1
, @notify_level_eventlog = 3
, @notify_level_email = 2
, @notify_level_netsend = 0
, @notify_level_page = 0
, @delete_level = 0
, @description = N'No description available.'
, @category_name = N'Reliability'
, @owner_login_name = N'sa'
, @notify_email_operator_name = N'DBA'
, @job_id = @jobId OUTPUT;
IF (@@ERROR <> 0 OR @ReturnCode <> 0) GOTO QuitWithRollback;
SET @msg = N'Added "' + @JobName + '" job.';
PRINT @msg;
EXEC @ReturnCode = msdb.dbo.sp_add_jobstep @job_id = @jobId
, @step_name = N'email space usage report'
, @step_id = 1
, @cmdexec_success_code = 0
, @on_success_action = 1
, @on_success_step_id = 0
, @on_fail_action = 2
, @on_fail_step_id = 0
, @retry_attempts = 0
, @retry_interval = 0
, @os_run_priority = 0
, @subsystem = N'TSQL'
, @command = @JobCommand
, @database_name = @DatabaseName
, @flags = 0;
IF (@@ERROR <> 0 OR @ReturnCode <> 0) GOTO QuitWithRollback;
SET @msg = N'Added "email space usage report" job step.';
PRINT @msg;
EXEC @ReturnCode = msdb.dbo.sp_update_job @job_id = @jobId
, @start_step_id = 1;
IF (@@ERROR <> 0 OR @ReturnCode <> 0) GOTO QuitWithRollback;
SET @msg = N'Job is configured to start at step 1.';
PRINT @msg;
EXEC @ReturnCode = msdb.dbo.sp_add_jobserver @job_id = @jobId
, @server_name = N'(local)';
IF (@@ERROR <> 0 OR @ReturnCode <> 0) GOTO QuitWithRollback;
END
ELSE
BEGIN
SET @jobId = (
SELECT sj.job_id
FROM dbo.sysjobs sj
WHERE sj.name = @JobName
);
SET @msg = N'"' + @JobName + N'" job already exists. Using that job''s JobID.';
PRINT @msg;
END
IF @jobId IS NOT NULL
BEGIN
/*
Add an alert to fire the above job whenever the Transaction log crosses 80% full
*/
SET @JobName = @@SERVERNAME + N' ' + @DatabaseName + N' : 80pct TxLog Alert';
IF NOT EXISTS (
SELECT 1
FROM dbo.sysalerts sa
WHERE sa.name = @JobName
)
BEGIN
IF SERVERPROPERTY('InstanceName') IS NOT NULL
BEGIN
SET @PerformanceCondition = N'MSSQL$' + CONVERT(nvarchar(128), SERVERPROPERTY('InstanceName')) + N':Databases|Percent Log Used|' + @DatabaseName + '|>|80'
END
ELSE
BEGIN
SET @PerformanceCondition = N'MSSQL:Databases|Percent Log Used|' + @DatabaseName + '|>|80';
END
EXEC @ReturnCode = msdb.dbo.sp_add_alert @name = @JobName
, @message_id = 0
, @severity = 0
, @enabled = 1
, @delay_between_responses = 300
, @include_event_description_in = 7
, @category_name = N'[Uncategorized]'
, @performance_condition = @PerformanceCondition
, @job_id = @jobId;
IF (@@ERROR <> 0 OR @ReturnCode <> 0) GOTO QuitWithRollback;
SET @msg = N'Added "' + @JobName + '" alert.';
PRINT @msg;
EXEC msdb.dbo.sp_add_notification @alert_name = @JobName
, @operator_name = N'DBA'
, @notification_method = 1;
IF (@@ERROR <> 0 OR @ReturnCode <> 0) GOTO QuitWithRollback;
SET @msg = N'Added job notification to email the DBA.';
PRINT @msg;
END
ELSE
BEGIN
SET @msg = N'Alert already exists.';
PRINT @msg;
END
END
ELSE
BEGIN
SET @msg = @JobName + N' not found. Aborting.';
RAISERROR (@msg, 14, 0) WITH NOWAIT;
END
COMMIT TRANSACTION
GOTO EndSave
QuitWithRollback:
IF (@@TRANCOUNT > 0) ROLLBACK TRANSACTION;
EndSave:
GO