Redis latency spikes and the 99th percentile

Salvatore Sanfilippo

Redis 延迟尖峰与第 99 百分位

Stripe 那篇关于 Redis 的博客文章有一个很有意思的地方:他们附上了测试期间获得的延迟图表。为了将数据持久化到磁盘,Redis 需要调用 fork() 系统调用。通常情况下,在物理服务器以及大多数虚拟机管理程序上进行 fork 操作都很快,即使进程很大也是如此。然而 Xen 的 fork 速度很慢,因此在某些 EC2 实例类型上(其他虚拟服务器提供商也存在同样的问题),每当父进程为了持久化到磁盘而执行 fork 时,就可能出现严重的延迟尖峰。Stripe 的图表在这一点上非常直观。

可以想见,如果你恰好在 fork 发生时进行延迟测试,那么所有跨越父进程执行 fork 这一时刻的请求都会被延迟长达一秒(以上图为例,我不确定当时的进程大小和 EC2 实例类型)。这会产生大量高延迟的样本,并影响第 99 百分位(99th percentile)的结果。

通过更换实例类型、调整配置或部署方式等手段来改善这一行为是个好主意,而且在某些使用场景下,哪怕只有一个请求延迟过高都是不可接受的。不过,每 30 分钟出现一次的 1 秒延迟尖峰(如果使用 AOF 并配置了合适的重写触发条件,间隔可能更长),与均匀分布在请求集合中的延迟尖峰相比有着本质区别——这一点显然并不显而易见。

如果延迟尖峰是均匀分布的,而生成一个页面需要向 Redis 服务器发起多个请求才能产生输出,那么一次页面访问就很可能遭遇延迟惩罚:这会在很大程度上影响服务质量,参见这个链接:http://latencytipoftheday.blogspot.it/2014/06/latencytipoftheday-most-page-loads.html

但每 30 分钟才出现一次的 1 秒延迟则完全是另一回事。首先,随着请求数量的增加,低延迟部分的百分位表现反而会变得更好,因为请求越多,这一秒的延迟就越不可能在样本中被过度代表(如果你的系统每分钟只有 1 个请求,而其中一个恰好碰上了高延迟,它对第 99.99 百分位的影响会远大于每秒 100 个请求的情况)。

其次,绝大多数页面访问都不会受到影响。唯一会看到这 1 秒延迟的用户,是那些发出跨越 fork 调用时刻的请求的人。所有其他请求命中一个延迟显著高于平均值的请求的概率都极低。另外还要注意,即使某次页面访问恰好跨越了 fork 时刻、并且由 100 个请求组成,它的延迟也不会超过一秒,因为一旦 fork() 调用结束,这些请求就会立即完成。

这里的结论是:如果对每一个单独的请求都有严格的延迟要求,那么一个偶尔会让请求延迟 1 秒的部署方案显然是个大问题。但如果目标是提供良好的服务质量,延迟尖峰的分布方式就会对结果产生巨大影响。由于 Xen 上 fork 导致的 Redis 延迟尖峰只是时间轴上的孤立点,因此它们只会影响一定比例的页面访问——即使这些页面访问由大量 Redis 请求组成——受影响的比例与延迟尖峰占总时间的比例成正比。在本例中就是每 1800 秒中有 1 秒,所以只有 0.05% 的页面访问会受到影响。

延迟特性很难用单一指标来刻画:完整的百分位曲线以及尖峰的分布情况,能够提供更全面的图景。一般来说,好的经验法则是开展研究的好起点,“平均延迟是一个糟糕的指标”这一说法通常也是成立的。然而把一条经验法则奉为绝对真理也有其弊端,因为许多复杂的事物依然复杂,无论我们多么想把它们过度简化,它们仍然需要仔细审视。

与此同时,EC2 实例上的 fork 延迟是当今最流行的运行环境之一中 Redis 用户最糟糕的体验之一,所以我从现在开始会定期在 EC2 上测试 Redis:我们很快会在 Redis 官方文档中加入针对 EC2 的优化页面,并提供一种以更安全的方式运行禁用了持久化的主从副本的方法。

如果你现在就需要部署禁用了持久化的 EC2 + Redis 主节点,最简单易行的“快速修复”办法是禁用 Redis 实例的自动重启,并使用 Sentinel 进行故障转移,这样崩溃的主节点就不会自动恢复可用状态,而是由 Sentinel 执行故障转移。系统管理员可以在确认故障转移成功、已有新的活动主节点之后,再手动重启原主节点。

编辑:请务必看看 Hacker News 上那个包含有关 EC2、Xen 和 fork 耗时的有趣信息的讨论帖:https://news.ycombinator.com/item?id=8532851。此外,并非所有 EC2 实例都一样,某些实例类型提供的 fork 耗时可媲美裸机系统:https://redislabs.com/blog/testing-fork-time-on-awsxen-infrastructure#.VFJQ-JPF8yF