当删除请求量达到10k QPS时,如何保证亚秒级延迟?

解读

面试官把场景限定在“删除”这一写操作,且并发高达10k QPS,同时要求P99 延迟 <1 s。国内互联网场景下,这类需求常见于:

  • 社交平台内容审核通过后批量清理缓存
  • 电商大促时违规商品下架
  • 云盘秒级清空回收站

删除链路往往要同步做物理删除、索引更新、缓存失效、搜索引擎踢词、风控日志落盘等多件事,任何一环放大延迟都会导致超时。因此考点是:在高并发写数据一致性可观测性成本控制之间给出可落地的国内架构方案,并能自证P99<1 s

知识点

  1. 分片与热点隔离:按user_id 哈希分片+热点 key 单独队列,避免单台机成为瓶颈。
  2. 异步化与最终一致:把强同步路径缩到最短,非强一致步骤丢给可靠消息队列(RocketMQ/RabbitMQ),利用批量 ACK降低毛刺。
  3. 缓存双 key 策略逻辑删除+懒物理删除;先给缓存置短 TTL 墓碑值,再异步刷 DB,读链路永不穿透失效主键。
  4. 批量提交与管道:MySQL 采用批量 delete + 分段 limit + 自旋索引;Redis 用UNLINK非阻塞删除+Pipeline打包。
  5. GC 与内核调优:Linux 开启multi-queue SSD+noop 调度器;JVM 用ZGC并把最大停顿目标设为 50 ms,防止 Full GC 拖尾。
  6. 限流与降级Sentinel配置关联限流——当缓存失效 QPS>12k 时,降级为先标记后删除,保证核心链路不崩。
  7. 可观测P99 延迟通过Prometheus+Grafana秒级拉取,TraceId全链路透传,SLS实时告警>800 ms 的慢请求。

答案

“我在上一家公司做过 12k QPS 删除场景,P99 稳定在 600 ms 以内,核心思路是把同步路径做短,把异步路径做稳

  1. 接入层OpenResty+Lua一致性哈希分片,把 10k QPS 均摊到 20 台 API 网关,单节点 500 QPS,CPU 占用 <30%。
  2. 缓存层
    • 采用Redis 6.2 集群单分片 8 线程 IO,开启UNLINK非阻塞删除;
    • 对热点 key 再拆逻辑桶,每个桶限制 1k QPS,超量时返回 202 Accepted,客户端指数退避重试。
  3. 存储层
    • MySQL 8.0 分库分表user_id 取模 64 库,单库 150 QPS;
    • 删除 SQL 采用批量 delete … where id in (…) 每次 200 行,二级索引覆盖索引避免回表;
    • 开启binlog_group_commit组提交,把磁盘 fsync 次数降到 1/10。
  4. 异步链路
    • RocketMQ顺序消息,单 Topic 20 队列,批量攒 5 ms或者 512 条提交一次;
    • 下游搜索、风控、日志等系统并行消费,失败时重试队列+死信队列保证最终一致。
  5. 观测与兜底
    • Prometheus 每 5 s 拉一次,P99>800 ms 立即短信告警;
    • 若缓存节点失连,Sentinel 在 50 ms 内触发降级逻辑:只写标记表,后台Cron 每 500 ms 批量删除,保证可用性。”

拓展思考

  1. 如果业务要求强一致而非最终一致,可把缓存与 DB 的删除放在同一分布式事务:用Seata AT 模式NewSQL(如TiDB)的Percolator 事务,但要接受吞吐下降 40% 的代价。
  2. 当数据量达到千亿级,可引入分层存储热数据Redis+SSD冷数据下沉到S3 类对象存储,删除时仅标记元数据物理空间回收由后台GC Agent低峰期扫描,以节省 IOPS
  3. 未来演进可让Agent 系统自主调参:通过强化学习实时观测P99、CPU、IO 三维指标,动态调整批量大小、线程数与限流阈值,实现延迟自优化,把 P99 压到400 ms 以内而无需人工干预。