记一次 .NET某旅行社酒店管理系统 卡死分析
一:背景
1. 讲故事
年初有位朋友找到我,说他们的管理系统不响应了,让我帮忙看下到底咋回事? 手上也有dump,那就来分析吧。
二:为什么没有响应
1. 线程池队列有积压吗?
朋友的系统是一个web系统,那web系统的无响应我们首先要关注的就是 线程池
,使用 !sos tpq
命令,参考输出如下:
0:000> !sos tpq
global work item queue________________________________
0x00000004010774C0 Microsoft.AspNetCore.Server.IIS.Core.IISHttpContextOfT<Microsoft.AspNetCore.Hosting.HostingApplication+Context>
0x0000000401077808 Microsoft.AspNetCore.Server.IIS.Core.IISHttpContextOfT<Microsoft.AspNetCore.Hosting.HostingApplication+Context>
....
0x000000030239DD78 Microsoft.AspNetCore.Server.IIS.Core.IISHttpContextOfT<Microsoft.AspNetCore.Hosting.HostingApplication+Context>
0x000000030239E0C0 Microsoft.AspNetCore.Server.IIS.Core.IISHttpContextOfT<Microsoft.AspNetCore.Hosting.HostingApplication+Context>
local per thread work items_____________________________________
0x0000000100A46410 System.Threading.Tasks.Task<System.Threading.Tasks.Task>
...
0x000000010133F8C0 System.Threading.Tasks.Task<System.Threading.Tasks.Task>
2 Work Microsoft.AspNetCore.Http.Connections.Internal.HttpConnectionContext+<>c.<WaitOnTasks>b__123_1
4 Work Microsoft.AspNetCore.Http.Connections.Internal.HttpConnectionContext+<>c.<WaitOnTasks>b__123_0
266 Work Microsoft.AspNetCore.SignalR.HubConnectionContext.AbortConnection
----
272
从卦中可以看到确实存在线程池积压的情况,那为什么会有积压呢?条件反射告诉我,是不是因为锁的原因,使用 !syncblk
观察。
0:000> !syncblk
Index SyncBlock MonitorHeld Recursion Owning Thread Info SyncBlock Owner
-----------------------------
Total 468
CCW 0
RCW 0
ComClassFactory 0
Free 120
从卦中看和锁没半毛钱关系,那就只能深入各个消费线程,看看这些线程为什么这么不给力。。。。
2. 线程都在干什么
要想观察各个线程都在做什么,可以用 ~*e !clrstack
观察各个线程调用栈,输出的调用栈太长,但仔细观察之后,发现很多线程都停留在 TryGetConnnection
上,截图如下:
从卦中可以看到大概有143个线程卡在 TryGetConnection 上,不知道 Connection 为啥取不到了,接下来观察问题代码,简化后如下:
private bool TryGetConnection(DbConnection owningObject, uint waitForMultipleObjectsTimeout, bool allowCreate, bool onlyOneCheckConnection, DbConnectionOptions userOptions, out DbConnectionInternal connection)
{
DbConnectionInternal dbConnectionInternal = null;
Transaction transaction = null;
if (HasTransactionAffinity)
{
dbConnectionInternal = GetFromTransactedPool(out transaction);
}
if (dbConnectionInternal == null)
{
Interlocked.Increment(ref _waitCount);
do
{
num = WaitHandle.WaitAny(_waitHandles.GetHandles(allowCreate), (int)waitForMultipleObjectsTimeout);
} while (dbConnectionInternal == null);
}
}
private DbConnectionInternal GetFromTransactedPool(out Transaction transaction)
{
transaction = ADP.GetCurrentTransaction();
DbConnectionInternal dbConnectionInternal = null;
if (null != transaction && _transactedConnectionPool != null)
{
dbConnectionInternal = _transactedConnectionPool.GetTransactedObject(transaction);
//....
}
return dbConnectionInternal;
}
internal DbConnectionInternal GetTransactedObject(Transaction transaction)
{
lock (_transactedCxns)
{
flag = _transactedCxns.TryGetValue(transaction, out value);
}
if (flag)
{
lock (value)
{
int num = value.Count - 1;
if (0 <= num)
{
dbConnectionInternal = value[num];
value.RemoveAt(num);
}
}
}
return dbConnectionInternal;
}
从卦中源码看是 _transactedCxns
返回 null 所致,看样子是_transactedCxns中的Conenction耗尽,接下来使用 !dso
从 DbConnectionPool 字段中去挖,输出如下:
0:298> !dso
OS Thread Id: 0x42c0 (298)
SP/REG Object Name
0005c087d020 0003ffc79528 Microsoft.Data.ProviderBase.DbConnectionPool
0:298> !do 0003ffc79528
Name: Microsoft.Data.ProviderBase.DbConnectionPool
MethodTable: 000007fe8dd714c8
EEClass: 000007fe8dd65438
Tracked Type: false
Size: 176(0xb0) bytes
File: D:\APP\EnergyDotNet\runtimes\win\lib\net6.0\Microsoft.Data.SqlClient.dll
Fields:
MT Field Offset Type VT Attr Value Name
000007fe8dd71370 40001a7 8c System.Int32 1 instance 1 _state
...
000007fe8cd994b0 40001ad 94 System.Int32 1 instance 143 _waitCount
000007fe8e012c68 40001b4 78 ...tedConnectionPool 0 instance 00000003ffc7a3e8 _transactedConnectionPool
...
000007fe8cd994b0 40001b6 9c System.Int32 1 instance 100 _totalObjects
...
0:298> !DumpObj /d 00000003ffc7a520
Name: System.Collections.Generic.Dictionary`2[[System.Transactions.Transaction, System.Transactions.Local],[Microsoft.Data.ProviderBase.DbConnectionPool+TransactedConnectionList, Microsoft.Data.SqlClient]]
MethodTable: 000007fe8e0146a0
EEClass: 000007fe8cd8af30
Tracked Type: false
Size: 80(0x50) bytes
File: C:\Program Files\dotnet\shared\Microsoft.NETCore.App\6.0.22\System.Private.CoreLib.dll
Fields:
MT Field Offset Type VT Attr Value Name
000007fe8cda8480 4002065 8 System.Int32[] 0 instance 0000000000000000 _buckets
000007fe8e3255d8 4002066 10 ...ivate.CoreLib]][] 0 instance 0000000000000000 _entries
000007fe8cd9ca08 4002067 30 System.UInt64 1 instance 0 _fastModMultiplier
000007fe8cd994b0 4002068 38 System.Int32 1 instance 0 _count
000007fe8cd994b0 4002069 3c System.Int32 1 instance 0 _freeList
000007fe8cd994b0 400206a 40 System.Int32 1 instance 0 _freeCount
000007fe8cd994b0 400206b 44 System.Int32 1 instance 0 _version
000007fe8cdb7900 400206c 18 ...Private.CoreLib]] 0 instance 0000000000000000 _comparer
000007fe8da70c90 400206d 20 ...Private.CoreLib]] 0 instance 0000000000000000 _keys
000007fe8d2a1c60 400206e 28 ...Private.CoreLib]] 0 instance 0000000000000000 _values
从上面的卦中数据可知三点信息:
100 _totalObjects
当前的线程池存着100个Connection。0 _count
当前100个Connection全部耗尽。143 _waitCount
表示当前有 143 个线程在获取 Connection 上进行等待。
3. 池中之物都去了哪里
要想找到这个答案,继续观察线程栈,比如搜索TDS传输层方法 Microsoft.Data.SqlClient.TdsParserStateObject.TryReadByte
,可以看到刚好是 100 个,上层大多是 xxxx.GetRoomNosInDltParams
方法,截图如下:
挖掘各个线程栈,大概都是下面的sql,格式化如下:
SELECT [HotelId],
[RoomId],
[SubRoomId],
[SupplierId],
[RoomNo],
[RoomCategory],
[SellPrice],
[RoomNoState],
[CheckInDate]
FROM [xxx]
WHERE ((((((([RoomId] IN (673,674)) AND( [SellPrice] > @SellPrice1 ))
AND ( [CheckInDate] >= @CheckInDate2 ))
AND ( [CheckInDate] <= @CheckInDate3 ))
AND ( [RoomCategory] <=@constant5))
AND ( [RoomNoState] =@constant7))
AND NOT ([RoomNo] LIKE @MethodConst8+'%') )
反编译源码发现有一个 useCached 字段形同虚设,导致每次都是从 数据库 读取,截图如下:
到这里基本上就能推测出来的,程序的卡死主要是 Connection 耗尽,优化建议如下:
- SQL 加上 nolock,避免锁问题。
- GetRoomNosInDltParams 方法尽量使用缓存。
- 观察数据库层的锁和负载情况。
- 设置更大的数据库连接池。
三:总结
这次卡死的生产事故,是大量数据库的慢请求导致SDK侧的数据库连接池(100)耗尽所致,如果有数据库侧的监控工具,我想一眼就能找到答案。
记一次 .NET某旅行社酒店管理系统 卡死分析的更多相关文章
- 记一次 .NET 某药品仓储管理系统 卡死分析
一:背景 1. 讲故事 这个月初,有位朋友wx上找到我,说他的api过一段时间后,就会出现只有请求,没有响应的情况,截图如下: 从朋友的描述中看样子程序是被什么东西卡住了,这种卡死的问题解决起来相对简 ...
- 记一次 .NET 某金融企业 WPF 程序卡死分析
一:背景 1. 讲故事 前段时间遇到了一个难度比较高的 dump,经过几个小时的探索,终于给找出来了,在这里做一下整理,希望对大家有所帮助,对自己也是一个总结,好了,老规矩,上 WinDBG 说话. ...
- 记一次 .NET 某数控机床控制程序 卡死分析
一:背景 1. 讲故事 前段时间有位朋友微信上找到我,说它的程序出现了卡死,让我帮忙看下是怎么回事? 说来也奇怪,那段时间求助卡死类的dump特别多,被迫训练了一下对这类问题的洞察力 ,再次声明一下, ...
- Java swing实现酒店管理系统
今天给大家提供一个由今天给大家提供一个由Java swing实现的酒店管理系统,数据库采用sqlserver,我会贴上部分代码,完整的代码请看文章最下方下载,下面看代码: 1.主框架代码: packa ...
- 这几天做完简易酒店管理系统,对Sql Server执行计划的浅显了解。
我是一名大三的小学生,今天开始我的第一篇博客,最近随便做了一个简易的酒店管理系统,对sql执行计划有了初步的了解. 查看上面语句的预估执行计划,在工具栏中有这个按钮 聚集索引扫描被称为Index Sc ...
- Java编写ArrayBasic制作一个简单的酒店管理系统
听老师讲了一些ArrayBasic的一些知识,让制作一个酒店管理系统,要求:显示酒店所有房间列表,预订房间.... 经过老师的指导写了一个代码,如下: import java.util.Scanner ...
- Linux下C++酒店管理系统
功能要求: 相关源码:码云:传送门,GitHub:传送门 相关图片: 拆分版 make编译 ./hotel运行 输入2,进入开房模块 相关源码: class.cpp #include ...
- 基于UML的中职班主任工作管理系统的分析与设计--文献随笔(二)
一.基本信息 标题:基于UML的中职班主任工作管理系统的分析与设计 时间:2016 出版源:遵义航天工业学校 关键字:中职学校; 班主任工作管理; UML建模 二.研究背景 问题定义:班主任是一项特殊 ...
- 基于UML的毕业设计管理系统的分析与设计
基于UML的毕业设计管理系统的分析与设计 <本段与标题无关,自行略过 最近各种忙,天气不错,导师心情不错:“我们要写一个关于UML的专著”,一句话:“一个完整的系统贯穿整个UML的知识”:我:“ ...
- 「艺蜂酒店管理系统」 · Java Swing + mysql 开发 学生毕业设计项目
Java Swing在社会上基本用不到,但是任有学校拿来当做结课设计,只是博主在校期间的一个项目.如果在部署过程中有问题可以加我qq68872185. 码云仓库地址:https://gitee.co ...
随机推荐
- .NET最佳实践:避免同步读取HttpRequest
为什么要避免同步读取 ASP.NET Core 中的所有 I/O 操作都是异步的.服务器实现了 Stream 接口,该接口同时具备同步和异步的方法. 在进行 I/O 操作时,应优先使用异步方法,以避免 ...
- 鸿蒙开发 - 数据持久化 Preferences (内存存储) (封装)
这篇文章介绍鸿蒙中的 Preferences,它是一种轻量级存储方式,数据存储在内存中,用于存储少量的数据. 可以执行 flush() 方法将内存中的数据写入到磁盘文件,保证下次重启后数据可以继续使用 ...
- 【SqlServer主从复制】Sql Server主从复制【完美实践】
目录 [0]环境信息 [0.1]拓扑架构环境 [0.2]实例名与服务器名检查 [1]前置环境配置 [1.1]修改hosts(配置DNS) [1.2]修改防火墙(网络连通性) [1.3]建立复制账户,测 ...
- Deepseek学习随笔(7)--- 构建私人知识库(附网盘链接)
Step 1:创建知识库 入口定位 登录 DeepSeek 控制台 → 左侧导航栏点击「知识库」→ 点击「新建知识库」按钮 基础设置 知识库名称:建议使用「领域+用途」命名法,如「医疗-糖尿病文献库」 ...
- MySQL - [20] 事务
题记部分 一.什么是ACID (1)Atomicity 原子性 某个操作,要么全部执行完毕,要么全部回滚. (2)Consistency 一致性 数据库中的数据全都符合现实世界中的约束,则这些数据就符 ...
- 如何通过 Linux Bash 技术,让你的独立产品实现一键安装
我在业余时间开发了一款自己的独立产品:升讯威在线客服与营销系统.陆陆续续开发了几年,从一开始的偶有用户尝试,到如今线上环境和私有化部署均有了越来越多的稳定用户. 虽然我编写了非常详细的在线文档,说明如 ...
- .netCore 使用 Quartz 实例
一.参考源文链接 1.https://www.likecs.com/show-897836.html 2.https://blog.csdn.net/weixin_43614067/article/d ...
- 【Matlab】基于KDtree的最近邻搜索和范围搜索
摘要:介绍Matlab的rangesearch()函数和knnsearch()函数. rangesearch() -- 根据给定k-维数据集,返回指定距离范围内的所有数据点 knnsearch() - ...
- css3 渐变边框如何实现圆角效果
常规的 border-image 属性如果直接使用 border-radius 会无效,关于如何实现渐变边框圆角,网上流传着大概这么几种办法: 渐变背景方式(仅适用于纯底色背景) 借助 after 伪 ...
- MySQL REPLACE函数:字符串替换
语法 REPLACE ( string_expression , string_pattern , string_replacement ) 替换字符串,接受3个参数,分别是原字符串,被替代字符串,替 ...