Redis 核心技术与实战
蒋德钧
中科院计算所副研究员
79224 人已学习
新⼈⾸单¥68
登录后,你可以任选4讲全文学习
课程目录
已完结/共 53 讲
开篇词 (1讲)
实践篇 (28讲)
Redis 核心技术与实战
15
15
1.0x
00:00/00:00
登录|注册

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

你好,我是蒋德钧。今天我们来学习切片集群。
我曾遇到过这么一个需求:要用 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/2000
荧光笔
直线
曲线
笔记
复制
AI
  • 深入了解
  • 翻译
    • 英语
    • 中文简体
    • 中文繁体
    • 法语
    • 德语
    • 日语
    • 韩语
    • 俄语
    • 西班牙语
    • 阿拉伯语
  • 解释
  • 总结
仅可试看部分内容,如需阅读全部内容,请付费购买文章所属专栏
《Redis 核心技术与实战》
新⼈⾸单¥68
立即购买
登录 后留言

全部留言(140)

  • 最新
  • 精选
  • 扩散性百万咸面包
    隔壁分布式数据库也讲到了分片,但是它里面提到现代的分布式数据库实现分片基本都是Range-based的,能够实现分片的动态调度,适合互联网的场景。那为什么Redis依旧要用Hash-based的设计方式呢?是为了更高并发的写入性能吗?

    作者回复: 如果是根据某个字段的取值范围进行range-based分片,有可能的一个问题是:某个range内的记录数量很多,这就会导致相应的数据分片比较大,一般也叫做数据倾斜。对这个数据分片的访问量也可能大,导致负载不均衡。 基于记录key进行哈希后再取模,好处是能把数据打得比较散,不太容易引起数据倾斜,还是为了访问时请求负载能在不同数据分片分布地均衡些,提高访问性能。

    9
    57
  • 流浪地球
    请问老师,集群中的每个切片都是主从配置的吗?

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

    6
    27
  • yyl
    解答: 1. 引入哈希槽,将key的分布与具体的Redis实例解耦,有利于Redis数据的均衡分布。 2. 不采用哈希槽的话,Redis实例的扩容和缩容,需要针对无规则的key进行处理,实现数据迁移。此外,需要引入负载均衡中间件来协调各个Redis实例的均衡响应,确保数据的均匀分布;中间件需要保存key的分布状态,保证key的查询能够得到响应。 增加了Redis系统的复杂性 与 可维护性。 看到问题后的第一反应,理解不够深刻,讲述不够清楚。贵在思考啦😜

    作者回复: 回答的挺好,对hash算法可用于打散键值对分布的理解到位!

    14
  • CityAnimal
    笔记打卡 * [ ] 多个 Redis 实例组成一个集群 * [ ] 扩展 * [ ] 纵向扩展(scale up) * [ ] 优势 * [ ] 简单 * [ ] 直接 * [ ] 问题 * [ ] 当使用RDB时,内存越大,主线程fork就越有可能阻塞 * [ ] 受到硬件和成本的限制 * [ ] 横向扩展(scale out) * [ ] 数据切片和实例的对应分布关系 * [ ] Redis Cluster 方案:无中心化 * [ ] 采用哈希槽(Hash Slot)来处理数据和实例之间的映射关系 * [ ] 一个切片集群共有 16384 个哈希槽 * [ ] 具体的映射过程 * [ ] 1. 根据键值对的 key,按照CRC16 算法计算一个 16 bit 的值; * [ ] 2. 再用这个 16bit 值对 16384 取模,得到 0~16383 范围内的模数,每个模数代表一个相应编号的哈希槽 * [ ] 哈希槽映射到具体的 Redis 实例上 * [ ] 1. 用 cluster create 命令创建集群 * [ ] Redis 会自动把这些槽平均分布在集群实例上 * [ ] 2. 使用 cluster meet 命令手动建立实例间的连接,形成集群,再使用 cluster addslots 命令,指定每个实例上的哈希槽个数 * [ ] 小提醒,需要把 16384 个槽都分配完,否则 Redis 集群无法正常工作 * [ ] ShardedJedis:基于客户端分区 * [ ] Codis、Twemproxy :基于代理 * [ ] 客户端如何定位数据 * [ ] Redis 实例会把自己的哈希槽信息发给和它相连接的其它实例,来完成哈希槽分配信息的扩散 * [ ] 客户端和集群实例建立连接后,实例就会把哈希槽的分配信息发给客户端 * [ ] 客户端会把哈希槽信息缓存在本地。当请求键值对时,会先计算键所对应的哈希槽 * [ ] 但集群中,实例和哈希槽的对应关系并不是一成不变的 * [ ] 实例新增或删除 * [ ] 负载均衡 * [ ] 实例之间可以通过相互传递消息,获得最新的哈希槽分配信息,但客户端是无法主动感知这些变化 * [ ] **重定向机制** * [ ] 1. 如果实例上没有该键值对映射的哈希槽,就会返回 MOVED 命令 * [ ] 客户端会更新本地缓存 * [ ] 2. 在**迁移部分完成**情况下,返回ASK * [ ] 表明 Slot 数据还在迁移中 * [ ] ASK 命令把客户端所请求数据的最新实例地址返回给客户端 * [ ] 并不会更新客户端缓存的哈希槽分配信息

    作者回复: 这个笔记整理得不错,层次分明!

    8
  • 小喵喵
    Redis本身是基于内存操作的,如果数据量大,基于查表法来做,会消耗太多的内存空间,成本上不允许。还有再有hash冲突的时候,会有大量数据迁移,维护起来困难多了。

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

    2
    1
  • 努力奋斗者
    老师,请问redis对key做分片存储在不通的哈希槽,这是全局key-value。那么对于hash这种复杂类型的value的key也会做分片存储吗?

    作者回复: 对于复杂类型的value来说,key也一样会做分片的。这个key是指复杂类型本身的key,并不是value集合内部的key(例如Hash集合内部的key)。

  • 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提供了异步迁移数据的方案,迁移速度更快,对性能影响最小,当然,实现方案也比较复杂。
    95
    927
  • 注定非凡
    1,作者讲了什么? 切片集群 2,作者是怎么把事给讲明白的? 从一个案例入手,讲到单实例内存过大在数据备份时会导致Redis性能下降, 引出redis分片集群来解决大数据量,高性能的设计 提出两个关键问题:数据分片与实例间如何建立对应关系,2,客户端如何知晓去哪个实例中获取数据 3,为了讲明白,作者讲了哪些要点?有哪些亮点? 1,亮点1:这一课我更加清晰的明白了之前别人聊Redis扩容中的纵向扩容和横向扩容的真实含义和区别 2,要点1:数据分片和实例的对应关系建立:按照CRC16算法计算一个key的16bit的值,在将这值对16384取模 3,要点2:一个切片集群的槽位是固定的16384个,可手动分配每个实例的槽位,但必须将槽位全部分完 4,要点3:客户端如何确定要访问那个实例获取数据:1从任意个实例获取并缓存在自己本地,2,重定向机制 5,要点4:重定向机制:客户端访问的实例没有数据,被访问实例响应move命令,告诉客户端指向新的实例地址 6,要点5:ASK命令:1,表明数据正在迁移 2,告知客户端数据所在的实例 7,要点6:ASK命令和MOVE命令的区别: move命令是在数据迁移完毕后被响应,客户端会更新本地缓存。 ASK命令是在数据迁移中被响应,不会让客户端更新缓存 4,对作者所讲,我有哪些发散性思考? 对于CRC16算法,应该可以用到我们系统当中,对所有手机的设备号进行计算取模,用于分表存储数据 在系统设计时,可以通过分层或增加一层来提升系统设计的弹性 5,在将来的那些场景中,我能够使用它? 6,留言区的收获(来自 @Kaito 大神) 1,Redis Cluster不采用直接把key映射到实例,而采用哈希槽的方式原因:可用使Redis集群设计:简洁,高效,有弹性 不使用的劣势 ①:集群中的key无法预估,直接存key对应实例的映射关系,需占用的内存空间不可控 ②:Cluster是去中心化设计,所有实例都需保存完整的映射关系, 采用直接的映射,会导致节点间信息交换成本高昂 ③:key与实例直接映射,在集群扩缩容时,需要数据迁移,所有的key都需要重新映射 使用的好处 ①:在中间增加一层哈希槽,可以将数据与节点解耦,使数据分配均匀 key通过hsah计算在取模,可以把数据打的更散, 只需要关心映射到了哪个哈希槽,通过哈希槽映射表找到对应的实例 ②:增加哈希槽可以使得映射表比较小,有利于客户端和服务端保存,节点间信息交换 ③:集群扩缩容,数据均衡时,操作单位是哈希槽,可以简化操作难度 2,Redis集群方案的两个关键问题: ①:请求路由 ②:数据迁移
    9
    102
  • Darren
    我认为有以下几点: 1、存在表的话,存在单点问题,即使部署多份,存在数据一致性问题,提高了复杂度; 2、即使解决了第一个问题,但是Redis主打的是快,表的读写并发问题处理; 3、key对应的是实例,对应关系粒度太大; 4、用key做hash避免依赖别的功能或者服务,提供了整体的内聚性; 5、在做Redis集群,为了数据分配均匀,进行一致性哈希的时候,虚拟节点和真实节点之间还有对应关系,存在多级映射关系,增加了耗时,影响Redis主线程的执行速度。
    1
    16
  • 天敌
    在手动分配哈希槽时,需要把 16384 个槽都分配完,否则 Redis 集群无法正常工作。 老师,您手动分配的例子也只分配了5个哈希槽,这只是为了教学方便吗? 我用的时候是不是应该 从0写到16383, 就像下面这样? redis-cli -h 172.16.19.3 –p 6379 cluster addslots 0,1 redis-cli -h 172.16.19.4 –p 6379 cluster addslots 2,3 redis-cli -h 172.16.19.5 –p 6379 cluster addslots 4,5,6,7,8,...,16383
    2
    13
收起评论
显示
设置
留言
99+
收藏
沉浸
阅读
分享
手机端
快捷键
回顶部