昨儿个折腾我那破.NET项目差点没把电脑砸了。本来项目跑得好好的,结果用户量一上来直接卡成PPT,老板拍着桌子问咱是不是该换技术栈了,搞得我后脖子直发凉。
一、内存漏得跟筛子似的
抄起Visual Studio直奔诊断工具,好家伙不看不知道,内存曲线跟爬山似的只上不下。原来之前图省事把一堆数据往静态变量里怼,想着反正常驻内存不用白不用。
- 逮住几个大对象:发现个1.2GB的缓存字典,这破玩意儿存了三年没人清的聊天记录
- 事件订阅不松绑:登录模块里事件注册了七八层,用户退出了还死抓着内存不放
- 手动收垃圾上瘾:菜鸟时期写的*遍地开花,CPU被折腾得直抽抽
当天下午就抡起袖子改代码,静态缓存全换成弱引用,事件处理统一加注销机制,*直接当仇家删光。
二、数据库查得比老太太散步还慢
刚把内存压下去,SQL Server又开始喘粗气。有张用户表500万数据,某功能点个按钮要等20秒才响应。
- 索引贴得跟狗皮膏药似的:查执行计划发现有的索引根本没命中,有的字段重复建了三个索引
- 不分青红皂白全表扫:有个排行榜功能每次都SELECT 再内存排序,服务器内存直接爆表
- EF瞎搞导航属性:代码里三层Include连查带join,返回的数据比新华字典还厚
连夜重写SQL,该分页的分页,该拆查询的拆查询。把EF里那些花里胡哨的Include全换成延时加载,发现原来要20秒的请求现在800毫秒就完事。
三、异步写成四不像
自认用了async/await就是异步大神,结果CPU利用率死活上不去。测试小哥拿着压测报告直翻白眼:“你这异步怕不是用的同步大法?”
- async void到处乱窜:事件响应方法里全写的void,异常抛出去连个鬼影都抓不到
- .Result卡死人不偿命:登录验证里嵌套五层Result,线程池活活饿死
- 数据库连接不关闸门:开了2000个异步连接,SQL Server哭着说撑不住了
咬着牙重构了三个通宵,await ConfigureAwait(false)加得手抽筋,.Result全改成await,再用SemaphoreSlim给数据库连接数上了把锁。
四、万万没想到的意外收获
本来以为大功告成,测试时候发现个邪门事儿——每次发布后首次请求总要卡10秒。查了半天发现是第一次跑代码时的JIT编译在搞鬼。
祭出大招:尼玛老子直接预热!写个开机脚本自动访问所有核心接口,现在发布完用户根本感觉不到卡顿。
优化完跟老板汇报战果,内存消耗砍掉65%,响应速度提升40倍。老板笑眯眯给我续了杯奶茶:“早该让你改嘛下个月用户量再翻倍也没问题!” 我捧着奶茶手直抖——该不会明天又要让我扛千万级流量?