-
我给服务做了配置热更新不重启就能改限流阈值这些参数、本以为很丝滑,可有一次我同时改了限流的阈值和时间窗口两个相关参数推下去、那一瞬间线上限流就乱套了有请求按新阈值配旧窗口的奇怪组合被误杀,排查很久才搞懂我的热更新是一个字段一个字段改的并发请求正好读到了一半新一半旧的中间态的深度复盘
我给服务接了配置中心做了热更新、不重启就能让新配置生效,平时改单个参数很丝滑。直到一次同时改了限流的两个相关参数——阈值和时间窗口长度,推送上线那一瞬间:配置生效的那一两秒内限流突然异常、有些本该放行的被误杀 429 有些本该拦的又放过、像是用了新阈值加旧窗口或旧阈值加新窗口这种从没配过的奇怪组合在限流(而新配旧配单独都合理)、过一两秒就自己好了、改单个参数从没出过这事。这几条指向我热更新代码里的…- 0
- 0
-
我给一个带 sync.Mutex 的 Go 结构体写了用值接收者的方法又到处用值传递把它传来传去、自以为加了锁就线程安全了,结果高并发下那份本该被锁保护的数据还是被改乱了计数器还是丢更新,排查很久才搞懂我每次值拷贝这个结构体时把里面的锁也一起复制成了另一把副本锁的根本不是同一把锁的深度复盘
我写了个带计数功能的结构体 Counter 里有个 sync.Mutex 保护并发、方法用了值接收者 func (c Counter) Inc() 里面 c.mu.Lock() defer Unlock() c.count++,然后多个 goroutine 并发调 Inc() 还经常把 Counter 当参数值传递。上线后:开 1000 个 goroutine 各 Inc() 一次最后 Value…- 0
- 0
-
我在 Go 的 for 循环里写 select 处理 channel、为了让它别卡住顺手加了个 default 分支,结果服务一启动某个 CPU 核心就直接飙到百分之百风扇狂转,我盯着那段逻辑明明没干什么重活百思不得其解最后才反应过来那个 default 让 select 永远不阻塞整个循环变成了疯狂空转的忙轮询的深度复盘
我有个后台 goroutine 在 for 循环里用 select 监听几个 channel(收任务、收停止信号),担心万一所有 channel 都没消息 select 会卡住、就给它加了个 default 分支,想着没消息时走 default 不卡住挺稳妥,功能也正常。可一上线就发现:服务明明很闲没什么任务、某个 CPU 核心却常年 100% 风扇狂转、整体性能还下降了。top 看是我的进程吃满…- 0
- 0
-
我给一段并发临界区加了 lock 锁、自以为线程安全了,可线上还是不停冒出数据被并发改乱的脏数据,我反复确认 lock 语句写得没错临界区也包对了,最后才发现问题出在我锁的那个对象上——每个线程锁的根本不是同一个对象、这把锁形同虚设的深度复盘
我有一段会被多个线程并发调用的代码,里面要读改一份共享状态(往共享集合加元素、更新共享计数),我知道需要互斥就规规矩矩用 lock 把临界区包了起来、自以为万无一失。可线上偏偏时不时出现脏数据:共享集合元素丢失、计数对不上,典型的并发写竞争没被挡住。我反复检查 lock 语法没错、临界区范围也把读改都包进去了,又怀疑别处有没加锁的路径也没发现。直到盯着 lock 后面括号里的锁对象看才如遭雷击:我…- 0
- 0
-
我的 Go 程序某个功能莫名其妙地卡住、既不报错也不返回,goroutine 越积越多最后内存涨爆,排查半天才发现是一个 channel 忘了 make 初始化、它是 nil,而往 nil channel 收发竟然不是报错、而是永久阻塞的深度复盘
我有段 Go 代码用 channel 在 goroutine 间传数据,在结构体里声明了一个 channel 字段,启动 goroutine 往里发另一边收,自测小场景跑通了。可某功能上线后行为诡异:它莫名卡住——既不报错也不返回也不超时,就静静挂着;随请求进来卡住的 goroutine 越积越多内存一路涨爆。我以为是死锁、下游慢,查半天没问题。直到打 goroutine 栈,发现一大堆都阻塞在那…- 0
- 0
-
我的 HTTP 请求明明已经超时返回了,可它在后台启动的 goroutine 还在埋头跑、下游调用也没停,goroutine 越积越多内存一路涨,因为我没把 context 的取消信号传下去也没人监听它的深度复盘
我有个接口处理请求时会启动一些 goroutine 做并行子任务,并给请求设了超时。线上现象诡异:有些请求超时返回了,可监控显示 goroutine 数量只增不减、内存一路缓慢上涨。排查发现那些超时的请求主流程返回了,但它启动的 goroutine 还在后台埋头跑(还在查早已没人要结果的下游)、跑完没人收、白白耗着然后泄漏。复盘才搞懂:Go 用 context 传播取消/超时,请求超时时 ctx …- 0
- 0
-
我想用多线程加速一段纯计算的代码,开了 8 个线程满心以为能快 8 倍,结果不但没快、反而比单线程还慢,因为 Python 有个 GIL、同一时刻只让一个线程真正在算的深度复盘
我有一段 CPU 密集的计算(大量纯数值运算),单线程跑得慢,我想这台机器有 8 核、开 8 个线程并行算不就快 8 倍?于是用 threading 开了 8 个线程分摊计算。结果一测:不但没快 8 倍,反而比单线程还慢,CPU 监控显示始终只有一个核在忙、其他核闲着。复盘才搞懂:CPython 有一个 GIL(全局解释器锁),同一时刻只允许一个线程执行 Python 字节码;我开的 8 个线程没…- 0
- 0
-
我为了防止插入重复数据,代码里先查一下存不存在、不存在才插入,单机测试一切正常,可一上并发就冒出了重复记录,因为先检查再插入这两步之间有个窗口、并发请求全挤了进来的深度复盘
我有个场景要防止插入重复(同一用户名只能注册一个、同一订单号只能下一单),写得很合理:先 SELECT 查这条记录在不在、不在才 INSERT。单机低并发测试一切正常、防重也生效。可一上线并发一高就冒出重复记录:数据库里出现两条一模一样的记录,先查再插形同虚设。复盘才想明白:先检查(SELECT)再行动(INSERT)这个 check-then-act 模式在并发下根本不是原子的——两步之间有时间…- 2
- 0
-
我用 WaitGroup 等一批 goroutine 全部干完,却把 wg.Add 写进了 goroutine 内部,结果主协程的 Wait 在 Add 之前就跑完直接返回,程序以为活儿都干完了其实大半还没开始:一次 WaitGroup 时序用错的深度复盘
我要启动一批 goroutine 并发处理任务、再用 WaitGroup 等它们全部干完汇总结果,写成了在循环里 go func(){ wg.Add(1); defer wg.Done(); doWork() }()——把 wg.Add(1) 放在了 goroutine 内部。线上现象诡异:不报错,但汇总结果经常不完整,明明启动 100 个任务却只有零星几个的结果。复盘才搞懂:WaitGroup …- 0
- 0
-
我用一个 channel 收集多个 worker 的结果,某个 worker 干完顺手把 channel 关了,其他还在干活的 worker 一发送就 panic:send on closed channel,一次 Go channel 关闭所有权的深度复盘
我用多个 worker goroutine 并发处理任务,每个 worker 把结果发到一个共享的 results channel,主 goroutine 收集。为了发完就通知结束,我让每个 worker 干完后 close(results)。功能偶尔能跑,可线上常崩 panic: send on closed channel。推演时序才明白:这个 channel 是多个 worker 共享发送的…- 0
- 0
-
我把带对话记忆的 Agent 做成了单例,上线后用户 A 问的问题,Agent 拿着用户 B 的对话历史在回答,记忆全串了:一次 Agent 会话状态没隔离的深度复盘
我做了个对话式 AI Agent,它有记忆能记住对话历史 conversationHistory,为了方便省资源把它做成了单例。上线后多个用户并发使用时,用户 A 问的问题 Agent 却拿着用户 B 的对话历史在回答、答非所问,更严重的是 A 的对话内容出现在了 B 的回复里(信息泄漏)。查清才明白:所有用户共用了同一个 Agent 实例的记忆——我把会话级的对话历史放成了单例的共享可变实例字段…- 0
- 0
-
两笔转账并发执行,一笔从 A 转 B、一笔从 B 转 A,数据库突然报 Deadlock found 把其中一笔回滚了:一次数据库死锁、加锁顺序不一致循环等待的深度复盘
我的转账逻辑一个事务里更新两个账户(扣转出方、加转入方),平时好好的,可线上偶发数据库报 Deadlock found、其中一笔被回滚。把两笔并发转账的加锁顺序画出来才看明白:事务 T1 从 A 转 B 先锁 A 再要锁 B,事务 T2 从 B 转 A 先锁 B 再要锁 A——T1 锁住 A 等 B、T2 锁住 B 等 A,互相等对方释放、循环等待、谁也动不了,数据库检测到死锁就挑一个回滚。根因是…- 0
- 0
-
我扣库存的逻辑是先查出当前库存、减一、再写回,平时好好的,一上量就超卖、库存对不上账:一次数据库并发更新丢失、读改写非原子导致超卖的深度复盘
我扣库存的逻辑写得很直白——先 SELECT 查出当前库存,代码里减一,再 UPDATE 写回。平时流量小一直没问题,活动一放量并发一高就出事了:商品明明只有 100 件却卖出 100 多件超卖,对账时库存扣减数量和卖出订单数对不上。推演两个并发请求才看明白:这是更新丢失——A 查出 stock=100,B 也查出 100(都读到旧值),A 算 99 写回,B 也算 99 写回,B 把 A 的扣减…- 0
- 0
-
我在 for range 循环里起了一批 goroutine 并发处理任务,结果它们全都处理了同一个、也就是最后一个任务:一次 Go 循环变量被复用、闭包捕获到的全是最后一个值的深度复盘
我要并发处理一批任务,for range 遍历任务列表、每个任务起一个 goroutine 去处理。结果诡异:明明有 10 个不同任务,日志里 10 个 goroutine 处理的全是同一个、也就是最后一个任务,前 9 个一个都没处理。查清才发现:在 Go 1.22 之前,for range 的循环变量 task 是整个循环复用的同一个变量,每轮只是赋新值;而 goroutine 闭包捕获的是这个…- 0
- 0
-
两个并发事务因为以不同的顺序去更新两条记录,互相等着对方手里的锁,撞成了死锁、被 MySQL 强行回滚了一个:一次数据库死锁的深度复盘
转账接口高并发时偶发 Deadlock found,部分请求失败。根因是一个事务要更新两条记录,而不同请求加锁顺序不同:A→B 的请求先锁 A 再锁 B、B→A 的先锁 B 再锁 A,并发时事务1持有A等B、事务2持有B等A,互相等待形成循环等待的环、谁也走不了,MySQL 检测到死锁就回滚其中一个。本文讲透死锁怎么形成和它的四个必要条件,给出固定加锁顺序(破坏循环等待,最有效)、缩小事务/缩短持…- 0
- 0
-
一个每次请求都起一个 goroutine 却没人保证它能退出的服务,goroutine 越积越多、内存缓慢上涨,跑几天就 OOM:一次 goroutine 泄漏的深度复盘
服务内存缓慢上涨、跑几天就被 OOM,pprof 一看 goroutine 从几十个涨到几十万、只增不减,且全卡在同一行 channel 接收/发送上。根因是每请求起 goroutine 通过无缓冲 channel 返回结果,而主流程超时/取消后提前返回、不再接收,那个负责发送的 goroutine 就永远阻塞在 ch- 0
- 0
-
一个被多个 goroutine 同时读写的普通 map,把整个 Go 服务以 fatal error 直接干崩、连 recover 都拦不住:一次 map 并发不安全的深度复盘
高并发 Go 服务平时稳如磐石,一到高峰就毫无征兆整个进程挂掉,日志只留一行 fatal error: concurrent map read and map write,而且入口的 recover 居然拦不住。根因是用普通 map 做内存缓存、多个 goroutine 无同步地并发读写它——Go 内置 map 为性能不做并发同步,并发写会破坏其哈希结构,运行时检测到就抛不可 recover 的 …- 2
- 0
-
一段用多线程给 CPU 密集计算加速的 Python 代码,开了八个线程却比单线程还慢,我被 GIL 实实在在上了一课:一次多线程并行误区的深度复盘
一个 CPU 密集计算单线程跑十几秒,我想当然地以为 8 核机器开 8 个线程能快好几倍,结果不但没快反而更慢,而且 8 核机器 CPU 利用率始终只有一个核的量。根因是 CPython 的 GIL(全局解释器锁):同一时刻只有一个线程能执行 Python 字节码,8 个线程只能轮流抢 GIL、本质串行用不上多核,还多了切换开销。本文讲透 GIL 是什么、为何让多线程对 CPU 密集无效却对 IO…- 0
- 0
-
我用 Python 多线程并行跑 CPU 密集计算想提速,结果开了 8 个线程比单线程还慢,我对着 GIL 排查了大半天的复盘
写了个 CPU 密集型计算任务,想当然用多线程并行加速,开 8 个线程以为快 8 倍。结果目瞪口呆:开 8 个线程不但没快反而比单线程还慢一点;CPU 监控更困惑——8 核机器,8 个线程加起来只用满约 1 个核,其他 7 个核基本闲着。排查大半天才理解 Python 又爱又恨绕不开的设计——GIL(全局解释器锁):它规定同一时刻只有一个线程能执行 Python 字节码,所以即使开 8 线程、有 …- 0
- 0
-
我用 Redis 加了分布式锁防并发,结果两个进程还是同时执行了、还互相把对方的锁删了,我对着分布式锁的几个致命细节排查了大半天的复盘
做了个多实例部署的定时任务,用 Redis 加分布式锁确保同一时刻只有一个实例执行,以为加了锁就万事大吉。结果生产诡异:有时两个实例同时跑(锁没锁住)导致数据重复处理,有时一个实例把另一个实例的锁删了。盯着 SETNX 抢锁、DEL 释放的标准代码反复看,排查大半天才发现藏着三个致命细节:SETNX 没设过期(持锁者崩溃就死锁)、锁提前过期(业务跑40秒超过锁30秒过期,A还在执行B就抢到锁同时执…- 0
- 0
-
我在 for 循环里起了一堆 goroutine 处理每个元素,结果它们全在处理同一个、还是最后一个,我对着循环变量被闭包捕获排查了大半天的复盘
用 Go 写批处理,想为切片每个元素并发起一个 goroutine,经典的 for + go 写法,行云流水。结果目瞪口呆:10 个元素起了 10 个 goroutine,处理的却全是同一个、还是最后一个,前 9 个一个没处理。盯着代码反复看,循环明明遍历了每个元素,凭什么 goroutine 全拿到最后一个?排查大半天才理解 Go 1.22 之前坑了无数人的经典陷阱:for 循环变量被闭包捕获。…- 0
- 0
-
我用 Redis 加了分布式锁防并发,结果偶尔还是有两个节点同时拿到锁、把临界区并发执行了,我对着这把"形同虚设"的锁排查了大半天的复盘
我用 Redis 的 SETNX 加锁、设过期时间防死锁、用完 DEL 释放,实现了分布式锁防并发,结果临界区偶尔还是被两个节点同时执行、锁形同虚设,还极其偶发难复现。把时序画出来才懂两个致命问题:① 业务执行时间超过了锁的过期时间,锁自动到期释放、另一个节点趁机拿到锁,于是两节点同时持锁;② 释放锁直接 DEL 没校验"是不是自己的锁",A 超时后 B 拿到锁,A 执行完却把…- 0
- 0
-
我的转账接口在高峰期偶尔会报 Deadlock found、事务莫名其妙被回滚,我对着这个时有时无的数据库死锁排查了大半天才搞懂加锁顺序的复盘
我的转账接口在一个事务里扣 A 加 B,平时没事,一到高峰并发大就偶发 Deadlock found、事务被强行回滚,时有时无、并发越高越频繁。把并发事务的加锁顺序画在纸上一对照才懂:这是死锁,根源是加锁顺序不一致——A 转 B 的事务先锁 A 再锁 B,而同时 B 转 A 的事务先锁 B 再锁 A,于是事务1锁住A等B、事务2锁住B等A,各自持有对方想要的锁、互相死等,形成循环等待;InnoDB…- 0
- 0
-
我在多线程里共用一个 HashMap 做缓存,某天线上 CPU 突然飙到 100% 卡死、线程全堆在 HashMap.get 上,我抓着线程栈复盘了大半天的惨痛经历
我的服务用一个 HashMap 当本地缓存、多线程并发读写,平时好好的,某天线上 CPU 毫无征兆飙到 100% 且降不下来、服务卡死。jstack 一抓头皮发麻:好几个线程死死卡在 HashMap.get 的 for 循环里出不来——死循环。深挖才懂:HashMap 线程不安全,多线程并发 put 同时触发扩容,JDK7 头插法迁移节点时并发把链表指针搞乱、接成 A→B→A→B 的环形链表,此后…- 0
- 0
并发
幸运之星正在降临...
点击领取今天的签到奖励!
恭喜!您今天获得了{{mission.data.mission.credit}}积分
我的优惠劵
-
¥优惠劵使用时效:无法使用使用时效:
之前
使用时效:永久有效优惠劵ID:×
没有优惠劵可用!
























