Redis核心技术与实战
蒋德钧
中科院计算所副研究员
新⼈⾸单¥29.9
6043 人已学习
课程目录
已更新 10 讲 / 共 50 讲
0/4登录后,你可以任选4讲全文学习。
开篇词 (1讲)
开篇词 | 这样学Redis,才能技高一筹
免费
基础篇 (9讲)
01 | 基本架构:一个键值数据库包含什么?
02 | 数据结构:快速的Redis有哪些慢操作?
03 | 高性能IO模型:为什么单线程Redis能那么快?
04 | AOF日志:宕机了,Redis如何避免数据丢失?
05 | 内存快照:宕机后,Redis如何实现快速恢复?
06 | 数据同步:主从库如何实现数据一致?
07 | 哨兵机制:主库挂了,如何不间断服务?
08 | 哨兵集群:哨兵挂了,主从库还能切换吗?
09 | 切片集群:数据增多了,是该加内存还是加实例?
Redis核心技术与实战
15
15
1.0x
00:00/00:00
登录|注册

09 | 切片集群:数据增多了,是该加内存还是加实例?

蒋德钧 2020-08-24
你好,我是蒋德钧。今天我们来学习切片集群。
我曾遇到过这么一个需求:要用 Redis 保存 5000 万个键值对,每个键值对大约是 512B,为了能快速部署并对外提供服务,我们采用云主机来运行 Redis 实例,那么,该如何选择云主机的内存容量呢?
我粗略地计算了一下,这些键值对所占的内存空间大约是 25GB(5000 万 *512B)。所以,当时,我想到的第一个方案就是:选择一台 32GB 内存的云主机来部署 Redis。因为 32GB 的内存能保存所有数据,而且还留有 7GB,可以保证系统的正常运行。同时,我还采用 RDB 对数据做持久化,以确保 Redis 实例故障后,还能从 RDB 恢复数据。
但是,在使用的过程中,我发现,Redis 的响应有时会非常慢。后来,我们使用 INFO 命令查看 Redis 的 latest_fork_usec 指标值(表示最近一次 fork 的耗时),结果显示这个指标值特别高,快到秒级别了。
这跟 Redis 的持久化机制有关系。在使用 RDB 进行持久化时,Redis 会 fork 子进程来完成,fork 操作的用时和 Redis 的数据量是正相关的,而 fork 在执行时会阻塞主线程。数据量越大,fork 操作造成的主线程阻塞的时间越长。所以,在使用 RDB 对 25GB 的数据进行持久化时,数据量较大,后台运行的子进程在 fork 创建时阻塞了主线程,于是就导致 Redis 响应变慢了。
确认放弃笔记?
放弃后所记笔记将不保留。
新功能上线,你的历史笔记已初始化为私密笔记,是否一键批量公开?
批量公开的笔记不会为你同步至部落
公开
同步至部落
取消
完成
0/1000字
划线
笔记
复制
© 版权归极客邦科技所有,未经许可不得传播售卖。 页面已增加防盗追踪,如有侵权极客邦将依法追究其法律责任。
该试读文章来自付费专栏《Redis核心技术与实战》,如需阅读全部文章,
请订阅文章所属专栏新⼈⾸单¥29.9
立即订阅
登录 后留言

精选留言(24)

  • Kaito
    Redis Cluster不采用把key直接映射到实例的方式,而采用哈希槽的方式原因:

    1、整个集群存储key的数量是无法预估的,key的数量非常多时,直接记录每个key对应的实例映射关系,这个映射表会非常庞大,这个映射表无论是存储在服务端还是客户端都占用了非常大的内存空间。

    2、Redis Cluster采用无中心化的模式(无proxy,客户端与服务端直连),客户端在某个节点访问一个key,如果这个key不在这个节点上,这个节点需要有纠正客户端路由到正确节点的能力(MOVED响应),这就需要节点之间互相交换路由表,每个节点拥有整个集群完整的路由关系。如果存储的都是key与实例的对应关系,节点之间交换信息也会变得非常庞大,消耗过多的网络资源,而且就算交换完成,相当于每个节点都需要额外存储其他节点的路由表,内存占用过大造成资源浪费。

    3、当集群在扩容、缩容、数据均衡时,节点之间会发生数据迁移,迁移时需要修改每个key的映射关系,维护成本高。

    4、而在中间增加一层哈希槽,可以把数据和节点解耦,key通过Hash计算,只需要关心映射到了哪个哈希槽,然后再通过哈希槽和节点的映射表找到节点,相当于消耗了很少的CPU资源,不但让数据分布更均匀,还可以让这个映射表变得很小,利于客户端和服务端保存,节点之间交换信息时也变得轻量。

    5、当集群在扩容、缩容、数据均衡时,节点之间的操作例如数据迁移,都以哈希槽为基本单位进行操作,简化了节点扩容、缩容的难度,便于集群的维护和管理。

    另外,我想补充一下Redis集群相关的知识,以及我的理解:

    Redis使用集群方案就是为了解决单个节点数据量大、写入量大产生的性能瓶颈的问题。多个节点组成一个集群,可以提高集群的性能和可靠性,但随之而来的就是集群的管理问题,最核心问题有2个:请求路由、数据迁移(扩容/缩容/数据平衡)。

    1、请求路由:一般都是采用哈希槽的映射关系表找到指定节点,然后在这个节点上操作的方案。

    Redis Cluster在每个节点记录完整的映射关系(便于纠正客户端的错误路由请求),同时也发给客户端让客户端缓存一份,便于客户端直接找到指定节点,客户端与服务端配合完成数据的路由,这需要业务在使用Redis Cluster时,必须升级为集群版的SDK才支持客户端和服务端的协议交互。

    其他Redis集群化方案例如Twemproxy、Codis都是中心化模式(增加Proxy层),客户端通过Proxy对整个集群进行操作,Proxy后面可以挂N多个Redis实例,Proxy层维护了路由的转发逻辑。操作Proxy就像是操作一个普通Redis一样,客户端也不需要更换SDK,而Redis Cluster是把这些路由逻辑做在了SDK中。当然,增加一层Proxy也会带来一定的性能损耗。

    2、数据迁移:当集群节点不足以支撑业务需求时,就需要扩容节点,扩容就意味着节点之间的数据需要做迁移,而迁移过程中是否会影响到业务,这也是判定一个集群方案是否成熟的标准。

    Twemproxy不支持在线扩容,它只解决了请求路由的问题,扩容时需要停机做数据重新分配。而Redis Cluster和Codis都做到了在线扩容(不影响业务或对业务的影响非常小),重点就是在数据迁移过程中,客户端对于正在迁移的key进行操作时,集群如何处理?还要保证响应正确的结果?

    Redis Cluster和Codis都需要服务端和客户端/Proxy层互相配合,迁移过程中,服务端针对正在迁移的key,需要让客户端或Proxy去新节点访问(重定向),这个过程就是为了保证业务在访问这些key时依旧不受影响,而且可以得到正确的结果。由于重定向的存在,所以这个期间的访问延迟会变大。等迁移完成之后,Redis Cluster每个节点会更新路由映射表,同时也会让客户端感知到,更新客户端缓存。Codis会在Proxy层更新路由表,客户端在整个过程中无感知。

    除了访问正确的节点之外,数据迁移过程中还需要解决异常情况(迁移超时、迁移失败)、性能问题(如何让数据迁移更快、bigkey如何处理),这个过程中的细节也很多。

    Redis Cluster的数据迁移是同步的,迁移一个key会同时阻塞源节点和目标节点,迁移过程中会有性能问题。而Codis提供了异步迁移数据的方案,迁移速度更快,对性能影响最小,当然,实现方案也比较复杂。
    2020-08-24
    9
    30
  • Darren
    我认为有以下几点:
    1、存在表的话,存在单点问题,即使部署多份,存在数据一致性问题,提高了复杂度;
    2、即使解决了第一个问题,但是Redis主打的是快,表的读写并发问题处理;
    3、key对应的是实例,对应关系粒度太大;

    4、用key做hash避免依赖别的功能或者服务,提供了整体的内聚性;
    5、在做Redis集群,为了数据分配均匀,进行一致性哈希的时候,虚拟节点和真实节点之间还有对应关系,存在多级映射关系,增加了耗时,影响Redis主线程的执行速度。
    2020-08-24
    6
  • 小宇子2B
    1.让key在多个实例上分布更均匀
    2.需要rehash的时候,还要去修改这个对应关系表,代价有点大
    3.存在表里,key的数量太大,表的维护是个问题
    2020-08-24
    4
  • Monday
    思考题:
    1、使用CRC这个hash函数原因
    1)hash从查询slot的时间复杂度上讲,CRC为O(1);存表(理解为有序数组或跳表),再快也就是O(Log n)
    2)hash从存储映射关系的空间复杂度上讲,CRC为O(1);存表,至少也得O(n),若是跳表还得存额外的索引

    另外我有两个问题咨询下老师,望答复,谢谢!
    1、Redis切片集群使用CRC这个hash函数先获取到具体的slot,然后在具体的slot中,是不是再通过另一个hash函数访问Key对应的值?类似于Java结构:HashMap<String, HashMap<String,Object>>
    2、Redis的slot数量为什么是16384=2^14个,如果用2B来存长度也是2^16=65536个啊?

    2020-08-24
    1
  • test
    用表保存key和实例对应关系的话表数据量太大了,而且不灵活。
    2020-08-24
    1
  • yyl
    解答:
    1. 引入哈希槽,将key的分布与具体的Redis实例解耦,有利于Redis数据的均衡分布。
    2. 不采用哈希槽的话,Redis实例的扩容和缩容,需要针对无规则的key进行处理,实现数据迁移。此外,需要引入负载均衡中间件来协调各个Redis实例的均衡响应,确保数据的均匀分布;中间件需要保存key的分布状态,保证key的查询能够得到响应。
    增加了Redis系统的复杂性 与 可维护性。

    看到问题后的第一反应,理解不够深刻,讲述不够清楚。贵在思考啦😜
    2020-08-25
  • Vincent
    请问老师两个问题
    1.为什么要采用crc16
    2.各个节点的ping/pong消息大小大概会有多大呢,怎么来计算呢
    2020-08-24
  • 扩散性百万咸面包
    隔壁分布式数据库也讲到了分片,但是它里面提到现代的分布式数据库实现分片基本都是Range-based的,能够实现分片的动态调度,适合互联网的场景。那为什么Redis依旧要用Hash-based的设计方式呢?是为了更高并发的写入性能吗?

    作者回复: 如果是根据某个字段的取值范围进行range-based分片,有可能的一个问题是:某个range内的记录数量很多,这就会导致相应的数据分片比较大,一般也叫做数据倾斜。对这个数据分片的访问量也可能大,导致负载不均衡。

    基于记录key进行哈希后再取模,好处是能把数据打得比较散,不太容易引起数据倾斜,还是为了访问时请求负载能在不同数据分片分布地均衡些,提高访问性能。

    2020-08-24
  • MrPolo
    先前在研究 redis cluster 時有注意到,若改為 cluster mode 後有些 command 會無法使用,這部分請問老師會在後面課程講解嗎?
    2020-08-24
  • 流浪地球
    请问老师,集群中的每个切片都是主从配置的吗?

    作者回复: 切片集群中每个切片可以配置从库,也可以不配置。不过一般生产环境中还是建议对每个切片做主从配置。

    可以使用cluster replicate命令进行配置。

    2020-08-24
  • 篮球不是这么打的
    你好老师,请问下如何算的Redis中每个键值对的大小
    2020-08-24
  • 那时刻
    老师,请问一个Redis延迟的问题。

    Redis情况:
    单实例Redis,内存10G,最大吞吐量在25000ops/second。数据持久化方式:AOF - 每秒fsync一次。
    高峰期的ops是每秒8000,key的数量是4万,内存占用量是2.5G。

    遇到的问题:
    低峰期Redis延迟0.125ms左右,但是在高峰期的时候,延迟比较大,有1ms左右。

    通过INFO命令,看到latest_fork_usec是0。INFO命令其它的信息,我也看了下,感觉都还正常。

    我能想到的是通过增加Redis分片机制,来缓解压力。

    请问老师,针对这种情况,其一,如何来诊断Redis延迟高的情况呢?其二,如何来缓解Redis延迟高?
    2020-08-24
  • williamcai
    如果按照键值存储的话,数据量很大,导致该映射占用空间会很大,进而影响查询速度,采用映射卡擦的方式有些与多级目录有异曲同工之妙
    2020-08-24
  • 小喵喵
    请教小老师,我对ASKING命令不太理解,我觉得会返回一个包含bool值的信息,告诉客户端是否可以从这个实例中获取数据,若可以,然后客户端重新发送请求来获取数据,不知道这么理解对不对,请老师解惑。谢谢。
    2020-08-24
  • zhou
    如果在客户端计算好 CRC 值,REDIS 集群是不是就不用做计算了
    2020-08-24
  • Jackey
    关于ASKING的一点想法:在迁移过程中,已迁移的slot也还没有真正归新的实例管理,如果不加ASKING,那么目标实例还会返回MOVED错误。就会出现循环请求的情况
    2020-08-24
  • 小喵喵
    Redis本身是基于内存操作的,如果数据量大,基于查表法来做,会消耗太多的内存空间,成本上不允许。还有再有hash冲突的时候,会有大量数据迁移,维护起来困难多了。

    作者回复: 其实,如果用表来记录映射关系的话,就不用计算哈希值了,所以这个时候倒没有hash冲突了。不过,如果有实例下线,的确会涉及大量数据迁移,和映射关系变化,如果用表记录的话,对表的修改就会比较麻烦。

    感觉你说的hash冲突,是不是和Redis Cluster里的哈希槽使用有些混淆了? :)

    2020-08-24
  • 写点啥呢
    请问老师,在重定向的机制中,像例子里的情况key1 key2已经迁移到新的实例3,key3 key4还在实例2的时候,如果客户端请求的是key3的话,它是会得到实例2直接返回key3的value还是得到ASK?如果是ASK那么客户端去ASKING 实例3的时候会不会阻塞到key3迁移完成?谢谢
    2020-08-24
  • Hugh
    为什么不保存键值对的主要关键点在于,当要扩容或时,需要改变键值对的关系,这里就需要额外的记录哪些值在哪个实例上,相当于要记录两份数据,造成额外的开销。不知道我这样理解对不对。。
    2020-08-24
  • 优秀的吉吉国王
    槽相当于虚拟节点,这样可以灵活的扩缩容,因为是按槽数分的key,这是主要的优点,而且只需要存槽与机器实例的对应关系,不用每个实例都存一份所有的键对应的实例,节省内存
    2020-08-24
收起评论
24
返回
顶部