背景

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. 9.JAVA编程思想 多形性

    欢迎转载,转载请标明出处:http://blog.csdn.net/notbaron/article/details/51040241 "对于面向对象的程序设计语言,多型性是第三种最主要的特 ...

  2. HDU1573 X问题【一元线性同余方程组】

    题目链接: http://acm.hdu.edu.cn/showproblem.php? pid=1573 题目大意: 求在小于等于N的正整数中有多少个X满足:X mod a[0] = b[0], X ...

  3. 计算cost--全表扫描

    以下教大家怎样手工算出oracle运行计划中的cost值. 成本的计算方式例如以下: Cost = (        #SRds * sreadtim +        #MRds * mreadti ...

  4. EF学习笔记——生成自定义实体类

    使用EF,采用DataBase 模式,实体类都是按照数据库的定义自动生成,我们似乎无法干预.如果要生成自定义的实体类,该怎么做呢? 思路是这样的: 1.我们要自定义生成的实体类,都是分部类(parti ...

  5. 【BZOJ 1598】 牛跑步

    [题目链接] https://www.lydsy.com/JudgeOnline/problem.php?id=1598 [算法] A*求k短路 [代码] #include<bits/stdc+ ...

  6. PCB MS CLR 聚合函数 joinString加排序实现

    准备着手,动态列SQL查询,要实现动态列SQL,会运用到聚合函数,而SQL本身聚合函数有限, 无满足此用户任意字段组合变化,再加上工艺流程重复相同工序,如;沉铜1,沉铜2对应工序代码都是相同的 而通常 ...

  7. SpringBoot + Redis:基本配置及使用

    注:本篇博客SpringBoot版本为2.1.5.RELEASE,SpringBoot1.0版本有些配置不适用 一.SpringBoot 配置Redis 1.1 pom 引入spring-boot-s ...

  8. C - Anton and Danik

    Problem description Anton likes to play chess, and so does his friend Danik. Once they have played n ...

  9. Spring Cloud (3) 服务消费者-Ribbon

    在上一篇中使用LoadBalancerClient接口实现了获取某个服务的具体实例,并根据实例信息发起服务接口消费请求.但是这样的做法需要我们手工的区编写服务选取.连接拼接等繁琐的工作,对于开发人员来 ...

  10. VMware中linux安装jdk

    首先安装linux系统 如何将jdk安装包复制到linux中不做概述,可以使用xftp工具,或者Xshell,或者其他方式. 1.下载jdk包:本章使用的为后缀为tar.gz的文件(不需要安装),如j ...