twitter如何优化淘汰策略减少25%缓存占用

本文介绍了twitter如何通过优化key的过期淘汰使得在一些集群中,redis的内存使用降低了25%。除此之外,该文章介绍的问题解决思路也有非常大的借鉴意义: 发现问题-检查变更-测试变更-分析变更-选择解决方案。对于解决方案的选择也相当有趣,当你发现修改一行代码可以让性能提升的时候,但是无法搞清楚为啥能提升,那么你是否会选择使用这行代码呢。

Even though removing the if statement caused such a drastic improvement we were uncomfortable making the change without being able to explain why it was better

最终的结果是twitter的工程师并没有使用这行代码,因为当你不知道它的原理的时候,可能表面可以带来提升,但是背后是否会有啥隐患是你无法把握的。

最后twitter使用添加可配置的参数来调节key过期的策略,同时向redis提交了一个PR,但是该pr最终没有合入,redis最终选择了另一个类似的PR,没有合入的原因是

I'm not sure it should be exposed to the user in such terms of iterations / divisor, that are implementation details.

对比两个pr的区别,前一个暴露了除法的实现细节,后一个则是通过直接指定一个值来进行配置,对用户屏蔽了除法的实现。应用配置不应该对用户暴露代码的实现细节,这也是在代码设计上非常重要的一个点。

以下为原文翻译,原文链接 : https://blog.twitter.com/engineering/en_us/topics/infrastructure/2019/improving-key-expiration-in-redis


最近,我们遇到了一个有趣的问题,我们在 Redis 集群中遇到了一些性能问题。在花费大量时间进行调试和测试之后,我们能够通过更改key过期将 Redis 在我们的某些集群中的内存使用量减少多达 25%。

Twitter 在内部运行多个缓存服务。其中之一是由 Redis 支持的。我们的 Redis 集群存储一些 Twitter 最重要用例的数据,例如印象和参与数据、广告支出计数和直接消息。

问题及背景

早在 2016 年初,Twitter 的缓存团队对我们的 Redis 集群的架构进行了大规模更新。核心是从从 Redis 2.4 版到 3.2 版的更新。在此更新之后,出现了几个问题。用户开始看到内存使用与他们预期或配置使用的不一致、延迟增加和key驱逐。key驱逐是一个大问题,因为预计会持久的数据被删除,或者由于缓存miss而回源。

初步调查

受影响的团队与缓存团队一起开始调查。我们发现延迟增加与现在发生的key驱逐有关。当 Redis 收到写入请求但没有内存来保存写入时,它会停止正在做的事情,驱逐一个key,然后保存新的key。我们仍然需要找到导致这些新驱逐的内存使用量增加发生在哪里。

我们怀疑内存中充满了已过期但尚未删除的密钥。有人建议的一个想法是使用扫描,它会读取所有密钥,从而删除过期的密钥。

在 Redis 中有两种方式可以使key过期,主动和被动。扫描将触发被动key过期,当eky被读取时,将检查 TTL,如果过期则将其丢弃并且不返回任何内容。 Redis 文档中描述了版本 3.2 中的主动过期机制。它从一个名为 activeExpireCycle 的函数开始。它运行在 Redis 称为 cron 的内部计时器上,每秒运行几次。该函数的作用是循环遍历每个键空间,检查设置了 TTL 的随机键,如果过期键达到百分比阈值,则重复此过程直到达到时间限制。

这种扫描所有键的想法奏效了,扫描完成后内存使用量下降。 Redis 似乎不再主动使key过期。不幸的是,当时的解决方案是增加集群的大小和更多的硬件,这样key就会分布得更多,并且会有更多的可用内存。这令人失望,因为前面提到的升级 Redis 的项目通过提高效率来减少运行这些集群的大小和成本。

检查redis版本变更

在 2.4 和 3.2 版本之间,activeExpireCycle 的实现发生了变化。在 2.4 中,每次运行时都会检查每个数据库,在 3.2 中,现在可以检查的数据库数量有一个最大值。 3.2 版还为该功能引入了一个快速选项。 “慢”在计时器上运行,“快”在检查事件循环上的事件之前运行。快速到期周期会在某些条件下提前返回,并且函数超时和退出的阈值也较低。时间限制也被更频繁地检查。该函数总共添加了 100 行代码。

进一步分析

最近我们有时间回过头来重新审视这个内存使用问题。我们想探索为什么会出现退化,然后看看我们如何才能更好地使key过期。我们的第一个理论是,在一个 Redis 实例中有这么多的键,采样 20 是不够的。我们要调查的另一件事是 3.2 中引入的数据库限制的影响。

规模和处理分片的方式使得在 Twitter 上运行 Redis 是独一无二的。我们拥有包含数百万个键的大型键空间。这对于 Redis 的用户来说并不典型。分片由一个键空间表示,因此每个 Redis 实例都可以有多个分片。我们的 Redis 实例有很多键空间。分片与 Twitter 的规模相结合,创建了具有大量密钥和数据库的密集后端。

改动内容测试验证

每个循环上的采样数由变量 ACTIVE_EXPIRE_CYCLE_LOOKUPS_PER_LOOP 配置。我决定测试三个值并在其中一个有问题的集群中运行它们,然后运行扫描,并测量内存使用前后的差异。较大的变化表明有大量过期数据等待收集。该测试最初对内存使用有积极的结果。

该测试有一个控件和三个对更多键进行采样的测试实例。数字 500 和 200 是任意的。值 300 基于统计样本大小计算器的输出,其中总键是总体大小。在上面的图表中,即使只看测试实例的起始数字,也很明显它们表现得更好。与运行扫描的百分比差异表明,过期key的开销约为 25%。

尽管对更多key进行采样有助于找到更多过期密钥,但负面延迟影响超出了我们的容忍范围。

上图显示了 99.9% 的延迟(以毫秒为单位)。这表明延迟与采样键的增加相关。橙色使用值 500,绿色使用 300,蓝色使用 200,控件为黄色。线条与上表中的颜色相匹配。

在看到延迟受样本大小的影响后,我想看看是否可以根据要过期的key数量自动调整样本大小。当有更多的键过期时,延迟会受到影响,但是当没有更多的工作要做时,我们会扫描更少的键并更快地执行。

这个想法最有效,我们能够看到内存使用量较低,延迟没有受到影响,并且度量跟踪样本大小显示它随着时间的推移而增加和减少。但是,我们没有采用这种解决方案。它引入了一些在我们的控制实例中没有出现的延迟峰值。代码也有点复杂,难以解释,而且不直观。我们还必须为每个不理想的集群调整它,因为我们希望避免增加操作复杂性。

分析版本间性能差距

我们还想调查 Redis 版本之间的变化。新版本引入了一个名为 CRON_DBS_PER_CALL 的变量。这设置了每次运行此 cron 时将检查的最大数据库数。为了测试它的影响,我们简单地注释掉了这些行

//if (dbs_per_call > server.dbnum || timelimit_exit)
        dbs_per_call = server.dbnum;

这将比较有限制和每次运行检查所有数据库之间的效果。我们的基准测试结果令人兴奋。虽然我们的测试实例只有一个数据库,但从逻辑上讲,这行代码在修改版本和未修改版本之间没有区别。该变量将始终设置。

我们开始研究为什么注释掉的这一行会产生如此巨大的差异。由于这是一个 if 语句,我们首先怀疑的是分支预测。我们利用 gcc 的 __builtin_expect 来改变代码的编译方式。它对性能没有任何影响。

接下来,我们查看了生成的汇编,看看到底发生了什么。

if 语句编译为 3 个重要指令,mov、cmp 和 jg。 mov 会将一些内存加载到一个寄存器中, cmp 将比较两个寄存器并根据结果设置另一个寄存器,而 jg 将根据另一个寄存器的值进行条件跳转。跳转到的代码将是 if 块或 else 中的代码。我把if语句取出来,把编译好的程序集放到Redis中。然后我通过注释掉不同的行来测试每条指令的效果。我测试了 mov 指令,看看是否存在加载内存或 cpu 缓存问题的某种性能问题,但没有区别。我测试了 cmp 指令,没有任何区别。当我在包含 jg 指令的情况下运行测试时,延迟又回到了未修改的水平。在找到这个之后,我测试了它是只是跳转还是这个特定的 jg 指令。我添加了非条件跳转指令 jmp 来跳转,然后跳转回正在运行的代码,并且没有性能损失。

我们花了一些时间查看不同的性能指标,并尝试了 cpu 手册中列出的一些自定义指标。关于为什么一条指令会导致这样的性能问题,尚无定论。我们有一些与指令缓存缓冲区和执行跳转时的 cpu 行为相关的理论,但时间已用完,并决定将来可能会回到这一点。

最终解决方案

既然我们对问题的原因有了更好的了解,我们就需要为这个问题选择一个解决方案。我们的决定是进行简单的修改,即能够在启动选项中配置稳定的样本量。我们能够找到一个在延迟和内存使用之间进行良好权衡的值。尽管删除 if 语句导致了如此巨大的改进,但我们在无法解释为什么它更好的情况下做出改变感到不舒服。

该图是部署到的第一个集群的内存使用情况。隐藏在橙色后面的粉红色顶线是集群的内存使用中值。橙色的顶行是一个控制实例。图表的中间部分是新变化的金丝雀。第三部分显示了一个控制实例被重新启动以与金丝雀进行比较。重新启动后控件上的内存使用量迅速增加。

最终这是一项相当大的调查,其中包括一些工程师和多个团队,但集群大小减少了 25% 是一个非常好的结果,我们学到了很多东西!我们想再看一下这段代码,看看我们可以在其他一些专注于性能和调优的团队的帮助下进行哪些优化。似乎还有很多潜在的收获。

Reference:
https://blog.twitter.com/engineering/en_us/topics/infrastructure/2019/improving-key-expiration-in-redis
https://github.com/redis/redis/pull/5843
https://github.com/redis/redis/commit/84b01f63dbe28d5541e09313d35deacf4344ab16

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,324评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,303评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,192评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,555评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,569评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,566评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,927评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,583评论 0 257
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,827评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,590评论 2 320
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,669评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,365评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,941评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,928评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,159评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,880评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,399评论 2 342

推荐阅读更多精彩内容