
优步的工程师更新了CacheFront的架构,以满足每秒钟1.5亿次的读取操作,同时确保了更强的一致性。这次更新解决了延迟敏感的服务中陈旧数据的读取问题,并通过引入新的写入一致性协议、与Docstore的更紧密协作以及对优步 Flux 流系统进行改进,支持了不断增长的需求。
在早期的CacheFront设计中,通过消除请求重复并将频繁访问的键缓存在靠近应用服务的地方,实现了每秒 4000 万次读取的高吞吐量。虽然这种模型在可扩展性方面非常有效,但它缺乏强大的端到端一致性,这对于需要最新数据的工作负载来说是无法满足需求的。缓存失效依赖于生存时间(time-to-live,TTL)和变更数据捕获(change data capture,CDC),这实现了最终一致性并延迟了更新的可见性。这会造成特定的问题:在读取-拥有-写入(read-own-write)不一致性中,一条被读取、缓存然后更新的数据行可能会继续提供陈旧的值,直到它无效或过期。负缓存(存储“未找到”的结果)即使在行被插入后也可能返回错误的未命中结果,这可能会破坏读取-拥有-插入(read-own-insert)不一致性中的服务逻辑。

CacheFront 以前的读写失效路径(图片来源:Uber Engineering的博客文章)
新的实现引入了写入一致性协议以及位于查询引擎和 Flux(优步的流更新系统)之间的去重层。现在每个 CacheFront 节点在提供响应之前都会与 Docstore 验证数据的新鲜度。存储引擎层包括用于已删除行的墓碑标记和用于MySQL会话分配的严格单调时间戳。这些机制允许系统在提交前高效地识别和读取所有修改过的键,包括删除操作,确保即使在高负载下也不会提供陈旧的数据。

改进后的 CacheFront 写路径与失效(图片来源:Uber Engineering的博客文章)
当事务完成时,存储引擎会返回提交时间戳和受影响行键的集合。在这些响应上注册的回调会立即通过写入失效标记来使 Redis 中先前缓存的条目失效。Flux 继续尾随MySQL binlogs并执行异步缓存填充。这三种缓存填充机制:直接查询引擎更新、失效标记和 TTL 过期,结合 Flux 尾随,协同工作以便在支持极高读取吞吐量的同时保持强一致性。
优步的工程师Preetham Narayanareddy和 Eli Pozniansky解释了改进背后的动机:
对更高的缓存命中率和更强的一致性保证的需求在不断增加。使用 TTL 和 CDC 进行缓存失效的最终一致性在某些用例中成为了限制因素。
通过这次集成,优步的工程师还能够弃用并移除之前引入的专用API,减少了运维复杂性并简化了系统。
优步工程师增强了遥测和可观测性仪表板,以监控缓存健康和实时 binlog 尾随。缓存分片被重新组织以均匀分配负载。Cache Inspector 工具构建在与 Flux 相同的 CDC 管道上,它会将 binlog 事件与缓存中存储的条目进行比较。这些更新允许将表的 TTL 延长至 24 小时,将缓存命中率提高到 99.9%以上,同时保持低延迟。
查看英文原文:Uber Achieves 150M Reads per Second with CacheFront Improvements
评论