背景

SQLServer Agent作为Windows服务提供给用户定期执行管理任务,这些任务被称为Job;考虑应用镜像的场景如何解决Job同步问题,AWS RDS的做法是不予理会,由用户维护Job,假如发生切换用户需要在新的Principal端创建Job;另一种做法是镜像端保持同步Job,切换后尽量让用户无感知不需要多余维护动作,但这种做法在某些情况会遇到非常严重的问题——内存耗尽。

问题排查分析

第一次分析

问题发生时实例的ERRORLOG出现:

Error: 701, Severity: 17, State: 123.

并且记录了 MEMORYSTATUS,根据 MEMORYSTATUS 的信息本身已经可以做推断,但既然有现场 我们不妨用DAC(Dedicated Administrator Connection)看一下:

SELECT
TOP 10 [type],
sum ( single_pages_kb) as [SPA MEM/KB],
SUM(multi_pages_kb) AS [MPA MEM/KB]
FROM sys.dm_os_memory_clerks
GROUP BY type
ORDER BY SUM ( single_pages_kb+multi_pages_kb) DESC ;

内存占用

占用最多的 OBJECTSTORE_SNI_PACKET、MEMORYCLERK_SQLCONNECTIONPOOL 一般跟连接数、network packet size有关;

select
c.session_id,
c.net_packet_size,
s.host_name as client_host_name,
s.program_name,
s.client_interface_name
from sys.dm_exec_connections c
join sys.dm_exec_sessions s
on c.session_id = s.session_id
join sys.endpoints e
on c.endpoint_id = e.endpoint_id
order by c.net_packet_size desc

net_packet_size

select value_in_use from sys.configurations where configuration_id=1544

select program_name,count(*) as conn_num from sys.sysprocesses where spid>50 group by program_name order by conn_num desc

内存规格

1W+的链接保持,根据sys.dm_exec_sessions.program_name可以确认都来自SQLServer Agent,每个session的packet size是4K,实例的max server memory是1G,所以出现内存耗尽;

那么这1W个链接在干嘛?根据sys.dm_exec_sessions.program_name中暴露的job_id我们找到对应的Job,先看下这个Job要做什么;

select * from msdb.dbo.sysjobs where job_id=0x825F84340AFD5B4BA1D5AD82A8E76C1A

Job信息

第一次推论

这部分内容涉及业务逻辑我不再贴出,重点是Job_Step使用的DATABASE是镜像中的(RESTORING),因为镜像的DB不可用,Schedule又设置的过于频繁,所以SQLServer Agent没能及时释放这部分链接导致内存耗尽。

第一次验证

重新搭建了一个测试环境,制造类似的场景但问题并没能复现。感兴趣的小伙伴可以测试下:镜像 + Mirror端Job + Job链接镜像库且频度调成10s + Job内容不限(为什么不限后续通过Profiler可以看出)。

借助Profiler和Session相关视图我们可以看出,当Job-Step的链接DB设置为镜像DB时,会出现:

Error: 18456, Severity: 14, State: 38.

表示账号校验成功但数据库不可访问或登录权限不够,SQLServer Agent会重试多次,但最后都会释放链接,这跟之前的推论不符。

Profiler信息

select program_name,count(*) as conn_num from sys.sysprocesses where spid>50 group by program_name order by conn_num desc

connection

第二次分析

回到原现场,我们再做分析;尝试复现时我们发现完整的链接都无法建立,但这1W个链接是如何做到的:

select spid,program_name,loginame,login_time as time,db_name(dbid) as dbname from sys.sysprocesses where spid>50

dbname

根据spid我们看一下未释放的链接最后执行的SQL是什么:

inputbuffer

(@P1 nvarchar(max),@P2 uniqueidentifier,@P3 int,@P4 int)DECLARE @logTextWithPreAndPost nvarchar(max) set @logTextWithPreAndPost = N'' + @P1 + N''; EXECUTE msdb.dbo.sp_write_sysjobstep_log @job_id = @P2, @step_id = @P3, @log_text=@logTextWithPreAndPost, @append_to_last=@P4

看下 msdb.dbo.sp_write_sysjobstep_log这个存储过程:

exec sp_helptext 'sp_write_sysjobstep_log'

	Text
-------------------------------------------------------------------------------
CREATE PROCEDURE sp_write_sysjobstep_log
@job_id UNIQUEIDENTIFIER,
@step_id INT,
@log_text NVARCHAR(MAX),
@append_to_last INT = 0
AS
BEGIN
DECLARE @step_uid UNIQUEIDENTIFIER
DECLARE @log_already_exists int
SET @log_already_exists = 0 SET @step_uid = ( SELECT step_uid FROM msdb.dbo.sysjobsteps
WHERE (job_id = @job_id)
AND (step_id = @step_id) ) IF(EXISTS(SELECT * FROM msdb.dbo.sysjobstepslogs
WHERE step_uid = @step_uid ))
BEGIN
SET @log_already_exists = 1
END --Need create log if "overwrite is selected or log does not exists.
IF (@append_to_last = 0) OR (@log_already_exists = 0)
BEGIN
-- flag is overwrite --if overwrite and log exists, delete it
IF (@append_to_last = 0 AND @log_already_exists = 1)
BEGIN
-- remove previous logs entries
EXEC sp_delete_jobsteplog @job_id, NULL, @step_id, NULL
END INSERT INTO msdb.dbo.sysjobstepslogs
(
log,
log_size,
step_uid
)
VALUES
(
@log_text,
DATALENGTH(@log_text),
@step_uid
)
END
ELSE
BEGIN
DECLARE @log_id INT
--Selecting TOP is just a safety net - there is only one log entry row per step.
SET @log_id = ( SELECT TOP 1 log_id FROM msdb.dbo.sysjobstepslogs
WHERE (step_uid = @step_uid)
ORDER BY log_id DESC ) -- Append @log_text to the existing log record. Note that if this
-- action would make the value of the log column longer than
-- nvarchar(max), then the engine will raise error 599.
UPDATE msdb.dbo.sysjobstepslogs
SET
log .WRITE(@log_text,NULL,0),
log_size = DATALENGTH(log) + DATALENGTH(@log_text) ,
date_modified = getdate()
WHERE log_id = @log_id
END RETURN(@@error) -- 0 means success END

MSDN 没有找到详尽 Document,但看完定义也可以确认它是Job-Step做Advanced配置时会用到的一个存储过程,作用是把Job-Step日志写到表 msdb.dbo.sysjobstepslogs 中,根据参数的不同可能会overwrite或append;

log_to_table

第二次推论

根据之前的信息我们可以推测出,这1W+空闲链接是由于执行完Job-Step后,内部更新msdb的日志表,更新完成后链接未释放。

第二次验证

构造的场景跟第一次基本相同,只需要增加一点的是Job-Step开启了log to table。

验证成功

结果稳定复现,40s左右新增一个链接(Schedule 10s 不开启Step Retry attempts 和 Retry interval),根据msdb.dbo.sysjobstepslogs.log_size和msdb.dbo.sysjobstepslogs.log可以确认日志的更新频度在40s。

开启Profiler、不断调整Schedule时间、监控msdb.dbo.sysjobstepslogs.log_size大小,可以发现,当调度频度大于40s时,新增链接按照频度增加,当频度小于40s时,新增链接按照40s一个增加。

结论

如果Job-Step中定义的链接 DATABASE 是镜像库(RESTORING)且配置了Log To Table,那么每次做日志记录的Session(msdb.dbo.sysjobstepslogs)都不会自动关闭,即Agent在这种场景下存在链接泄露。

 

SQLServer · BUG分析 · Agent 链接泄露分析(转载)的更多相关文章

  1. xcode怎样分析检测内存泄露(iOS)

    虽然iOS 5.0版本之后加入了ARC机制,由于相互引用关系比较复杂时,内存泄露还是可能存在.所以了解原理很重要. 这里讲述在没有ARC的情况下,如何使用Instruments来查找程序中的内存泄露, ...

  2. 详解服务器性能测试的全生命周期?——从测试、结果分析到优化策略(转载)

    服务器性能测试是一项非常重要而且必要的工作,本文是作者Micheal在对服务器进行性能测试的过程中不断摸索出来的一些实用策略,通过定位问题,分析原因以及解决问题,实现对服务器进行更有针对性的优化,提升 ...

  3. 鸿蒙内核源码分析(静态链接篇) | 完整小项目看透静态链接过程 | 百篇博客分析OpenHarmony源码 | v54.01

    百篇博客系列篇.本篇为: v54.xx 鸿蒙内核源码分析(静态链接篇) | 完整小项目看透静态链接过程 | 51.c.h.o 下图是一个可执行文件编译,链接的过程. 本篇将通过一个完整的小工程来阐述E ...

  4. 关于内存泄露分析插件 MAT 的用法

    关于内存泄露分析插件 MAT 的用法,建议大家有时间看一下,下面的文章 http://www.blogjava.net/rosen/archive/2010/05/21/321575.html htt ...

  5. 学会用Clang来进行内存泄露分析

    最近项目出现了内存泄露的问题,对于PC x86平台来说,一点点的内存泄露往往不会出错,很难进行debug调试.这个时候我们可以用到苹果给我们带来的神器--Clang编译器来进行内存泄露分析检测,开关打 ...

  6. 偶发异常BUG,如何高效精准分析排查定位?

    偶发异常BUG,如何高效精准分析排查定位? 作为测试,经常会收到领导.同事.用户反馈过来各种各样BUG,令人措手不及 首选需要判断确认是不是BUG,不要急于给予回复,需有充分的条件给予说明回复 很多测 ...

  7. 性能分析工具gprof介绍(转载)

    性能分析工具gprof介绍Ver:1.0 目录1. GPROF介绍 42. 使用步骤 43. 使用举例 43.1 测试环境 43.2 测试代码 43.3 数据分析 53.3.1 flat profil ...

  8. Okhttp之RealConnection建立链接简单分析

    在之前的博客中我们知道Okhttp在发起链接请求先从链接池中获取连接,如果链接池中没有链接则创建新的链接RealConnection对象,然后执行其connet方法打开SOCKET链接(详见< ...

  9. QQ链接病毒分析

    QQ链接病毒分析 特征 点击病毒链接后,自动会在每一时刻范围内通过所有途径群发新的病毒链接(途径包括Qzone,群聊等) 分析 首先看一下病毒链接的一个样例 http://news.soso.com/ ...

随机推荐

  1. 保护眼睛-eclipse黑色背景设置

    eclipse中java编辑器颜色改动,适合程序员人群: 长时间编码,眼睛是有非常大负担的,特别是使用eclipse,它自带的java编辑器背景色是刺眼的白色.代码颜色基本是黑色,这样一个编辑器里白色 ...

  2. 基于Windows Azure 安装 SharePoint 2010简体中文语言包

    在Windows Azure上安装的Windows Server默认是英文版本的,当时安装的SharePoint也是英文版的,为方便使用,决定安装中文的语言包,具体过程如下: 1. 安装 Window ...

  3. PL/SQL Developer使用技巧、快捷键(转发)

    转发自:https://www.cnblogs.com/linjiqin/archive/2013/06/24/3152538.html PL/SQL Developer使用技巧.快捷键 1.类SQL ...

  4. Codeforces Round #281 (Div. 2) D. Vasya and Chess 博弈

    D. Vasya and Chess   Vasya decided to learn to play chess. Classic chess doesn't seem interesting to ...

  5. Navicat Lite 提示Connection to mysql server on 10065

    Navicat Lite 提示Connection to mysql server on 10065 验证过主要是防火墙问题 [root@014136251035 zhop]# vi /etc/sys ...

  6. ubuntu下一款有点感觉的 linux音乐播放器 clementine(小橘子))

    https://www.clementine-player.org/ 在linux听音乐的感觉确实不是很好,音乐播放器很多.但是仅仅只是数量上的优势,在确实不是很好用.自带的rhythmbox确实很占 ...

  7. poj2342 Anniversary party (树形dp)

    poj2342 Anniversary party (树形dp) Time Limit: 1000MS   Memory Limit: 65536K Total Submissions: 9128   ...

  8. C# 读取ini文件 百度问问学习文档

    C# 读取ini文件 10 有多个section,现想读取整个ini文件和指定section下所有内容 补充: 发布答案可以,请对准题目啊,我不要指定节点的内容,我知道!我要的是读取指定区域的内容,假 ...

  9. spring:使用<prop>标签为Java持久属性集注入值

    spring:使用<prop>标签为Java持久属性集注入值 使用 spring 提供的<prop>为Java持久属性集注入值,也就是向 java.util.Propertie ...

  10. autofac的小知识点

    autofac 注入中i遇到的泛型传参问题 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 ...