Nous avons une grande et assez bien réglée SQL Server 2016 Enterprise Edition. Compte tenu du nombre de cœurs que nous avons, notre tempdb
est actuellement composé de 16 fichiers de 2 950 Mo exécutés sur un disque RAM avec un fichier journal de transaction de 16 Go. Tous les fichiers sont définis pour ne pas autogrow et, en règle générale, cela a fonctionné bien.
Nous avons récemment commencé à obtenir un "journal de transaction aléatoire pour tempdb
la base de données est pleine" Erreurs. Il n'y a pas de temps défini lorsque cela se produit, il s'agit probablement d'une interaction de l'utilisateur. Étant donné que toutes les interactions sont via des procédures stockées, il est probablement un ensemble étrange de paramètres ou de données qui causent le problème, mais nous ne pouvons pas suivre exactement ce qui pourrait le causer. Toute suggestion qui pourrait nous aider à identifier le coupable est appréciée.
J'utilise les informations dans Comment identifier quelle requête remplit le journal des transactions TEMPDB? pour suivre cette descente. Et cela donne des informations précieuses, mais cela ne me donne pas un moyen de déterminer en réalité les circonstances erronées qui causent le journal de transaction à remplir.
Y a-t-il un moyen de déclencher un instantané ou de loger ce qui cause réellement le problème. Le pire des cas, je peux écrire un programme de surveillance qui pinge le serveur chaque demi-seconde et utilise des variations sur ces requêtes pour capturer tout ce qui est énorme et une transaction ouverte, mais qui ne garantit toujours pas que je tiens tout ce qui cause la question.
Vous pouvez utiliser une alerte d'agent SQL Server pour effectuer automatiquement une opération chaque fois que le journal de transaction traverse un seuil de pourcentage d'occasion.
À titre d'exemple, les résultats suivants courent automatiquement les résultats de l'une des questions de la réponse de Aaron Bertrand sur une question sur la manière d'identifier quelle requête remplit le journal des transactions Tempdb chaque fois que le journal de transaction TEMPDB devient 80% complet.
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