Airbnb 亿级流量的限流架构

发布时间:2026/6/30 23:23:37
Airbnb 亿级流量的限流架构 写在前面如果你做过任何线上服务的限流大概会写过这种代码ifredis.incr(fqps:{caller})LIMIT:return429按 caller 配 QPS 上限超了直接 429。简单、有效很多业务都是这么干的。但 Airbnb 在 MusselAirbnb的核心 KV 存储上跑了几年这套方案之后发现这在阻止系统挂掉这件事上是合格的但不能让系统在压力下表现最好这篇文章我们来讲一下Airbnb 是怎么把 Mussel 的静态 QPS 限流重构成自适应流量管理系统目标从防止崩溃变成压力下保留尽可能多的好流量。为什么静态 QPS 限流不够用?先看看 Airbnb 的两个真实问题问题 1请求之间的成本差异巨大。同样是一个请求可能是读单条 row也可能是扫 10 万条 row。它们在 QPS 计数器里都算一次。结果是什么低成本调用方一直被限流高成本调用方反而过得很好——因为前者很容易把 QPS 打满后者一秒钟就发几个但是把后端打爆。问题 2流量分布偏斜。某个热点 key 突然被狂打比如一个被分享出去的房源这时候哪怕 caller 的总 QPS 没超单个 shard 已经被打爆了。从这两个问题我们可以发现QPS 其实跟系统真实压力并没有太紧密。QPS更多是在假设每个请求成本一样、每个 key 压力均匀但真实的场景并不会每个请求都一样所以静态限流没法满足真实系统需要在边界附近精细调度。RARC 按真实成本算请求Airbnb 引入了一个叫RARCResource-Aware Rate Control的机制核心是用request unitRU代替 QPS 当限流单位公式如下每种操作有个基础 RU读 1、写 6写更贵加上 bytes 和延迟等等。权重是通过离线 load test 校准出来的往集群里灌不同 size 的请求观察后端的真实 CPU/IO/磁盘表现来反推权重。每个调用方有自己的 RU 配额按 token bucket 形式补给。一个请求执行完才知道它真实消耗了多少 RU然后从 bucket 里扣。很类似现在一些大模型的token计费。这套系统每个 dispatcher pod本地维护自己的 token bucket不做跨 pod 同步。牺牲严格全局精确省掉了一个分布式协调系统的麻烦。Airbnb 觉得客户端流量自然在多 pod 间负载均衡统计意义上的 RU 消耗会均匀本地 token bucket 已经够用。这样就能解决上面说的两个问题一个 100k rows 的扫描会被记上几百 RU正常的 small read 只记 1 RU调用方再也不能用我 QPS 没超这种话术绕过限流。load shedding 基于实时信号的负载抛弃光有 RU 还不够RU 只是个配额但当系统真的被打到压力极限的时候我们需要主动丢掉一部分请求来保住核心。这就是load shedding负载抛弃。Airbnb 的做法是把 load shedding 拆成三个实时信号配合信号 1criticality tier关键度分层系统会提供一个明确的优先级骨架每个调用方在配置里被打成 T0~T3shedding 的时候先丢 T3再丢 T2T1 和 T0 留到最后。信号 2latency ratioAirbnb 会给 shard 的健康度算一个延迟指标ratiop95_latency_short_window/p95_latency_long_window比如 10s 短窗口的 p95 除以 5min 长窗口的 p95ratio ≈ 1当前延迟跟平时差不多正常。ratio 0.3 或 3系统在剧烈变化特别是 ratio 超过某阈值代表当前比平时慢很多这时候就需要提高 RU 单价来限制流量。⚠️ 注意这里用比率而不是绝对值是为了自适应不同 shard有的 shard 平时本来就慢一点比如读多冷数据的绝对延迟阈值不通用比率归一化掉了baseline的不同。信号 3CoDel 队列控制CoDelControlled Delay是一个经典的网络队列管理算法思路是看请求在队列里排队多久sojourn time超过阈值就主动丢请求。Airbnb 把 CoDel 接进 Mussel 的请求队列当队列里堆积的请求 sojourn time 超过阈值直接丢最老的。这个跟 latency ratio 配合能在毫秒级别检测到压力异常。这里我给你最直接、最简洁、最不绕弯子、最一针见血的一句话总结三个信号一起配合tier 决定丢谁、ratio 决定涨多少价来限流、CoDel 决定什么时候必须丢。hot-key defence 单点风暴防御最后一类问题是 hot key某个 key 突然被狂打把它所在的 shard 单独打爆。Airbnb 用了三步防线Step 1Space-Saving 算法做 top-k 计数。Space-Saving 是一种 streaming 算法O(k) 内存就能近似估算key的频率。每个 dispatcher 自己跑一份不需要全局聚合单 pod 的视角就能识别出对自己来说的热点。Step 2超阈值识别为 hot。某个 key 的频率超过预设阈值标记为 hot key进入下一阶段处理。Step 3进程内缓存 request coalescing。热点 key 进进程内 LRUTTL 大概 3 秒。同一个 key 的并发请求合并成一个真实后端请求同时来 1000 个查同一个 key 的请求后端只看到 1 个其他 999 个拿同一个 result就是 singleflight 机制。⚠️注意3 秒 TTL 是非常细的取舍。短了 cache hit 不够挡不住热点风暴长了数据 staleness 增加影响一致性。Airbnb 选 3 秒是因为 Mussel 的写入主流向已经走 Kafka 异步3 秒级别的滞后对调用方影响小。这个数字不能盲抄——你的系统的可接受 staleness 决定了你自己的 TTL。Airbnb模拟约 1M QPS 打到单个 key 上经过这三步后后端实际看到的流量被压到了几乎察觉不到。最后我们把整套自适应流量管理系统从上往下捋一遍请求进来 →RARC估算 RU 成本 → 扣 token bucket。同时 →hot-key 识别 进程内缓存合并。同时 →CoDel 监控队列 sojourn time latency ratio 监控 shard 健康。系统压力上来 → 按criticality tier主动丢 T3/T2 流量保住 T0/T1。整体效果p99 在压力下更稳。真实 hot key 风暴下后端无感。不需要 oncall 半夜爬起来调 QPS。最后一条才是最重要的自适应的本质就是把 oncall 从循环里踢出去。参考https://medium.com/airbnb-engineering/from-static-rate-limiting-to-adaptive-traffic-management-in-airbnbs-key-value-store-29362764e5c2