用 Lua 脚本原子实现滑动窗口日志限流的计数清理
用 Redis 做限流时,很多人第一版都会写成「先 ZREMRANGEBYSCORE 清过期、再 ZCARD 判断、最后 ZADD 写入」的三段式逻辑。单机压测没问题,一上并发就出现超卖:明明配置 100 次/分钟,实际放过了 130 次。根因不是 Redis 性能不够,而是这三步之间存在竞态窗口——两个请求可能同时读到 99,然后各自 +1。把这三步塞进同
用 Redis 做限流时,很多人第一版都会写成「先 ZREMRANGEBYSCORE 清过期、再 ZCARD 判断、最后 ZADD 写入」的三段式逻辑。单机压测没问题,一上并发就出现超卖:明明配置 100 次/分钟,实际放过了 130 次。根因不是 Redis 性能不够,而是这三步之间存在竞态窗口——两个请求可能同时读到 99,然后各自 +1。把这三步塞进同
缓存击穿是后端服务里最常见的一类雪崩源头:某个热点 key 刚过期,或者某个冷门 ID 突然被大量请求命中,缓存层没有数据,于是几百上千个 goroutine 同时打到 MySQL、Redis 从库或者某个内部 RPC 接口上。数据库连接池瞬间被占满,接口 P99 从 20ms 飙到 3s,上游开始超时重试,流量进一步放大,最终把下游彻底压垮。本文要解决的正
项目里几乎都有一个 config.ts,里面导出各种配置对象:路由表、主题变量、构建参数、feature flag。这类对象的诉求很特别——既要求结构被编译器严格校验,又要求字段值保留字面量类型。用类型注解写,校验有了,字面量类型却被拓宽成 string / number;用 as const 写,字面量保住了,却完全没有结构约束。TypeScript 5.
软删除是业务表的常见设计:加一个 deleted_at 或 is_deleted 字段标记删除,查询时过滤。真删除会绕过这个设计,所以大部分团队都会遇到同一个问题——原本在 email、username、order_no 这类字段上的唯一索引,在软删除之后不再"正确":删除一条记录后再次注册同一个邮箱会撞唯一键,报 Duplicate entry 'a@b.
商品详情页要同时拿到价格、库存、促销、评价四份数据,四个下游接口串行调用 800ms 起步,改成并行之后 P99 掉到 220ms。但并行带来的新问题是:只要有一个接口卡住,整页就跟着卡住。给每个 future 单独 orTimeout 只能解决单点超时,它抛出的 TimeoutException 会让 allOf 立刻异常结束,其余还在跑的请求白白浪费,而
用 paramiko 对几十上百台机器批量跑命令,最容易踩的两个坑是:远端命令卡住导致线程永久阻塞,以及stderr 没读干净导致 exit_status 拿到 None。前者会让整个批量任务的时间不可控,后者会让你明明看到失败却拿不到错误原因。paramiko 本身没有内置的命令级超时,exec_command 的 timeout 参数只作用于 chann
购物车是电商系统里最容易出现并发写冲突的数据结构之一:同一个用户可能在 App、小程序、Web 三端同时操作,也可能因为网络重试导致同一个「加购 +1」请求被提交两次。如果直接对 Redis Hash 里的 quantity 字段做 HINCRBY,数字本身不会写坏,但业务规则会被绕过——比如「单 SKU 最多 99 件」的限购、「购物车最多 50 个 SK
写 Go 并发的时候,最常踩的坑不是「不会起 goroutine」,而是「起了收不回来」。典型场景:一批 RPC 要并发发起,只要有一个失败就立刻停掉其余请求并返回第一个错误;或者一批 worker 在跑,用户在终端按下 Ctrl+C,需要让所有 worker 收到信号、优雅收尾。手写 sync.WaitGroup + context.WithCancel
消息队列重投、用户连点两次提交、定时任务因为上一次超时被重新调度——这些场景都会让同一条业务数据被写入两次。如果写入逻辑是「先 SELECT 判断存在再 INSERT」,在并发下会稳定地漏判:两个事务同时 SELECT 都没查到,然后都去 INSERT,最后要么产生两条脏数据,要么撞唯一索引报 Duplicate entry。把判断和写入压进一条 SQL,交