系统城装机大师 - 固镇县祥瑞电脑科技销售部宣传站!

当前位置:首页 > 数据库 > MsSql > 详细页面

SQL Server邮件相关SQL语句出现严重的ASYNC_NETWORK_IO等待事件案例

时间:2020-09-27来源:www.pcxitongcheng.com作者:电脑系统城

DPA监控发现一台SQL Server服务器最近两天执行系统存储过程msdb.dbo.sp_MailItemResultSets中的某个SQL时,出现较严重的ASYNC_NETWORK_IO等待。如下截图所示
 
clip_image001
 
进一步分析发现,主要是执行存储过程msdb.dbo.sp_MailItemResultSets中下面这段SQL语句出现ASYNC_NETWORK_IO等待
 
SELECT 
      mi.mailitem_id,
      mi.profile_id,
      (SELECT name FROM msdb.dbo.sysmail_profile p WHERE p.profile_id = mi.profile_id) as 'profile_name',
      mi.recipients,
      mi.copy_recipients,
      mi.blind_copy_recipients,
      mi.subject,
      mi.body, 
      mi.body_format, 
      mi.importance,
      mi.sensitivity,
      ISNULL(sr.send_attempts, 0) as retry_attempt,
      ISNULL(mi.from_address, '') as from_address,
      ISNULL(mi.reply_to, '')     as reply_to
   FROM sysmail_mailitems as mi
      LEFT JOIN sysmail_send_retries as sr
         ON sr.mailitem_id = mi.mailitem_id 
   WHERE mi.mailitem_id = @mailitem_id
 
进一步分析,发现随便一个与表sysmail_allitems有关的SQL都会出现严重的ASYNC_NETWORK_IO等待:
 
SELECT * FROM msdb.dbo.sysmail_allitems WITH(NOLOCK)
WHERE sent_status != 'sent' 
ORDER BY sent_date DESC;
 
另外,分析过程中发现sysmail_mailitems表只有7万多条记录,但是表的Size大小接近10G大小,如下截图所示:
 
 
clip_image002
 
 
这显然明显不正常,sysmail_mailitems中肯定有一些超大的邮件记录,因为这个系统经常有通过SQL,生成一些报表数据发送给用户。于是我想检查一下是否真的有一些超大的邮件。这里就必须查看sysmail_mailitems表的行大小,于是用下面脚本查看表sysmail_mailitems中行记录的大小。
 
USE msdb;
GO

 
IF object_id('sp_GetRowSize') is not null
drop procedure sp_GetRowSize
GO
CREATE procedure sp_GetRowSize(@Tablename varchar(100),@pkcol varchar(100))
AS 
BEGIN
declare @dynamicsql varchar(MAX)

 
-- A @pkcol can be used to identify max/min length row
set @dynamicsql = 'select ' + @PkCol +' , (0'

 
-- traverse each record and calculate the datalength
select @dynamicsql = @dynamicsql + ' + isnull(datalength(' + name + '), 1)' 
    from syscolumns where id = object_id(@Tablename)
set @dynamicsql = @dynamicsql + ') as rowsize from ' + @Tablename + ' order by 2 desc'

 

 
print (@dynamicsql)

 
END
 
 
如下截图所示,还真的有一些邮件记录的rowsize超级大,正常情况下,rowsize只有1122个字节左右大小,而mailitem_id=5146768 这条记录居然有1352285196字节,如果换算成大小的话SELECT 1352285196.0/1024/1024 ~=1290M, 真是无语了!!
 
 
EXEC sp_GetRowSize 'sysmail_mailitems', 'mailitem_id'
 
clip_image003
 
 
  原因倒也不复杂,就是生成邮件的SQL出现逻辑错误,导致邮件的Body变得无比巨大,导致msdb.dbo.sysmail_allitems变得非常大,与之相关的SQL语句IO性能变差,出现ASYNC_NETWORK_IO等待。其实以前也遇到过类似案例,请见SQL Server 2008 R2执行存储过程sp_MailItemResultSets引起大量PREEMPTIVE_OS_WAITFORSINGLEOBJEC等待,只是当时没有继续深挖Root Cause而已!
 
 
 
解决方案
 
那么如何解决这个问题呢? 很简单,就是删除表msdb.dbo.sysmail_allitems中的记录,让其Size变小。也可以删除那些mail_id非常大的记录。可以用下面脚本处理
 
/******************************************************************************************************
    Script Function        :    以下示例按条件删除数据库邮件系统中的电子邮件
*******************************************************************************************************/
DECLARE @GETDATE datetime  
SET @GETDATE = GETDATE()-2;  
EXECUTE msdb.dbo.sysmail_delete_mailitems_sp @sent_before = @GETDATE;  
GO  
 
其实sysmail_delete_mailitems_sp中的逻辑也是去删除msdb.dbo.sysmail_allitems 中的记录,如下所示,在处理的过程中,需要在业务空闲的时候处理,否则会引起大量阻塞。
 
DELETE 
FROM msdb.dbo.sysmail_allitems 
WHERE ((@sent_before IS NULL) 
OR ( send_request_date < @sent_before)) 
AND ((@sent_status IS NULL) 
OR (sent_status = @sent_status))
 
 
CREATE PROCEDURE  
sysmail_delete_mailitems_sp  
@sent_before DATETIME = NULL,  -- sent before
  
@sent_status varchar(8) = NULL -- sent status
  
AS  
   BEGIN  
      SET @sent_status = LTRIM(RTRIM(@sent_status))  
      IF @sent_status = ''  
      SET @sent_status = NULL  
      IF ( (@sent_status IS NOT NULL) AND  
      (LOWER(@sent_status collate SQL_Latin1_General_CP1_CS_AS) NOT IN ( 'unsent', 'sent', 'failed' 
      , 'retrying') ) )  
      BEGIN  
         RAISERROR(14266, -1, -1, '@sent_status', 'unsent, sent, failed, retrying')  
         RETURN(1) -- Failure
  
      END  
      IF ( @sent_before IS NULL AND @sent_status IS NULL )  
      BEGIN  
         RAISERROR(14608, -1, -1, '@sent_before', '@sent_status')  
         RETURN(1) -- Failure
  
      END 
/* BEGIN ACTIVE SECTION (comment inserted by DPA) */  
      DELETE  
      FROM msdb.dbo.sysmail_allitems  
      WHERE ((@sent_before IS NULL)  
      OR ( send_request_date < @sent_before))  
     AND ((@sent_status IS NULL)  
      OR (sent_status = @sent_status)) 
/* END ACTIVE SECTION (comment inserted by DPA) */  
         DECLARE @localmessage nvarchar(255)  
         SET @localmessage = FORMATMESSAGE(14665, SUSER_SNAME(), @@ROWCOUNT) exec  
         msdb.dbo.sysmail_logmailevent_sp @event_type=1,  
         @description=@localmessage  
      END 
 
 
分享到:

相关信息

  • SQL Server 数据库中的收缩数据库和文件操作

    收缩数据文件通过将数据页从文件末尾移动到更靠近文件开头的未占用的空间来恢复空间,在文件末尾创建足够的空间后,可取消对文件末尾的数据页的分配并将它们返回给文件系统,本文给大家介绍SQL Server 数据库中的收缩数据...

    2023-10-27

  • dbeaver配置SQL server连接实现

    一、需要java jdk环境,我用的比较新 二、dbeaver新建连接SQL server...

    2023-10-27

系统教程栏目

栏目热门教程

人气教程排行

站长推荐

热门系统下载