Redis Conf 2020之提高Redis访问速度最佳实践

来源:Zohaib Sibte Hassan from Doordash and RedisConf 2020 (redisconf.com/) organized by Redis Labs (redislabs.com)

翻译:Wen Hui

转载:中间件小哥

1. Cache stampede问题:

Cache stampede问题又叫做cache miss storm,是指在高并发场景中,缓存同时失效导致大量请求透过缓存同时访问数据库的问题。

image

如上图所示:

服务器a,b 访问数据的前两次请求因为redis缓存中的键还没有过期,所以会直接通过缓存获取并返回(如上图绿箭头所示),但当缓存中的键过期后,大量请求会直接访问数据库来获取数据,导致在没有来得及更新缓存的情况下重复进行数据库读请求 (如上图的蓝箭头),从而导致系统比较大的时延。另外,因为缓存需要被应用程序更新,在这种情况下,如果同时有多个并发请求,会重复更新缓存,导致重复的写请求。

image

针对以上问题,作者提出一下第一种比较简单的解决方案,主要思路是通过在客户端中,通过给每个键的过期时间引入随机因子来避免大量的客户端请求在同一时间检测到缓存过期并向数据库发送读数据请求。在之前,我们定义键过期的条件为:

Timestamp+ttl > now()

现在我们定义一个gap值,表示每个客户端键最大的提前过期时间,并通过随机化将每个客户端的提前过期时间映射到 0 到gap之间的一个值, 这样以来,新的过期条件为:

Timestamp+ttl +(rand()*gap)> now()

通过这种方式,由于不同客户端请求拿到的键过期时间不一样,在缓存没有被更新的情况下,可以在一定程度上避免同时有很多请求访问数据库。从而导致比较大的系统延时。

客户端的实例程序如下:

image

另一种更好的方法是将提前过期时间做一个小的更改,通过取随机函数的对数来将每个客户端检查的键提前过期时间更均匀的分布在0到gap的区间内(因为随机函数取对数为负值,所以整个提前过期的时间也需要取反),从而获得更好的性能提升(具体的数学证明在Optimal Probabilistic Cache Stampede Prevention https://cseweb.ucsd.edu/~avattani/papers/cache_stampede.pdf 这篇文章中)。

image

通过应用以上键的提前过期机制,我们看到整体的cache miss现象有明显的缓解。

image

2. Debouncing

Debouncing在这里指的是在较短时间内如果有多个相同key的数据读请求,可以合并成一个来处理,并同时等待数据的读请求完成。作者在这里介绍了可以使用类似javascript 的promise机制来处理请求,具体的步骤如下:

  1. 每个读请求提供一个L1 Cache Miss函数并返回一个promise,这个promise会去读相应的L2 Cache或数据库(如果L2 Cache也Miss的话)

  2. 当多个读请求使用debouncer访问相同Key id时,只有第一个请求会调用L1 Cache Miss函数,并立即返回一个promise。

  3. 当剩下的读请求到达并且Promise没有返回时,函数会立即返回第一个读请求L1 Cache Miss函数所返回的promise。

  4. 所有读请求都会等待这个Promise完成。

  5. 如果当前的Promise完成并返回,接下来的读请求将重复这个过程。

整体流程如下图程序所示:

image

在Java中,Caffeine Cache()缓存库也用到类似的设计来实现。

作者通过使用benchmark tool进行比较,通过使用debouncing的设计使得系统吞吐量有了较大的提高。如下图所示:

image

3. Big Key

Big Key是指包含数据量很大的键,在具体应用中,有如下几个例子:

  1. 缓存过的编译前的元数据(例如前端使用的试图,菜单等)

  2. 机器学习模型。

  3. 消息队列和具体消息。

  4. 更多的关于Redis流(stream)的例子。

在这种情况下,我们可以通过使用数据压缩算法来解决big key的问题。选择压缩算法的时候我们需要考虑以下几点:

  1. 压缩率(compress ratio)

  2. 是否轻量,不能耗费过多的资源

  3. 稳定性,是否进行过详尽的测试,以及社区支持等。

在选择算法的时候我们需要平衡上述几点,例如不能为了提高1% 的压缩率而使用额外20%资源。

在比较压缩算法的时候,可以使用lzbench(https://github.com/inikep/lzbench)来比较各类压缩算法的性能(https://morotti.github.io/lzbench-web/)。另外压缩算法的性能和具体的数据有直接的关系,所以建议大家自己动手尝试来比较各类压缩算法的性能差异。

具体的例子(doordash):

Chick-Fil-A 的菜单: 64220 bytes(序列化json)

起司公司产品清单: 350333 bytes(序列化json)

即使单独拿出来这些数据进行传输不会有太大问题,但如果有大量类似的公司需要多次传输,那么对网络和CPU负载是相当高的。

在具体选择压缩算法过程中,作者比较了LZ4和Snappy,并得到了以下结论:

  1. 在平均情况下,LZ4比Snappy的压缩率要高一点,但作者使用自己的数据作比较发现结论正好相反,LZ4 38.54% 和Snappy 39.71%

  2. 压缩速率相比两者差不多,LZ4会比Snappy慢一点点。

  3. 再解压方面,LZ4比Snappy快得多,在一些测试场景下会有两倍的差距。

通过以上结论作者选择LZ4 作为菜单传输的压缩算法,并进行Redis Benchmark测试,使用压缩算法可以对Redis的读写吞吐量有很大提高,具体如下:

image

另外整个系统的网络流量使用和系统延时也有比较明显的降低:

image
image

所以作者建议如果使用Redis存储Big Key时,可以使用压缩算法来提高系统吞吐量和降低网络负载。

4. Hot Key

Hot Key(热键)问题指的是在系统中有多个分区(partition),但因为某一个特定的键频繁的被访问,导致所有的请求都会转到某一个特定的分区中,从而导致某个特定分区资源耗尽而其他分区闲置的问题。在一些情况下不能使用L1缓存来解决这个问题,因为在这些场景下你需要不断地从L2 cache或数据库中获取最新的数据。Hot Key问题主要出现在Read Intensive的应用当中。

解决Redis 的 Hot Key问题的一个潜在方案是可以通过主从复制的方式来将读请求分散到多个replica中。如下图:

image

但是这种设计没有从根本解决hot key的问题,所以我们设计系统的目标是尽量使每个请求都分散到不同的cluster nodes中,如下图所示:

image

所以作者提出了如下针对Redis Hot key的解决方案,主要是通过Redis特有的Key Hash Tag来实现的。我们知道, 在Redis集群模式下,Redis会对每个键使用CRC16 算法并取模来决定这个键写在哪个Key Slot中,并存入相应的分区,但如果我们在键的名字中使用大括号{},则只有大括号里面的字符会用来计算键的槽和相应的分区,而不是整个键。举个例子,如果我们有个键:doordash,在正常情况下redis会使用doordash来计算相应的key slot和分区,但如果我们有另外一个键:{copy:0} doordash,我们则只会使用copy:0来计算key slot和分区。以此为基础,我们可以对Hot key做相应的copy如下:

image

Hot Key doordash现在有三个副本,我们可以把这三个副本均匀分布在redis cluster中。然后在写入数据的时候同时写入这三个副本到每一个分区中,在客户端读取过程中,通过生成从0-2随机值然后生成特定的副本key,再去相应的分区中读取值。示例程序如下:

image

在这种方式中相同的键值需要被复制多次在不同的分区中,但因为这个键值会被访问多次,所以这个复制操作也是值得的。

Future

在redis 6中,可以使用RESP3协议和Redis服务器端对客户端缓存的支持,来提高L1缓存的提前逐出时间,并减少使用网络资源。另外,使用proxy可以使客户端请求路由变得更直接。第三点作者提到的是redis 6.0中引入了多线程io,可以显著提高cpu利用率和提高系统吞吐量。

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