一:背景

1. 讲故事

前段时间有位朋友找到我,说他们在查询报表的时候发现程序的稳定性会受到影响,但服务器的内存,CPU都是正常的,让我帮忙看下怎么回事,问了下程序的稳定性指的是什么?指的是卡死,那既然是卡死,就抓一个卡死的dump吧。

二:Windbg 分析

1. 当前是什么程序

不同的程序类型分析卡死的思路是不一样的,如果是 WKS模式 可以看下主线程,如果是 SRV模式 就要看其他线程了,接下来用 !eeversion 验证下。


0:000> !eeversion
5.0.521.16609 free
5,0,521,16609 @Commit: 2f740adc1457e8a28c1c072993b66f515977eb51
Server mode with 16 gc heaps
SOS Version: 6.0.5.7301 retail build

从卦中看当前是 .NET5 写的 Web网站,那就需要看其他线程都在做什么了。

2. 不稳定因素在哪里

朋友前面也说了查询报表的时候导致程序不稳定,因为这个所以要找SQL查询的 ,大概率问题在数据库端,接下来使用 ~*e !clrstack 观察所有线程栈,输出如下:


OS Thread Id: 0x24e8 (60)
Child SP IP Call Site
000000C2E3BFC700 00007ffd72955d04 [InlinedCallFrame: 000000c2e3bfc700] Microsoft.Data.SqlClient.SNINativeMethodWrapper.SNIReadSyncOverAsync(Microsoft.Data.SqlClient.SNIHandle, IntPtr ByRef, Int32)
000000C2E3BFC700 00007ffce5e00264 [InlinedCallFrame: 000000c2e3bfc700] Microsoft.Data.SqlClient.SNINativeMethodWrapper.SNIReadSyncOverAsync(Microsoft.Data.SqlClient.SNIHandle, IntPtr ByRef, Int32)
000000C2E3BFC6D0 00007ffce5e00264 ILStubClass.IL_STUB_PInvoke(Microsoft.Data.SqlClient.SNIHandle, IntPtr ByRef, Int32)
000000C2E3BFC7A0 00007ffce7a3ca1f Microsoft.Data.SqlClient.TdsParserStateObjectNative.ReadSyncOverAsync(Int32, UInt32 ByRef)
000000C2E3BFC7F0 00007ffce7a3c72c Microsoft.Data.SqlClient.TdsParserStateObject.ReadSniSyncOverAsync()
000000C2E3BFC880 00007ffce7a3c60b Microsoft.Data.SqlClient.TdsParserStateObject.TryReadNetworkPacket()
000000C2E3BFC8C0 00007ffce7a3c53e Microsoft.Data.SqlClient.TdsParserStateObject.TryPrepareBuffer()
000000C2E3BFC900 00007ffce79de54e Microsoft.Data.SqlClient.TdsParserStateObject.TryReadByte(Byte ByRef)
000000C2E3BFC940 00007ffce5e06bce Microsoft.Data.SqlClient.TdsParser.TryRun(Microsoft.Data.SqlClient.RunBehavior, Microsoft.Data.SqlClient.SqlCommand, Microsoft.Data.SqlClient.SqlDataReader, Microsoft.Data.SqlClient.BulkCopySimpleResultSet, Microsoft.Data.SqlClient.TdsParserStateObject, Boolean ByRef)
000000C2E3BFCAE0 00007ffce5e38d51 Microsoft.Data.SqlClient.SqlDataReader.TryConsumeMetaData()
000000C2E3BFCB50 00007ffce7a94f77 Microsoft.Data.SqlClient.SqlDataReader.get_MetaData()
.... OS Thread Id: 0x854 (72)
Child SP IP Call Site
000000C2E66BBE70 00007ffd72955d04 [InlinedCallFrame: 000000c2e66bbe70] Microsoft.Data.SqlClient.SNINativeMethodWrapper.SNIReadSyncOverAsync(Microsoft.Data.SqlClient.SNIHandle, IntPtr ByRef, Int32)
000000C2E66BBE70 00007ffce5e00264 [InlinedCallFrame: 000000c2e66bbe70] Microsoft.Data.SqlClient.SNINativeMethodWrapper.SNIReadSyncOverAsync(Microsoft.Data.SqlClient.SNIHandle, IntPtr ByRef, Int32)
000000C2E66BBE40 00007ffce5e00264 ILStubClass.IL_STUB_PInvoke(Microsoft.Data.SqlClient.SNIHandle, IntPtr ByRef, Int32)
000000C2E66BBF10 00007ffce7a3ca1f Microsoft.Data.SqlClient.TdsParserStateObjectNative.ReadSyncOverAsync(Int32, UInt32 ByRef)
000000C2E66BBF60 00007ffce7a3c72c Microsoft.Data.SqlClient.TdsParserStateObject.ReadSniSyncOverAsync()
000000C2E66BBFF0 00007ffce7a3c60b Microsoft.Data.SqlClient.TdsParserStateObject.TryReadNetworkPacket()
000000C2E66BC030 00007ffce7a3c53e Microsoft.Data.SqlClient.TdsParserStateObject.TryPrepareBuffer()
000000C2E66BC070 00007ffce79de54e Microsoft.Data.SqlClient.TdsParserStateObject.TryReadByte(Byte ByRef)
000000C2E66BC0B0 00007ffce5e06bce Microsoft.Data.SqlClient.TdsParser.TryRun(Microsoft.Data.SqlClient.RunBehavior, Microsoft.Data.SqlClient.SqlCommand, Microsoft.Data.SqlClient.SqlDataReader, Microsoft.Data.SqlClient.BulkCopySimpleResultSet, Microsoft.Data.SqlClient.TdsParserStateObject, Boolean ByRef)
000000C2E66BC250 00007ffce5e38d51 Microsoft.Data.SqlClient.SqlDataReader.TryConsumeMetaData()
000000C2E66BC2C0 00007ffce7a94f77 Microsoft.Data.SqlClient.SqlDataReader.get_MetaData()
...

从卦中看当前有两个线程在请求SQLSERVER,并在等待返回结果,接下来问题就来了,既然说卡死肯定花费了不少时间,所以对当前是什么 SQL 产生了好奇,我们可以提取下 72 号线程的 SqlCommand 对象,看看是什么sql?


0:072> !dso
OS Thread Id: 0x854 (72)
RSP/REG Object Name
000000C2E66BBDC8 00000216eafe2520 Microsoft.Data.SqlClient.SqlCommand 0:072> !DumpObj /d 00000216eafe2520
Name: Microsoft.Data.SqlClient.SqlCommand
00007ffce4027a90 400046d 18 System.String 0 instance 00000216eafe1a60 _commandText

然后 do 一下 _commandText 就可以了,去敏之后的SQL如下;


SELECT xxx
FROM [xxxxxx] AS [d]
LEFT JOIN [xxxDrugs] AS [n]
ON [d].[DrugId] = [n].[xxx]
WHERE [d].[xxxId] = '4f1be71f-2edb-4a5c-87dc-9ab3b981cbca'

上面的sql也就一个简单的表关联,但我的职业敏感性告诉我,这条sql应该就是没加 nolock 导致在 SQLSERVER 层面一直获取不到 S 锁,获取不到 S 锁的原因应该就是 报表 间接导致很多的意向U和意向X在相关表上成了 U 和 X 锁。

3. 到底卡了多久

怀疑是 SQLSERVER 层面的问题后,接下来还要找证据验证一下,也就是当前这条SQL到底阻塞了多久? 那怎么在 Dump 中观察 SQL 的请求时间呢?这就需要考察你对 SQLConnection 类的理解了,其中有一个字段 SqlConnection._innerConnection._createTime._dateData 就记录了 SQL 的开始时间。


0:072> !DumpObj /d 00000216eaf85fa0
Name: Microsoft.Data.SqlClient.SqlConnection
00007ffce61bb9f8 40004c6 90 ...onnectionInternal 0 instance 00000215cad017c0 _innerConnection 0:072> !DumpObj /d 00000215cad017c0
Name: Microsoft.Data.SqlClient.SqlInternalConnectionTds
00007ffce42c7670 4000174 48 System.DateTime 1 instance 00000215cad01808 _createTime 0:072> !DumpVC /d 00007ffce42c7670 00000215cad01808
Name: System.DateTime
Fields:
MT Field Offset Type VT Attr Value Name
00007ffce3f6cf98 4000258 0 System.UInt64 1 instance 5249873640594058230 _dateData

卦中的 0n5249873640594058230 怎么转化为具体时间呢?这又是考验你的基础知识了,这个数的前两位bit记录的是 时区信息 ,比如本地还是UTC,参考代码如下:


internal DateTime(long ticks, DateTimeKind kind, bool isAmbiguousDst)
{
if (ticks < 0 || ticks > 3155378975999999999L)
{
throw new ArgumentOutOfRangeException("ticks", SR.ArgumentOutOfRange_DateTimeBadTicks);
}
_dateData = (ulong)ticks | (isAmbiguousDst ? 13835058055282163712uL : 9223372036854775808uL);
}

上面的两个超大十进制数转化为二进制即高位的 11 和 10 开头,也就是十六进制开头的 c 和 8 。


0:072> ? 0n13835058055282163712
Evaluate expression: -4611686018427387904 = c0000000`00000000 0:072> ? 0n9223372036854775808
Evaluate expression: -9223372036854775808 = 80000000`00000000

那怎么将 _dateData 转成 ticks 呢?在源码中也有说明,即抹掉二进制中的高二位。


internal long InternalTicks => (long)(_dateData & 0x3FFFFFFFFFFFFFFFL);

有了这些前置基础,接下来就可以用 windbg 转换了。


0:072> ? 0n5249873640594058230 & 0x3FFFFFFFFFFFFFFF
Evaluate expression: 638187622166670326 = 08db4c42`d74babf6 0:072> .formats 08db4c42`d74babf6
Evaluate expression:
Hex: 08db4c42`d74babf6
Decimal: 638187622166670326
Decimal (unsigned) : 638187622166670326
Octal: 0043332304132722725766
Binary: 00001000 11011011 01001100 01000010 11010111 01001011 10101011 11110110
Chars: ..LB.K..
Time: Thu May 4 09:56:56.667 3623 (UTC + 8:00)
Float: low -2.23939e+014 high 1.31985e-033
Double: 5.29119e-266 0:072> .time
Debug session time: Thu May 4 09:58:39.000 2023 (UTC + 8:00)
System Uptime: 40 days 10:15:42.713
Process Uptime: 0 days 16:55:48.000
Kernel time: 0 days 0:01:54.000
User time: 0 days 0:39:40.000

从卦中看,SQL 的请求时间是 09:56:56 ,Dump抓取时间为 09:58:39,也就表示当前这条 SQL 已经等待了 1分43秒,这确实是不可容忍的。

从 dump 中挖到这些信息后,让朋友重点观察下 SQLSERVER 端,比如在卡住的时候查下锁相关的 DMV视图,是不是出现了锁等待,也可以加上 nolock 尝试一下。

最终朋友在 SQLSERVER 层面修改了 max degree of parallelism 来提高并发度,说情况缓解了很多,这其中细节,熟悉 SQLSERVER 的朋友可以留言解答一下哈。

三:总结

这次不稳定的事故从直观上就能猜到可能是 SQLSERVER 层面导致的问题,但需要证据,所以我们需要用 windbg 在这小小的花园里,挖呀挖呀挖! 让朋友对外有数据层面的说服力。

记一次 .NET 某药材管理系统 卡死分析的更多相关文章

  1. 记一次 .NET 某药品仓储管理系统 卡死分析

    一:背景 1. 讲故事 这个月初,有位朋友wx上找到我,说他的api过一段时间后,就会出现只有请求,没有响应的情况,截图如下: 从朋友的描述中看样子程序是被什么东西卡住了,这种卡死的问题解决起来相对简 ...

  2. 记一次 .NET 某化妆品 webapi 卡死分析

    一:背景 1. 讲故事 10月份星球里的一位老朋友找到我,说他们公司的程序在一个网红直播带货下给弄得无响应了,无响应期间有大量的 RabbitMQ 超时,寻求如何找到根源,聊天截图我就不发了. 既然无 ...

  3. 记一次 .NET某汽车零件采集系统 卡死分析

    一:背景 1. 讲故事 前段时间有位朋友在微信上找到我,说他的程序会出现一些偶发卡死的情况,让我帮忙看下是怎么回事,刚好朋友也抓到了dump,就让朋友把 dump 丢给我,接下来用 windbg 探究 ...

  4. 记一次 .NET 某物管后台服务 卡死分析

    一:背景 1. 讲故事 这几个月经常被朋友问,为什么不更新这个系列了,哈哈,确实停了好久,主要还是打基础去了,分析 dump 的能力不在于会灵活使用 windbg,而是对底层知识有一个深厚的理解,比如 ...

  5. 基于UML的中职班主任工作管理系统的分析与设计--文献随笔(二)

    一.基本信息 标题:基于UML的中职班主任工作管理系统的分析与设计 时间:2016 出版源:遵义航天工业学校 关键字:中职学校; 班主任工作管理; UML建模 二.研究背景 问题定义:班主任是一项特殊 ...

  6. 基于UML的毕业设计管理系统的分析与设计

    基于UML的毕业设计管理系统的分析与设计 <本段与标题无关,自行略过 最近各种忙,天气不错,导师心情不错:“我们要写一个关于UML的专著”,一句话:“一个完整的系统贯穿整个UML的知识”:我:“ ...

  7. 记C++课程设计--学生信息管理系统

                                                                                  C++课程设计--学生信息管理系统     ...

  8. 记一次 .NET 某风控管理系统 内存泄漏分析

    一:背景 1. 讲故事 上个月中旬,星球里的一位朋友在微信找我,说他的程序跑着跑着内存会不断的缓慢增长并无法释放,寻求如何解决 ? 得,看样子星球还得好好弄!!! 不管怎么说,先上 windbg 说话 ...

  9. 记一次 .NET 某工控自动化控制系统 卡死分析

    一:背景 1. 讲故事 前段时间遇到了好几起关于窗体程序的 进程加载锁 引发的 程序卡死 和 线程暴涨 问题,这种 dump 分析难度较大,主要涉及到 Windows操作系统 和 C++ 的基础知识, ...

  10. 记一次 .NET 某金融企业 WPF 程序卡死分析

    一:背景 1. 讲故事 前段时间遇到了一个难度比较高的 dump,经过几个小时的探索,终于给找出来了,在这里做一下整理,希望对大家有所帮助,对自己也是一个总结,好了,老规矩,上 WinDBG 说话. ...

随机推荐

  1. pcm音频的录制、播放及转换

    操作系统 :Windows10_x64 pcm格式为原始音频数据,有时候会遇到需要录制.播放及转换的情况,这里记录下. 一.录制pcm音频 这里演示下使用Audacity进行pcm音频录音的过程. A ...

  2. python之zipfile应用

    zipfile Python 中 zipfile 模块提供了对 zip 压缩文件的一系列操作. 1 f=zipfile.ZipFile("test.zip",mode=" ...

  3. 新旧版本功能对比 | v1.5.0 全新升级

    Hi~社区的小伙伴们大家好呀! CloudQuery 最新 1.5.0 社区版本即将于 4月14日 发布,正式上线前,我们迫不及待与大家分享与 v1.4 相比,v1.5.0 在性能和功能上有哪些更新和 ...

  4. 当前标识(IIS APPPOOL\derl)没有对“C:\Windows\Microsoft.NET\Framework64\v4.0.30319\Temporary ASP.NET Files”的写访问权限。

    解决方法:运行cmd 执行以下指令 1.检查用户组,找到iis相关的用户组, 2.将电脑登录用户添加到该组 3.为指定的用户或用户组授予权限,使其可以访问 IIS 元数据库和 ASP.NET 使用的其 ...

  5. opengauss配置远程白名单

    DB_VERSION:openGauss 3.0.3 1.允许192.168网段用户使用jack用户登陆 --创建只读账号 CREATE USER jack WITH MONADMIN passwor ...

  6. 电脑上跨平台的电子书阅读器Koodo Reader

    https://wbsu2003.gitee.io/2021/04/30/%E7%94%B5%E8%84%91%E4%B8%8A%E8%B7%A8%E5%B9%B3%E5%8F%B0%E7%9A%84 ...

  7. RDIFramework.NET Web版报表管理-助力企业高效智能图表

    功能描述 在RDIFramework.NET Web版本中全新的报表管理功能模块,非常实用的功能,重量级推荐.主要用于对日常常用的报表做定制展示.可以自动发布到模块(就可授权给指定资源访问),在报表定 ...

  8. Java学习笔记06

    1. 类和对象 1.1 类和对象 ​ 客观存在的事物皆为对象,所以我们也常常说万物皆对象. 类 类的理解 类是对现实生活中一类具有共同属性和行为的事物的抽象 类是对象的数据类型,类是具有相同属性和行为 ...

  9. 自学C#,要懂得善用MSDN

    很多初学者学习编程,都会通过看别人写的教程.或者录制的视频,来学习. 这是一个非常好的途径,因为这个是非常高效的. 但是这样,存在两个问题: 1.教程不够全面:任何再好的教程,都无法囊括所有的知识点, ...

  10. IE盒模型和标准盒模型之间的差别

    1.W3C标准盒子模型 w3c盒子模型的范围包括margin.border.padding.content,并且content部分不包含其他部分 2.IE盒子模型 IE盒子模型的范围包括margin. ...