我给本地缓存加了 TTL 和 LRU,踩了三个并发坑
从一个风控服务的真实场景出发,手写一个带过期时间和 LRU 淘汰的进程内缓存工具类。讲清链表与 Map 的并发删除竞态、时间源选择、清理线程的两个陷阱,以及缓存击穿的处理方式。
Blog
格达软件工程师的实战经验、技术选型与行业观察
从一个风控服务的真实场景出发,手写一个带过期时间和 LRU 淘汰的进程内缓存工具类。讲清链表与 Map 的并发删除竞态、时间源选择、清理线程的两个陷阱,以及缓存击穿的处理方式。
把聚合 200 笔下游状态的页面从串行 40 秒改到 0.9 秒:讲清协程与事件循环的调度机制,以及三个不报错、只变慢的坑——协程里混入阻塞调用、并发不设上限、任务引用丢失与取消被吞。
断网后界面仍显示已连接、消息发出去石沉大海,这是 WebSocket 最常见的故障。文章从一次地铁切网的假在线排查出发,给出客户端应用层心跳、服务端协议级探活、全抖动指数退避重连的完整实现,并算清各层超时与心跳的流量代价。
在 38 万文件的 monorepo 里,grep -r 要跑一分半还刷满 node_modules 噪音。这篇讲清楚 rg 的忽略规则层级、-u/-uu/-uuu 的真实代价、把结果安全喂给 xargs 的写法、多行匹配吃内存的边界,以及哪些场景根本不该用 fd | rg。
支付回调天然会重复。本文从一次重复发货的线上故障切入,讲清楚幂等键怎么选、为什么只能靠数据库唯一索引兜底、状态机如何接住乱序回调、重试该放在哪里,以及几个真正会出事的地方。附可运行的 Spring Boot + MySQL 代码。
两个事务在 RR 隔离级别下先查后插,计数没超却多发了券;更怪的是 UPDATE 影响行数为 1,同一事务里 SELECT 却查不到这行。这篇文章用可复现的 SQL 会话讲清快照读与当前读的差异、间隙锁的边界,以及唯一索引和原子更新怎么兜底。
排查一个内存只涨不降的服务,ASAN 报出几万个没释放的 Session,代码里一个 new 都没有。本文从这个可复现的循环引用案例出发,讲清三种智能指针该用在哪、代价是什么,以及 pImpl 不完整类型、make_shared 延迟释放、expired 与 lock 竞态这几个实际会踩的坑。
订单接口在库存不足时返回 500,客户端 SDK 和网关各重试一次,库存被扣成负数。这篇文章把 4xx 和 5xx 的分界线划清楚:哪些错必须让调用方看到 4xx、哪些错重试真的有用,以及状态码选错之后监控、熔断、CDN 缓存会怎么一起塌。
自增 ID 直接 Base62 会被顺序遍历,哈希取位又会碰撞。本文给出号段发号 + Feistel 置换生成 6 位不可枚举短码的完整 Go 实现,并讲清 301 缓存、缓存穿透、LAST_INSERT_ID 连接级坑和建表时的 DDL 报错。
第三方接口 100 QPS,我们打出 750 QPS 换来一串 429。这篇文章给出 Go 版令牌桶和两种漏桶的可运行实现,说清三者真正的差别、burst 该怎么定、以及把 now 放到锁外这类会让实际速率悄悄变低的坑。