1。题目分析1.根据qps来计算多少台机器是不够的如果一台Redis服务器支持5000万以上的读写请求,那么这道题就很简单了,一台机器,一个Redis进程就够了。但是显然没有这样的超级机器,Redis一个进程无法处理5000万QPS。那么一个Redis能处理多少QPS呢?如果是简单的读写,那么一个硬件服务器上的Redis(只要1核,CPU是目前主流),可以处理Million级别的QPS,也就是至少百万。因此,上面的问题看起来很简单。我们只需要用50个Redis进程来支撑问题需要的5000万,然后在50核的机器上运行这50个进程。所以,答案似乎很简单:一台50核机器,50个Redis进程。但是是这样吗?不,因为应用程序环境很可能没有简单的有限数量的密钥。如果key的数量继续增加,就会有很大的麻烦。2、瓶颈在哪里?难点在于2000万QPS的写入(即20Mqps写入)。让我为您仔细分析一下。假设:每次写入100字节(即key+value之和平均为100Bytes,即0.1KB),假设2000万QPS写入的是新数据,后续3000万QPS(即30M)qpsread)还可以查看历史数据,我们会有什么麻烦呢?显然,下一秒会产生0.1KB20Mqps=2GB的新数据,每天会产生2GB86400秒=173TB的新数据。没有任何一台机器的内存可以支持一天的新数据容量,更不用说以年为单位的服务运行时间了。所以,我们只能使用磁盘。但问题是:如果保存了数据,Redis就找不到了。但是如果Redis能够找到磁盘上的数据,是不是就可以解决这个问题呢?RedRock支持Redis存储盘,所以我们尝试用RedRock来解决这个问题。2、首先假设我们使用100台机器,每台机器运行一个RedRock服务进程。即一个支持500Kqps的RedRock服务,包括200Kqps写入和300Kqps读取。这样一百台机器就可以达到五千万(50M)的qps。首先,每天新增数据每台机器只有1.7TB,需要保存。此外,RedRock使用压缩存储。虽然压缩率因数据而异,但在最坏的情况下,我们也有信心可以获得至少50%(最好是10%)的压缩率。因此,对于每台机器,每天最多会添加0.9TB的数据,然后存储在磁盘上。一年的数据可以达到300TB量级。这是实际的最大磁盘数量,现代机器配置完全支持一年规模的数据存储。但在实际应用中,往往用不到这么大的量,可能会出现十倍到一百倍甚至上千倍的减少。请耐心阅读以下分析。3、检查磁盘带宽是否足够上面的分析过于简单,因为磁盘还有一个瓶颈,就是读写速度是否足够?1.先看磁盘写入。对于每台机器,注入速度为:0.1KB*200Kqps=20MB/秒注入速度。考虑到RedRock使用RocksDB写入,一般会有10倍的写入放大,所以磁盘写入消耗的带宽是200MB/秒,考虑到50%的压缩,只写入了100MB/秒。这足以满足当前SSD的性能要求。2.看diskread30Mqpsread分配给100台机器,那么每台机器都是300Kqpsread。我们假设90%的读取都是热数据,即都是直接在RedRock的内存中读取,那么只有10%的读取需要上磁盘。那么实际的磁盘读取带宽需求是(50%压缩也需要考虑):30Kqpsreadfromdisk0.1KB50%=1.5MBread/second肯定够了。每秒,RocksDB处于顺序模式。对于1.5MB/sec的读取,RocksDB处于随机模式。混合使用是否足够需要测试,因为每个SSD支持不同的读写混合模式,但我自己的总体判断是:足够了!但是,建议您对其进行测试。4、查看热点数据的内存容量还有一个地方需要查看,就是热点数据的数量,因为热点数据都在内存中。由于每台机器每秒新注入的数据是:20MB。新写入的数据一般都是热点数据。所以,如果我们想让90%的访问都落在内存中,那么如果是20G内存,我们可以容纳最近1000秒的数据,也就是17分钟。即如果是20G内存,我们希望90%的读取发生在新注入的17分钟数据上。如果能扩展到200G内存,那么就是170分钟,也就是将近三个小时。5.查看密钥的内存容量注意RedRock需要将所有密钥存储在内存中。因此,我们还需要计算key占用了多少内存。对于每台机器,每秒输入200K个新密钥。假设密钥用16字节(UUID)表示,那么每秒会产生3M字节的密钥内存,每天内存中会产生276G的密钥。所以,真正麻烦的是钥匙的记忆容量。有几种解决方案:扩大机器数量。如果有1000台机器,每台机器每天只会产生不到30G的密钥内存。希望key不是全是新key,也就是有很多重复的,比如:每天只写10%的新key,100台机器一天新增key不到30G,1000台机器不到3G。如果每天只写入1%的新密钥,将再减少10倍。注:根据实际业务情况,运行时间越长,出现1%的概率越高,即可能出现第一天涨10%,然后开始下降,最后达到一个稳定值1%,甚至更低。无论如何,我们必须删除旧密钥。对于100台机器组成的集群(每台机器有300G内存存储key),如果每天只有10%的新key,那么key可以存储10天,如果只有1%的新key被每天存储,则密钥可以存储100天。对于1,000台机器的集群,这些时间分别为100天(一年的三分之一)和1,000天(三年)。我们必须删除太旧的密钥,以减少密钥的存储空间。同理,如果每天新增的key数量只占写入qps的10%或1%,那么磁盘容量也不需要是上面分析的300TB量级,也减少了10倍,或100倍,即低至几TB磁盘。6.客户端如何分担负载接下来,我们需要让客户端将负载平均分配给100台机器。解决方案有3种:1.client逻辑做shard最简单的算法是:Shard(key)module100,例如:我们可以用CRC16算法,即nodeid=crc16(key)%100。好处:简单。缺点是:动态扩容(例如:增减一台机器)不方便2.通过代理做分片通过使用代理(proxy)比如Envoy,让代理软件做分片,也可以实现一致性分片(consistenthash),这样集群的增减会更简单。缺点是通过代理,会增加一些延迟。但是Envoy采用的是sidecar模式,理论上至少增加了几十个us。一般情况下,延迟不会超过1ms,所以还是可以考虑的。3、使用RedisClusterRedRock支持RedisCluster模式。因此,直接使用RedisCluster,增删机器比较简单。缺点是:如果机器数量达到千台,RedisCluster的搭建就不太容易了,因为Gossip协议就在这一层,问题比较大。如果是一百台机器,没有问题。7、key+value超过100字节,如果value比较大,比如平均千字节,那么上面的算法仍然有效,但是需要根据value扩大10倍。相应的,计算(测试)磁盘的带宽需求和热数据的内存需求,也就是上面的百台机器可能需要变成千台。8.使用zstd压缩算法进一步减少磁盘。RedRock使用RocksDB库时,目前代码只支持lz4压缩算法。实际上,我们可以使用zstd算法对磁盘中LSM树的底层进行压缩,这样磁盘的容量就会大大减少。请测试它减少了多少。当然,你需要添加几行代码来让RedRock支持zstd压缩。具体可以自行修改src/rock.c中的init_rocksdb()函数。为了让RedRock少依赖一些库,我没有加这个功能,大家可以自己加zstd支持,几行代码,不复杂。9、热点数据的访问不是90%会怎样假设90%的访问还是热点数据,如果是80%,甚至70%怎么办?如果仔细阅读RedRock的文档,答案也很简单:要么延迟变差,不能保证P99还在1ms以下。要么吞吐量变差,即每台服务器无法支持500Kqps,这时候就需要增加更多的机器。具体如何,请测试,我不知道。10、如何加强高可用HA虽然我们可以通过Proxy和RedisCluster动态增加机器,但是这个过程并不简单,存在一定的风险(TB级的数据从一台机器转移到另一台或多台机器不是一件简单的事情事情)。因此,考虑增强高可用HA是一个值得考虑的解决方案。这时候就需要使用Master/Slave,即读写分离。RedRock支持Redis读写分离和多种集群方案。因此,对于每个分片,我们可以添加另一台机器作为从机,从而提高HA。还有一个好处就是对于写来说,添加slave是不能scaleout的。但是对于阅读来说,确实是可以横向扩展的。比如上面的100台机器,如果我们再增加100台机器作为slave,那么每台机器的读负载可以降低到150Kqps。
