Redis latency spikes and the 99th percentile

Salvatore Sanfilippo

Redis 延迟尖峰与 99 分位数

原文由 Salvatore Sanfilippo 发布,订阅该博客

Stripe 那篇关于 Redis 的博客有意思的一点在于,他们贴出了测试过程中采集到的延迟图表。Redis 要将数据持久化到磁盘,就必须调用 fork() 系统调用。通常在物理服务器以及大多数 Hypervisor 上,即使进程很大,fork 也很快。然而 Xen 的 fork 却很慢,因此在某些 EC2 实例类型(其他虚拟服务器提供商也一样)上,每次父进程为了持久化而 fork 时,都可能出现严重的延迟尖峰。Stripe 的那张图在这方面就很能说明问题。

可以想见,如果在 fork 期间进行延迟测试,所有恰好跨越父进程 fork 这一时刻的请求都会被延迟,最高可达一秒(以上图为例,当时的进程大小和 EC2 实例类型就不清楚了)。这会产生一批高延迟的采样,进而影响 99 分位数的统计结果。

为了改善这一表现而去更换实例类型、调整配置或做其他改动,当然是个好主意,而且在某些场景下,哪怕只有一个请求的延迟过高也是不可接受的。不过,很多人似乎没有意识到,每 30 分钟出现一次 1 秒的延迟尖峰(如果使用 AOF 并设置了合适的重写触发条件,间隔还会更长),与在请求集合中均匀分布的延迟尖峰,有着本质的区别。

如果是均匀分布的尖峰,当生成一个页面需要向 Redis 发起多次请求才能完成输出时,一次页面访问就很可能会撞上延迟惩罚:这可能会对服务质量造成很大影响,具体可以参考这篇文章:http://latencytipoftheday.blogspot.it/2014/06/latencytipoftheday-most-page-loads.html

但每运行 30 分钟才出现 1 秒延迟,则完全是另一回事。首先,随着请求量增加,体现良好延迟的分位数反而会变好,因为请求越多,这 1 秒的延迟就越不容易在采样中被过度代表(如果你每分钟只有 1 个请求,其中一个恰好撞上高延迟,它对 99.99 分位数的影响,要远大于每秒 100 个请求时的情况)。

其次,绝大多数页面访问不会受到影响。只有那些请求恰好跨越 fork 调用的用户才会感受到这 1 秒的延迟。其他所有请求,遇到明显高于平均延迟的请求的概率都极低。还要注意的是,即使一个页面访问由 100 个请求组成,只要它撞上了 fork 时刻,最多也只会被延迟一秒,因为一旦 fork() 调用结束,请求就会完成。

归根结底,如果对每一个请求都有严格的延迟要求,那么一个会不时让请求延迟 1 秒的部署显然是个大问题。但如果目标是提供良好的整体服务质量,延迟尖峰的分布方式就会对结果产生巨大影响。Redis 在 Xen 上因 fork 产生的延迟尖峰,在时间轴上是孤立的点,因此它所影响的页面访问比例——即便每个页面访问由大量 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

本文章由 muse-spark-1.2-contributor 进行翻译

评论