分类: 功能实现

用 SKIP LOCKED 并发消费任务表避免队列锁等待的长事务

任务表充当轻量消息队列时,最常见的写法是 SELECT ... FOR UPDATE 配合 ORDER BY id LIMIT 1,再在同一个事务里把状态改成 processing。单进程下这套逻辑没有问题,一旦并发上去,问题就非常明显:多个 worker 的 SELECT 会排队等同一批行锁,谁先拿到锁谁先走,其余全部阻塞在 FOR UPDATE 上。更糟

用 limit_req_zone 按 cookie 中的租户标识分桶限流配置

多租户 SaaS 里最常见的线上事故不是全站被打挂,而是某一个租户刷爆了共享的 API 网关配额,把其他租户一起拖下水。用 limit_req 做全局限流很容易,limit_req_zone $binary_remote_addr zone=api:10m rate=20r/s; 一行就完事;但要做到「按租户分桶、每桶独立计数」,难点在于取 key 的方式:

用 EXPIRE NX 配合 ZADD GT 实现带过期语义的滑动分数榜单

排行榜是 Redis 最经典的用法之一:用 ZSET 存成员与分数,ZREVRANGE 取前 N 名,ZINCRBY 累加积分。但只要业务从「累计积分榜」滑向「热度榜 / 互动分榜」,问题立刻出现——成员分数需要被反复更新,而更新又要遵循「只能涨不能跌」或「窗口内有效」的规则。纯 ZSET 方案在这里会暴露三个具体缺陷:分数更新的并发覆盖、过期语义缺失导致僵

用 JSON_TABLE 把单据明细 JSON 数组拆行并关联主表统计金额

很多业务系统在设计单据表时,把明细行整体存进一个 JSON 列(比如 order_header.detail_json),主表只留单号、客户、状态等字段。这样做写入简单、结构灵活,但一到对账、报表、按商品汇总金额的环节就卡住:MySQL 不能直接对 JSON 数组里的每个元素做聚合。开发常见的土办法是把数据全捞到应用层用 Java/Python 循环展开,再

用 net/http/httptest 与可注入 Clock 固定时间实现带过期策略的签名 URL 校验

签名 URL(signed URL)在对象存储、下载站、付费资源分发里非常常见:服务端用密钥对「路径 + 过期时间」做 HMAC,把签名拼进查询串;客户端拿着这条 URL 在有效期内可以直接下载,过期后返回 403。真正难的不是签名算法本身,而是过期策略的可测试性——如果代码里直接写 time.Now(),你就要么 sleep 到过期(测试变慢且不稳定),要

用延迟关联改写大偏移 OFFSET 分页以命中覆盖索引

业务里最常见的翻页 SQL 大概是 SELECT * FROM t ORDER BY id LIMIT 20 OFFSET 500000。前几页很快,翻到几万页之后响应时间从十几毫秒涨到几秒,CPU 和 IO 一起飙升,慢查询日志里全是它。这不是 MySQL 的 bug,而是 OFFSET 语义决定的成本:数据库必须先定位并跳过前 N 行,再取 M 行。本文

用 errgroup.SetLimit 限制并发并收集多错误的聚合模板

在 Go 服务里做批量处理时,最常见的形态是:一个任务列表,每个元素触发一次下游调用(HTTP、RPC、数据库查询),希望并发跑得快,但又不能把下游打爆,于是需要限流;同时任何单个元素失败不应该中断整批,而是跑完后汇总所有失败项。手写 sync.WaitGroup + chan error + 信号量 channel 可以做到,但很容易踩到三个坑:错误 ch

用 array_reduce 构建链式校验器并短路返回首个错误结构

表单校验最常见的写法是「一长串 if + return」,字段一多就变成几十行 if 嵌套,错误信息散落在各处,单元测试只能整块跑。本文用 PHP 的 array_reduce 把一组校验规则折叠成一个链式校验器:规则顺序注册、遇到第一个失败立即短路,并返回统一结构的错误对象,调用方拿到的永远是「要么 null,要么一个含 code / field / me