Netflix AI团队通过按ID拆分Cassandra分区,将宽分区读取延迟从秒级降至毫秒级
Netflix工程师详细介绍了他们如何在TimeSeries Abstraction中处理Apache Cassandra的宽分区。两种方法协同工作:时间切片重新分区在表级别调整未来分区,而动态分区则在读取路径上按TimeSeries ID检测并拆分过大的分区。检测通过字节计数和Kafka进行,拆分经过校验和验证,布隆过滤器将读取路由到并行子分区。平均读取延迟从秒级降至低双位数毫秒,500MB以上的分区仍保持可用。
Netflix工程团队发布了一种处理Apache Cassandra中宽分区的方法。该研究工作针对Netflix的TimeSeries Abstraction,这是一个用于时间事件数据的平台。
TimeSeries Abstraction以毫秒级延迟摄入和查询PB级的时间事件数据,底层使用Apache Cassandra 4.x。时序数据按标识符和时间范围组织成分区,随着事件累积,分区可能变得“宽”。动态重新分区将过大的分区异步地拆分为较小的子分区,应用程序继续查询相同的逻辑分区,而存储布局透明地演变。
对于大多数数据集,平均读取延迟保持在个位数毫秒。但当分区过宽时,尾部读取延迟上升到秒级,可能导致读取超时、垃圾回收暂停、高CPU利用率和线程排队。TimeSeries服务器还处理非常高的读取吞吐量,使问题更加复杂。Netflix团队希望找到比单纯扩展集群更智能的解决方案。
TimeSeries将数据集分解为离散的时间块:时间切片、时间桶和事件桶。当创建命名空间时,用户指定预期的工作负载特征,供应管道运行蒙特卡洛模拟来选择基础设施和分区配置。这种方法在三种情况下不足:工作负载未知或估计不准确;工作负载随时间和需求变化;存在数据异常值(少数ID收到更多事件)。离散时间切片为前两种情况提供了自然解决方案,但手动调整数千个数据集不可持续。
解决方案1:时间切片重新分区。Cassandra提供nodetool tablehistograms等内省API。后台工作线程监视这些直方图,并在分区大小偏离目标密度时计算调整因子。该密度通常设置为2 MiB到10 MiB。例如,如果过度分区导致高读取放大,工作线程会更新未来时间切片采用更宽的间隔。这减少了读取延迟和超时,但仅在大多数表需要重新分区时有效。
解决方案2:按ID动态分区。这是一个异步管道,按TimeSeries ID拆分宽分区。分为三个阶段:检测、规划和拆分、读取服务。检测在读取路径上进行:每次读取跟踪分区的字节数,当超过阈值时,服务器向Kafka发送事件。优先拆分不可变分区。规划阶段读取整个分区以计算准确拆分计划,支持检查点。拆分子任务使用事件桶分区策略,将更多事件桶分配到同一时间桶。验证通过比较拆分前后的校验和进行,仅当匹配时拆分才算完成。
读取路径使用布隆过滤器(微秒级响应)检查已拆分的分区键,命中时查询路由元数据,将请求路由到较小的子分区。原始宽分区永不删除,作为安全回退。Netflix还通过Data Bridge Spark作业进行离线验证。分阶段推出,包括影子比较模式。
两种解决方案的比较:粒度、触发条件、影响范围、核心机制、检测信号、最佳适用场景等均有不同。用例包括长期用户活动日志、设备遥测、过度供应的新数据集和延迟敏感仪表板。
结果:平均读取延迟从秒级降至低双位数毫秒,尾部延迟从几秒降至约200毫秒,读取超时减少,CPU利用率降低,集群更稳定。对于500MB以上的极端宽行,服务仍能分页查询并保持可用。未来工作包括拆分可变宽分区和重新处理失败的拆分。