.NET Core多线 (5) 常见性能问题
合集:.NET Core多线程温故知新
- .NET Core多线程(1)Thread与Task
- .NET Core多线程(2)异步 - 上
- .NET Core多线程(3)异步 - 下
- .NET Core多线程(4)锁机制
- .NET Core多线程(5)常见性能问题
去年换工作时系统复习了一下.NET Core多线程相关专题,学习了一线码农老哥的《.NET 5多线程编程实战》课程,我将复习的知识进行了总结形成本专题。
本篇,我们来继续复习一下多线程性能问题的相关知识点,预计阅读时间10分钟。
首先,我们可以明确一下,多线程场景下的常见问题一般为:高CPU占用。
一、CPU暴高问题
基本认知
CPU暴高大部分情况下都是线程打暴的!
暴高案例
(1)错误地使用List导致的CPU暴高
常见于偶发性CPU暴高案例中,比如使用了List.Insert(0, item) 时在大数据量下(比如20w+)时间复杂度很大 + 扩容机制,性能很差!一般可能是由模糊查询导致的查了大量DB数据出来组装,因此只会在大数据量时才会偶发。
(2)错误地使用String的拼接导致的CPU暴高
大量错误的大字符串(>85K的都会进LOH)拼接导致LOH频繁触发GC导致CPU暴高。建议使用StringBuilder来重构,但要设置一个合适的初始容量Capacity从而避免频繁对象申请和内存复制。
(3)非线程安全的Dictionary导致的CPU暴高
在多线程环境下使用非线程安全的Dictionary.Contains(key)时导致了在内部实现方法FindEntry(key)时出现了死循环(Entry结构体的next指针指向了自己,由于其他线程也正在Insert、Remove、Update等操作),然后多线程环境下可能有多个死循环一起把CPU打暴了!建议使用线程安全的ConcurrentDictionary结构。
(4)lock convoy(锁护送)导致的CPU暴高
在多线程环境下频繁的上下文切换导致,比如每个线程被分配了30ms时间片,但只执行了5ms就被卡主了,即每个请求都有一个lock锁。之前Edison所在的Y公司项目中的JSON-RPC的PreRequest就是这种情况。建议使用批量操作,降低串行化的 lock 个数,不要去玩锁内卷。
(5)应用服务器错误地配置32位导致的CPU暴高
多线程环境下某个方法读取了大量数据(50w+)导致了内存不够用进而引发GC频繁回收进而导致CPU暴高。这常常发生部署在IIS上的.NET Framework Web应用程序:
- 32bit最高只能吃4G内存;
- 32bit的临时代(Gen0+Gen1)大概只有不到100M的内存空间;
- 在IIS服务器模式下,GC会临时征用托管线程充当GC回收线程。
快速解决:将IIS的应用程序域 配置中的 “启用32bit应用程序” 改成False。
二、一些实际案例
案例背景
在Edison的前任Y公司,我们做了一些性能优化的措施,提高了系统的稳定性。这里假设之前的系统(大单体)域名为 cj.wzy.cn,每天平均UV(独立用户数) 10000~15000个,平均每天PV(页面浏览数)大概20000~25000个,实时用户数UV(高峰期)800~1000个。虽然这个数值并不高,但是对于这个已经运行了7年多的大单体老系统(.NET 4.5的大Shi山)而言,已经是线上很不稳定了,经常可以看到客服发来的客户抱怨的ticket。
优化内容
(1)优化了一堆年久失修的基础组件
未优化之前存在的问题:
jsonrpc的全局PreRequest方法中存在大量 lock convoy (锁护送) 导致线程频繁的上下文切换
比如:每个线程被分配了30ms时间片,但只执行了5ms就被卡主了,即每个请求都有一个lock锁
封装的LocalMemoryCache类基于ReaderWriterLocakSlim对本身就是线程安全的MemoryCache类做线程安全控制
用户态自旋 => 用户态 转 内核态 => 造成CPU压力升高
团队以前自己封装的一个 KafkaHelper 的 Send 方法中加锁范围过大导致等待时间较长
......
(2)优化了一堆慢SQL
未优化之前存在的问题:随着数据量的不断增加,老业务的SQL脚本包含了很多聚合函数、临时表操作 以及 未命中索引的查询条件,解决办法就是SQL优化,对比执行计划 + DBA Review后上线。
(3)优化了IIS的基本配置
未优化之前的问题:部分应用服务器特别是自建的文件服务,经常发生由于配置了“启用32位应用程序”导致的内存不够(因为32位应用最大可用4G内存)用进而引发GC频繁回收进而导致CPU暴高。
解决办法就是将启用32位应用设为False,然后参考一些IIS配置的最佳实践去做了一遍。
当然,根本解法还是去分析自建文件服务中耗内存的地方去优化代码。不过由于当时的物理服务器都是128G的内存且业务场景中也确实存在上传大文件的需求,因此耗内存的地方也暂时搁置去解决了。
(4)优化了滥用Parallel并行库的接口
未优化之前存在的问题:部分耗时较长的Job不加限制的使用 Parallel.ForEach 等方法造成所有CPU Core都被占用并持续数秒,造成CPU>=90%优化后增加了统一的设置的MaxParallelOptions,修复所有滥用的地方传递进去,默认只会用到CPU内核数量的一半。
(5)新增了一台DB服务器分摊压力
有一次因为XXXXXReadDB少了一台,本来是1台写库,2台读库,突然少了一台,导致XXXXXReadDB CPU暴高,应用程序段的DB连接超时严重进而造成延时较多,请求对接,应用程序频繁挂掉。因此后续DBA新增了一台读库,组成1主3从的配置,应用程序段通过切分 合同查询 的业务查询 到 XXXXXReadDB04,所有Job的查询都走XXXXXReadDB03,将流量分摊到不同的读库,保证核心用户的查询流量的可用性。
(6)新增了两台应用服务器分流压力
2021年开始研发中心内部各团队应用开始疯狂调用该系统接口,每分钟请求量达到了1500+左右,造成了原本只是对外部客户服务的应用服务器压力增大,因此新增了两台应用服务器将所有其他团队的内部应用的service请求流量切分到独立的三台服务器上,内外部客户的流量分开,优先保证外部客户的可用性。
未完成的事情
这一切的根因都是因为这七年来这个系统所在的团队单纯拼命的干业务迭代,往原本设计就不佳的大单体系统中堆了太多的屎山,造成了太多的技术债并未及时地去偿还。我们也原本想极力推荐将其拆分后升级到.NET Core或最新的.NET技术,基于.NET Core + 容器化技术 + 开源项目去做较低成本的升级改造,可是计划赶不上变化,当公司从阿什么味的公司找来一高P来做技术总监之后,研发中心所有的Team Leader基本都换成了阿什么味背景的或者靠近阿什么味领导的。此后,所有的计划都是围绕着Java从0到1花费大量成本重构整个大系统来进行,用他们的话来说就是降本增效只能靠Java而不是.NET。公司里整个Java圈子的高级开发者对.NET的认识也还是停留在10年前,我们的发声已变得微不足道,政治正确才是明哲保身的唯一出路。
在这里,Edison还是祝愿Y公司能够越走越好。
参考资料
一线码农,腾讯课堂《.NET 5多线程编程实战》
不明作者,《Task调度与await》
.NET Core多线 (5) 常见性能问题的更多相关文章
- 常见性能优化策略的总结 good
阅读目录 代码 数据库 缓存 异步 NoSQL JVM调优 多线程与分布式 度量系统(监控.报警.服务依赖管理) 案例一:商家与控制区关系的刷新job 案例二:POI缓存设计与实现 案例三:业务运营后 ...
- ASP.NET Core之跨平台的实时性能监控(2.健康检查)
前言 上篇我们讲了如何使用App Metrics 做一个简单的APM监控,最后提到过健康检查这个东西. 这篇主要就是讲解健康检查的内容. 没看过上篇的,请移步:ASP.NET Core之跨平台的实时性 ...
- 健康检查NET Core之跨平台的实时性能监控
ASP.NET Core之跨平台的实时性能监控(2.健康检查) 前言 上篇我们讲了如何使用App Metrics 做一个简单的APM监控,最后提到过健康检查这个东西. 这篇主要就是讲解健康检查的内 ...
- 使用Django.core.cache操作Memcached导致性能不稳定的分析过程
使用Django.core.cache操作Memcached导致性能不稳定的分析过程 最近测试一项目,用到了Nginx缓存服务,那可真是快啊!2Gb带宽都轻易耗尽. 不过Api接口无法简单使用Ngin ...
- Unity游戏项目常见性能问题
Unity技术支持团队经常会对有需求的客户公司项目进行游戏项目性能审查与优化,在我们碰到过的各种项目相关的问题中也有很多比较共同的方面,这里我们罗列了一些常见的问题并进行了归类,开发者朋友们可以参考下 ...
- ASP.NET Core 之跨平台的实时性能监控
前言 前面我们聊了一下一个应用程序 应该监控的8个关键位置. . 嗯..地址如下: 应用程序的8个关键性能指标以及测量方法 最后卖了个小关子,是关于如何监控ASP.NET Core的. 今天我们就来讲 ...
- ios 常见性能优化
1. 用ARC管理内存 2. 在正确的地方使用reuseIdentifier 3. 尽可能使Views透明 4. 避免庞大的XIB 5. 不要block主线程 6. 在Image Views中调整图片 ...
- .NET Core 成都线下面基会拉开序幕
2017年07月29日下午,由 .NET China Foundation 成都小组组织的 .NET Core 成都地区线下技术交流会在成都成华区某茶楼成功举行,这也是成都地区 .NET Core 非 ...
- EF Core 使用编译查询提高性能
今天,我将向您展示这些EF Core中一个很酷的功能,通过使用显式编译的查询,提高查询性能. 不过在介绍具体内容之前,需要说明一点,EF Core已经对表达式的编译使用了缓存:当您的代码需要重用以前执 ...
- async/await 的基本实现和 .NET Core 2.1 中相关性能提升
前言 这篇文章的开头,笔者想多说两句,不过也是为了以后再也不多嘴这样的话. 在日常工作中,笔者接触得最多的开发工作仍然是在 .NET Core 平台上,当然因为团队领导的开放性和团队风格的多样性(这和 ...
随机推荐
- 2022-05-19:给定一个数组arr,给定一个正数M, 如果arr[i] + arr[j]可以被M整除,并且i < j,那么(i,j)叫做一个M整除对。 返回arr中M整除对的总数量。 来自微软。
2022-05-19:给定一个数组arr,给定一个正数M, 如果arr[i] + arr[j]可以被M整除,并且i < j,那么(i,j)叫做一个M整除对. 返回arr中M整除对的总数量. 来自 ...
- Django date
date根据给定格式对一个日期变量进行格式化. 可用的格式字符串: 格式化字符 描述 示例输出a 'a.m.'或'p.m.' 'a.m.'A 'AM'或'PM' 'AM'b 月份,文字形式,3个字母, ...
- 2014年蓝桥杯C/C++大学B组省赛真题(奇怪的分式)
题目描述: 上小学的时候,小明经常自己发明新算法.一次,老师出的题目是:1/4 乘以 8/5 小明居然把分子拼接在一起,分母拼接在一起,答案是:18/45 (参见图1.png)老师刚想批评他,转念一想 ...
- k8s calico网络
- Intellij IDEA最新激活码,适合2022,2023和所有版本,永久更新
分享一下 IntelliJ IDEA 2023.1 最新激活注册码,破解教程如下,可免费永久激活,亲测有效,下面是详细文档哦~ 申明:本教程 IntelliJ IDEA 破解补丁.激活码均收集于网络, ...
- Linux下程序时间消耗监控与统计
良好的计时器可帮助程序开发人员确定程序的性能瓶颈,或对不同算法进行性能比较.但要精确测量程序的运行时间并不容易,因为进程切换.中断.共享的多用户.网络流量.高速缓存访问及转移预测等因素都会对程序计时产 ...
- 哈希工具john
john:一种极其强大且适应性强的哈希破解工具 爆破字典使用臭名昭著的 rockyou.txt 词表--这是一个非常大的常用密码词表 使用的工具 字典:rockyou.txt 哈希识别工具:hash- ...
- 【Python&GIS】GDAL栅格转面&计算矢量面积
GDAL(Geospatial Data Abstraction Library)是一个在X/MIT许可协议下的开源栅格空间数据转换库.它利用抽象数据模型来表达所支持的各种文件格式.它 ...
- 使用Python实现学生信息管理系统
本文介绍了一个简单的学生信息管理系统,包括管理员登录.重置学生密码.添加.删除和修改学生信息.查询学生信息以及对学生成绩进行排序等功能.该系统使用Python编写,基于控制台交互 实现思路 该系统分为 ...
- Java 网络编程 —— RMI 框架
概述 RMI 是 Java 提供的一个完善的简单易用的远程方法调用框架,采用客户/服务器通信方式,在服务器上部署了提供各种服务的远程对象,客户端请求访问服务器上远程对象的方法,它要求客户端与服务器端都 ...