Redis服务支持5000万的QPS,有什么好的思路?
Posted 敲代码的程序狗
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Redis服务支持5000万的QPS,有什么好的思路?相关的知识,希望对你有一定的参考价值。
一、题目分析
1. 仅根据qps计算多少机器是不够的
如果一个Redis服务器支持五千万上面的读写请求,那么此题很简单,一个机器,一个Redis进程即可。
但显然没有这样的超级机器,Redis也做不到一个进程就能处理五千万QPS。
那么一个Redis到底能处理多少QPS?
如果是简单的读写,那么一个Redis在一个硬件服务器(只要1个核,CPU为当前主流即可),就能处理Million级别的QPS,即至少百万。
所以,上面的问题好像简单了,我们只要用50个Redis进程就能支持题目所需的五千万,然后在一个50核的机器上跑这五十个进程,即可。
所以,答案好像很简单:一台五十核机器,五十Redis进程。
但是这样的吗?不是的,因为很可能应用环境并不是只有简单的有限量的key,如果key不断增多,那就会出现大麻烦。
2. 瓶颈在哪?
难点在那个两千万QPS的写入(即20M qps write),我来仔细分析给你看。
假设:每个写入是100字节(即key+value之和平均在100Bytes,即0.1KB),而且万一这两千万QPS的写入都是新增数据,而且随后的三千万QPS(即30M qps read)还可能查历史的数据,我们会有什么麻烦?
显然,下一秒将产生 0.1KB * 20M qps = 2GB 新增数据,每天将产生 2GB * 86400 秒 = 173 TB 新增数据。
没有任何一台机器的内存可以支持一天的新增数据容量,更何况服务运行时间以年为单位。
所以,我们只能用到磁盘。
但问题是:如果将数据存盘,Redis是查不到的。
但如果Redis能查到磁盘上的数据,是否能解此题?
RedRock支持Redis存储磁盘,那么我们尝试用RedRock解解此题。
二、首先拆分
假设我们用100台机器,每台跑一个RedRock服务进程。即一个RedRock服务,支持500K qps,其中200K qps write,300K qps read。这样百台机器就可以达到五千万(50M)qps。
首先,每天新增的数据对于每台机器,只有1.7 TB,需要存盘。再加上RedRock采用压缩存盘,虽然压缩率根据数据的不同而压缩效率不一样,但最差的情况下,我们也有信心获得至少50%的压缩率(最好可以到10%)。
所以,对于每台机器,每天会新增最多 0.9 TB数据,然后入磁盘。一年的数据可以到300 TB这个量级。
这个是磁盘的实际最大量,现代的机器配置完全支持一年这个规模的数据存盘。
但实际应用中,很多时候用不到这么大量,可能会有十倍到百倍甚至千倍的缩减,请耐心看下面的分析。
三、检查磁盘带宽够否
上面的分析还太简单,因为磁盘还有一个瓶颈,就是读写速度是否够?
1. 先看磁盘写
对于每台机器,注入的速度是:0.1 KB * 200K qps = 20MB/每秒 的注入速度。
再考虑RedRock用了RocksDB写入,一般会有10倍的写放大,所以,磁盘写所耗费的带宽是 200MB/每秒,再考虑压缩50%,则只有100MB/每秒的写入。
这个对于当前的SSD的性能要求是足够的。
2. 再看磁盘读
30M qps read分摊到100台机器,那么每台机器是300K qps read。
我们假设90%的读,都是热数据,即都在RedRock的内存中直接读取,那么只有10%的读需要到磁盘上。那么实际磁盘的读的带宽需求是(还需考虑50%的压缩):
30K qps read from disk * 0.1 KB * 50% = 1.5MB read/每秒
肯定也够
3. 磁盘读写一起看
对于写的100MB/每秒,RocksDB是顺序sequential模式。
对于读的1.5MB/每秒,RocksDB是随机random模式。
混用是否够,需要测试,因为每个SSD对于读写混合模式的支持是不同的
但我自己的大致判断是:够!不过,建议你最好测试一下。
四、检查热数据的内存容量
还有一个地方需要检查,就是热数据的量,因为热数据都在内存里。
由于每台机器每秒的新注入数据是:20MB。而新写入的数据一般都是热数据。
所以,如果我们希望90%的访问都落在内存里,那么如果是20G内存,我们可以容纳最近1000秒的数据,即17分钟。
即如果是20G内存,我们希望读read的90%都发生在最近新注入的17分钟的数据上。
如果可以扩大到200G内存,那么,就是170分钟,也就是近三个小时。
五、检查key的内存容量
注意,RedRock需要将所有的key都存储在内存里。所以,我们还需要算算key占用多少内存。
对于每台机器,每秒新进入200K个key。
假设key用16个字节表达(UUID),那么,每秒将产生3M字节的key内存,一天将产生276G的key放在内存里。
所以,真正麻烦的地方是key的内存容量。
解决的办法有几个:
扩大机器数量,如果机器是一千台,那么每天每台机器只产生不到30G的key内存量
希望key不都是新key,即有很多重复的,比如:每天的写入里,只有10%的新key,则一天新增的key量,对于100台机器,也就不到30G,1000台机器,不到3G。如果每天的写入,只有1%的新key,则再降低10倍。注意:根据业务实际情况,运行越久,则1%的发生概率越高,即可能发生第一天是10%新增,然后开始递减,最后到一个稳定值1%,甚至更低。
不管如何,我们都必须删除过老的key了。对于100台机器组成的集群(每台机器有300G内存放key),每天只有10%的新key,则可放10天的key,每天只有1%的新key,则可以放100天。对于1000台机器集群,则分别是100天(三分之一年)和1000天(三年)。过老的key,我们必须删除,让key的内存空间减少。
同样地,如果每天新增的key量,只占写入qps的10%或者1%,那么磁盘的容量,一年也不需要上面分析的300TB这个量级,而是同样降低10倍,或者100倍,即最低只需几个TB的磁盘。
六、客户端如何均分负载
下面我们需要让客户端均匀分配负载到这100台机器上。
有三个解决方案:
1. 客户端逻辑做Shard
最简单的算法是:Shard(key) module 100,比如:我们可以用CRC16算法,即 node id = crc16(key) % 100。
好处是:简单。坏处是:动态扩展(比如:增加、减少一台机器)不方便
2. 通过代理proxy做Shard
通过使用诸如Envoy这样的代理(proxy),让代理软件做shard,而且还可以实现一致性Shard(consistent hash),这样集群的增减会简单一些。
坏处在于,通过proxy,会增加一些时延。不过Envoy采用side car模式,理论上增加最低几十个us,一般情况下,不会超过1ms的时延,所以,还是可以考虑的。
3. 用Redis Cluster
RedRock支持Redis的Cluster模式。所以,直接使用Redis Cluster,增减机器都相对简单。
坏处是:如果机器到了千台,不易做Redis Cluster,因为其Gossip协议在这个量级,问题比较大。如果是百台机器,则无问题。
七、如果key+value超过百字节
如果value比较大,比如平均千字节,那么上面的算法还是成立,只是,需要根据value扩大了10倍,相应地,推算(和测试)磁盘的带宽需求以及热数据的内存需求,即上面的百台机器,可能需要变成千台。
八、用zstd压缩算法进一步减小磁盘
RedRock用RocksDB库时,当前代码里只支持了lz4的压缩算法。
实际上,我们可以用zstd算法对于磁盘里的LSM树的最底层进行压缩,这样,将大大减少磁盘的容量,减少多少,请测试。
当然,你需要增加几行代码,让RedRock支持zstd压缩,具体可自行修改 src/rock.c里的init_rocksdb()函数。为了让RedRock依赖的库较少,我没有增加这个特性,你可以自行加入zstd支持,几行代码,并不复杂。
九、热数据的访问不是90%会如何
上面假设90%的访问还是热数据,但万一是80%,甚至70%,会如何?
如果你仔细阅读了RedRock的文档,答案也很简单:
要么时延Latency变差,不能保证P99还是1ms以下。
要么Throughput变差,即每台服务器不能支持500K qps,这时,需要增加机器。
具体如何,请测试,我也不知道。
十、如何加强高可用性HA
虽然我们可以通过Proxy和Redis Cluster,动态地增加机器,但这个过程并不简单,而且有一定风险(TB级的数据从一台机器转移到另外一台或多台机器上,不是一个简单的事)。
所以,考虑加强高可用HA,是一个值得考虑的方案。
这时,就需要用到Master/Slave,即读写分离。
RedRock支持Redis的读写分离和各种集群方案,所以,对于每个shard,我们都可以再补充一台机器做slave,这样就提高了HA。
还有一个好处,对于写,加了slave并不能scale out。但对于读,确可以做到scale out。比如,对于上面的百台机器,如果我们再增加百台机器做slave的话,那么每台机器所承受的读,可以降低到150K qps。
开发者涨薪指南 48位大咖的思考法则、工作方式、逻辑体系以上是关于Redis服务支持5000万的QPS,有什么好的思路?的主要内容,如果未能解决你的问题,请参考以下文章
spring-data-redis 上百万的 QPS 压力太大连接失败,我 TM 人傻了
14_redis如何通过读写分离来承载读请求QPS超过10万+?
深度解析单线程的 Redis 如何做到每秒数万 QPS 的超高处理能力!