智能摘要文章源自JAVA秀-https://www.javaxiu.com/39705.html
append操作,则直接使用free中未使用的空间,减少了内存的分配。使用多线程)以及键值对指令读写是由一个线程来执行的。其原理就是开辟一个子进程对内存进行遍历转换成一系列Redis的操作指令,序列化到一个新的AOF日志文件中。当主从库完成了全量复制,它们之间就会一直维护一个网络连接,主库会通过这个连接将后续陆续收到的命令操作再同步给从库,这个过程也称为基于长连接的命令传播,使用长连接的目的就是避免频繁建立连接导致的开销。文章源自JAVA秀-https://www.javaxiu.com/39705.html
原文约 1.3 万 字 | 图片 30 张 | 建议阅读 26 分钟 | 评价反馈文章源自JAVA秀-https://www.javaxiu.com/39705.html
面霸:Redis 为什么这么快?
芋道源码 文章源自JAVA秀-https://www.javaxiu.com/39705.html
以下文章来源于码哥字节,作者码哥字节文章源自JAVA秀-https://www.javaxiu.com/39705.html
文章源自JAVA秀-https://www.javaxiu.com/39705.html 文章源自JAVA秀-https://www.javaxiu.com/39705.html 码哥字节文章源自JAVA秀-https://www.javaxiu.com/39705.html 既有硬核文章,又有诗和远方。文章源自JAVA秀-https://www.javaxiu.com/39705.html
点击上方“芋道源码”,选择“设为星标”文章源自JAVA秀-https://www.javaxiu.com/39705.html
管她前浪,还是后浪?文章源自JAVA秀-https://www.javaxiu.com/39705.html
能浪的浪,才是好浪!文章源自JAVA秀-https://www.javaxiu.com/39705.html
每天 8:55 更新文章,每天掉亿点点头发...文章源自JAVA秀-https://www.javaxiu.com/39705.html
文章源自JAVA秀-https://www.javaxiu.com/39705.html 源码精品专栏文章源自JAVA秀-https://www.javaxiu.com/39705.html
原创 | Java 2020 超神之路,很肝~文章源自JAVA秀-https://www.javaxiu.com/39705.html
中文详细注释的开源项目文章源自JAVA秀-https://www.javaxiu.com/39705.html
RPC 框架 Dubbo 源码解析文章源自JAVA秀-https://www.javaxiu.com/39705.html
网络应用框架 Netty 源码解析文章源自JAVA秀-https://www.javaxiu.com/39705.html
消息中间件 RocketMQ 源码解析文章源自JAVA秀-https://www.javaxiu.com/39705.html
数据库中间件 Sharding-JDBC 和 MyCAT 源码解析文章源自JAVA秀-https://www.javaxiu.com/39705.html
作业调度中间件 Elastic-Job 源码解析文章源自JAVA秀-https://www.javaxiu.com/39705.html
分布式事务中间件 TCC-Transaction 源码解析文章源自JAVA秀-https://www.javaxiu.com/39705.html
Eureka 和 Hystrix 源码解析文章源自JAVA秀-https://www.javaxiu.com/39705.html
Java 并发源码文章源自JAVA秀-https://www.javaxiu.com/39705.html
文章源自JAVA秀-https://www.javaxiu.com/39705.html
到底有多快?文章源自JAVA秀-https://www.javaxiu.com/39705.html
基于内存实现文章源自JAVA秀-https://www.javaxiu.com/39705.html
高效的数据结构文章源自JAVA秀-https://www.javaxiu.com/39705.html
单线程模型文章源自JAVA秀-https://www.javaxiu.com/39705.html
I/O 多路复用模型文章源自JAVA秀-https://www.javaxiu.com/39705.html
Redis 全局 hash 字典文章源自JAVA秀-https://www.javaxiu.com/39705.html
Hash 冲突怎么办?文章源自JAVA秀-https://www.javaxiu.com/39705.html
Redis 如何实现持久化?宕机后如何恢复数据?文章源自JAVA秀-https://www.javaxiu.com/39705.html
Redis 主从架构数据同步文章源自JAVA秀-https://www.javaxiu.com/39705.html
哨兵原理连环问文章源自JAVA秀-https://www.javaxiu.com/39705.html
Cluster 集群连环炮文章源自JAVA秀-https://www.javaxiu.com/39705.html
我是艿艿,一个正在某厂做 Web IDE 的死胖子。文章源自JAVA秀-https://www.javaxiu.com/39705.html
如果你目前也在从事这方面的工作,欢迎加我的微信 wangwenbin10文章源自JAVA秀-https://www.javaxiu.com/39705.html
相信不少胖友面试的时候,被问到过:Redis 为什么这么快?艿艿自己去某厂面试搬砖,一面的时候,也被问到了~文章源自JAVA秀-https://www.javaxiu.com/39705.html
昨天,悄悄录制了一个 5 分钟,搭建一个 Redis 调试环境的视频,嘿嘿~文章源自JAVA秀-https://www.javaxiu.com/39705.html
球球了,给个一间三连吧,我胖,我秃,我太难了。。。文章源自JAVA秀-https://www.javaxiu.com/39705.html
很多人只知道是 K/V NoSQl 内存数据库,单线程……这都是没有全面理解 Redis 导致无法继续深问下去。文章源自JAVA秀-https://www.javaxiu.com/39705.html
这个问题是基础摸底,我们可以从 Redis 不同数据类型底层的数据结构实现、完全基于内存、IO 多路复用网络模型、线程模型、渐进式 rehash…...文章源自JAVA秀-https://www.javaxiu.com/39705.html
到底有多快?
我们可以先说到底有多快,根据官方数据,Redis 的 QPS 可以达到约 100000(每秒请求数),有兴趣的可以参考官方的基准程序测试《How fast is Redis?》,地址:https://redis.io/topics/benchmarks文章源自JAVA秀-https://www.javaxiu.com/39705.html
基准测试文章源自JAVA秀-https://www.javaxiu.com/39705.html
横轴是连接数,纵轴是 QPS。文章源自JAVA秀-https://www.javaxiu.com/39705.html
❝文章源自JAVA秀-https://www.javaxiu.com/39705.html
这张图反映了一个数量级,通过量化让面试官觉得你有看过官方文档,很严谨。文章源自JAVA秀-https://www.javaxiu.com/39705.html
基于内存实现
Redis 是基于内存的数据库,跟磁盘数据库相比,完全吊打磁盘的速度。文章源自JAVA秀-https://www.javaxiu.com/39705.html
不论读写操作都是在内存上完成的,我们分别对比下内存操作与磁盘操作的差异。文章源自JAVA秀-https://www.javaxiu.com/39705.html
磁盘调用文章源自JAVA秀-https://www.javaxiu.com/39705.html
文章源自JAVA秀-https://www.javaxiu.com/39705.html
图片文章源自JAVA秀-https://www.javaxiu.com/39705.html
内存操作文章源自JAVA秀-https://www.javaxiu.com/39705.html
内存直接由 CPU 控制,也就是 CPU 内部集成的内存控制器,所以说内存是直接与 CPU 对接,享受与 CPU 通信的最优带宽。文章源自JAVA秀-https://www.javaxiu.com/39705.html
最后以一张图量化系统的各种延时时间(部分数据引用 Brendan Gregg)文章源自JAVA秀-https://www.javaxiu.com/39705.html
文章源自JAVA秀-https://www.javaxiu.com/39705.html
图片文章源自JAVA秀-https://www.javaxiu.com/39705.html
高效的数据结构
学习 MySQL 的时候我知道为了提高检索速度使用了 B+ Tree 数据结构,所以 Redis 速度快应该也跟数据结构有关。文章源自JAVA秀-https://www.javaxiu.com/39705.html
Redis 一共有 5 种数据类型,String、List、Hash、Set、SortedSet
。文章源自JAVA秀-https://www.javaxiu.com/39705.html
不同的数据类型底层使用了一种或者多种数据结构来支撑,目的就是为了追求更快的速度。文章源自JAVA秀-https://www.javaxiu.com/39705.html
❝文章源自JAVA秀-https://www.javaxiu.com/39705.html
码哥寄语:我们可以分别说明每种数据类型底层的数据结构优点,很多人只知道数据类型,而说出底层数据结构就能让人眼前一亮。文章源自JAVA秀-https://www.javaxiu.com/39705.html
文章源自JAVA秀-https://www.javaxiu.com/39705.html
图片文章源自JAVA秀-https://www.javaxiu.com/39705.html
SDS 简单动态字符串优势
C 语言字符串与 SDS文章源自JAVA秀-https://www.javaxiu.com/39705.html
SDS 中 len 保存这字符串的长度,O(1) 时间复杂度查询字符串长度信息。文章源自JAVA秀-https://www.javaxiu.com/39705.html
空间预分配:SDS 被修改后,程序不仅会为 SDS 分配所需要的必须空间,还会分配额外的未使用空间。文章源自JAVA秀-https://www.javaxiu.com/39705.html
惰性空间释放:当对 SDS 进行缩短操作时,程序并不会回收多余的内存空间,而是使用 free 字段将这些字节数量记录下来不释放,后面如果需要 append 操作,则直接使用 free 中未使用的空间,减少了内存的分配。文章源自JAVA秀-https://www.javaxiu.com/39705.html
zipList 压缩列表
压缩列表是 List 、hash、 sorted Set 三种数据类型底层实现之一。文章源自JAVA秀-https://www.javaxiu.com/39705.html
当一个列表只有少量数据的时候,并且每个列表项要么就是小整数值,要么就是长度比较短的字符串,那么 Redis 就会使用压缩列表来做列表键的底层实现。文章源自JAVA秀-https://www.javaxiu.com/39705.html
ziplist文章源自JAVA秀-https://www.javaxiu.com/39705.html
这样内存紧凑,节约内存。文章源自JAVA秀-https://www.javaxiu.com/39705.html
quicklist
后续版本对列表数据结构进行了改造,使用 quicklist 代替了 ziplist 和 linkedlist。文章源自JAVA秀-https://www.javaxiu.com/39705.html
quicklist 是 ziplist 和 linkedlist 的混合体,它将 linkedlist 按段切分,每一段使用 ziplist 来紧凑存储,多个 ziplist 之间使用双向指针串接起来。文章源自JAVA秀-https://www.javaxiu.com/39705.html
文章源自JAVA秀-https://www.javaxiu.com/39705.html
图片文章源自JAVA秀-https://www.javaxiu.com/39705.html
skipList 跳跃表
sorted set 类型的排序功能便是通过「跳跃列表」数据结构来实现。文章源自JAVA秀-https://www.javaxiu.com/39705.html
跳跃表(skiplist)是一种有序数据结构,它通过在每个节点中维持多个指向其他节点的指针,从而达到快速访问节点的目的。文章源自JAVA秀-https://www.javaxiu.com/39705.html
跳表在链表的基础上,增加了多层级索引,通过索引位置的几个跳转,实现数据的快速定位,如下图所示:文章源自JAVA秀-https://www.javaxiu.com/39705.html
跳跃表文章源自JAVA秀-https://www.javaxiu.com/39705.html
整数数组(intset)
当一个集合只包含整数值元素,并且这个集合的元素数量不多时,Redis 就会使用整数集合作为集合键的底层实现,节省内存。文章源自JAVA秀-https://www.javaxiu.com/39705.html
单线程模型
❝文章源自JAVA秀-https://www.javaxiu.com/39705.html
码哥寄语:我们需要注意的是,Redis 的单线程指的是 Redis 的网络 IO (6.x 版本后网络 IO 使用多线程)以及键值对指令读写是由一个线程来执行的。 对于 Redis 的持久化、集群数据同步、异步删除等都是其他线程执行。文章源自JAVA秀-https://www.javaxiu.com/39705.html
千万别说 Redis 就只有一个线程。文章源自JAVA秀-https://www.javaxiu.com/39705.html
单线程指的是 Redis 键值对读写指令的执行是单线程。文章源自JAVA秀-https://www.javaxiu.com/39705.html
先说官方答案,让人觉得足够严谨,而不是人云亦云去背诵一些博客。文章源自JAVA秀-https://www.javaxiu.com/39705.html
官方答案:* 因为 Redis 是基于内存的操作,CPU 不是 Redis 的瓶颈,Redis 的瓶颈最*有可能是机器内存的大小或者网络带宽 。既然单线程容易实现,而且 CPU 不会成为瓶颈,那就顺理成章地采用单线程的方案了。原文地址:https://redis.io/topics/faq。文章源自JAVA秀-https://www.javaxiu.com/39705.html
❝文章源自JAVA秀-https://www.javaxiu.com/39705.html
为啥不用多线程执行充分利用 CPU 呢?文章源自JAVA秀-https://www.javaxiu.com/39705.html
在运行每个任务之前,CPU 需要知道任务在何处加载并开始运行。也就是说,系统需要帮助它预先设置 CPU 寄存器和程序计数器,这称为 CPU 上下文。文章源自JAVA秀-https://www.javaxiu.com/39705.html
切换上下文时,我们需要完成一系列工作,这是非常消耗资源的操作。文章源自JAVA秀-https://www.javaxiu.com/39705.html
引入多线程开发,就需要使用同步原语来保护共享资源的并发读写,增加代码复杂度和调试难度。文章源自JAVA秀-https://www.javaxiu.com/39705.html
❝文章源自JAVA秀-https://www.javaxiu.com/39705.html
单线程有什么好处?文章源自JAVA秀-https://www.javaxiu.com/39705.html
不会因为线程创建导致的性能消耗;文章源自JAVA秀-https://www.javaxiu.com/39705.html
避免上下文切换引起的 CPU 消耗,没有多线程切换的开销;文章源自JAVA秀-https://www.javaxiu.com/39705.html
避免了线程之间的竞争问题,比如添加锁、释放锁、死锁等,不需要考虑各种锁问题。文章源自JAVA秀-https://www.javaxiu.com/39705.html
代码更清晰,处理逻辑简单。文章源自JAVA秀-https://www.javaxiu.com/39705.html
I/O 多路复用模型
Redis 采用 I/O 多路复用技术,并发处理连接。采用了 epoll + 自己实现的简单的事件框架。文章源自JAVA秀-https://www.javaxiu.com/39705.html
epoll 中的读、写、关闭、连接都转化成了事件,然后利用 epoll 的多路复用特性,绝不在 IO 上浪费一点时间。文章源自JAVA秀-https://www.javaxiu.com/39705.html
高性能 IO 多路复用文章源自JAVA秀-https://www.javaxiu.com/39705.html
Redis 线程不会阻塞在某一个特定的监听或已连接套接字上,也就是说,不会阻塞在某一个特定的客户端请求处理上。正因为此,Redis 可以同时和多个客户端连接并处理请求,从而提升并发性。文章源自JAVA秀-https://www.javaxiu.com/39705.html
Redis 全局 hash 字典
Redis 整体就是一个 哈希表来保存所有的键值对,无论数据类型是 5 种的任意一种。哈希表,本质就是一个数组,每个元素被叫做哈希桶,不管什么数据类型,每个桶里面的 entry 保存着实际具体值的指针。文章源自JAVA秀-https://www.javaxiu.com/39705.html
Redis 全局哈希表文章源自JAVA秀-https://www.javaxiu.com/39705.html
而哈希表的时间复杂度是 O(1),只需要计算每个键的哈希值,便知道对应的哈希桶位置,定位桶里面的 entry 找到对应数据,这个也是 Redis 快的原因之一。文章源自JAVA秀-https://www.javaxiu.com/39705.html
Redis 使用对象(redisObject)来表示数据库中的键值,当我们在 Redis 中创建一个键值对时,至少创建两个对象,一个对象是用做键值对的键对象,另一个是键值对的值对象。文章源自JAVA秀-https://www.javaxiu.com/39705.html
也就是每个 entry 保存着 「键值对」的 redisObject 对象,通过 redisObject 的指针找到对应数据。文章源自JAVA秀-https://www.javaxiu.com/39705.html
typedef struct redisObject{ //类型 unsigned type:4; //编码 unsigned encoding:4; //指向底层数据结构的指针 void *ptr; //... }robj;
Hash 冲突怎么办?
Redis 通过链式哈希 解决冲突:也就是同一个 桶里面的元素使用链表保存 。但是当链表过长就会导致查找性能变差可能,所以 Redis 为了追求快,使用了两个全局哈希表。用于 rehash 操作,增加现有的哈希桶数量,减少哈希冲突。文章源自JAVA秀-https://www.javaxiu.com/39705.html
开始默认使用 「hash 表 1 」保存键值对数据,「hash 表 2」 此刻没有分配空间。当数据越来越多触发 rehash 操作,则执行以下操作:文章源自JAVA秀-https://www.javaxiu.com/39705.html
给 「hash 表 2 」分配更大的空间;文章源自JAVA秀-https://www.javaxiu.com/39705.html
将 「hash 表 1 」的数据重新映射拷贝到 「hash 表 2」 中;文章源自JAVA秀-https://www.javaxiu.com/39705.html
释放 「hash 表 1」 的空间。文章源自JAVA秀-https://www.javaxiu.com/39705.html
值得注意的是,将 hash 表 1 的数据重新映射到 hash 表 2 的过程中并不是一次性的,这样会造成 Redis 阻塞,无法提供服务。文章源自JAVA秀-https://www.javaxiu.com/39705.html
而是采用了渐进式 rehash ,每次处理客户端请求的时候,先从「 hash 表 1」 中第一个索引开始,将这个位置的 所有数据拷贝到 「hash 表 2」 中,就这样将 rehash 分散到多次请求过程中,避免耗时阻塞。文章源自JAVA秀-https://www.javaxiu.com/39705.html
Redis 如何实现持久化?宕机后如何恢复数据?
Redis 的数据持久化使用了「RDB 数据快照」的方式来实现宕机快速恢复。但是 过于频繁的执行全量数据快照,有两个严重性能开销:文章源自JAVA秀-https://www.javaxiu.com/39705.html
频繁生成 RDB 文件写入磁盘,磁盘压力过大。会出现上一个 RDB 还未执行完,下一个又开始生成,陷入死循环。文章源自JAVA秀-https://www.javaxiu.com/39705.html
fork 出 bgsave 子进程会阻塞主线程,主线程的内存越大,阻塞时间越长。文章源自JAVA秀-https://www.javaxiu.com/39705.html
所以 Redis 还设计了 AOF 写后日志记录对内存进行修改的指令记录。文章源自JAVA秀-https://www.javaxiu.com/39705.html
❝文章源自JAVA秀-https://www.javaxiu.com/39705.html
面试官:什么是 RDB 内存快照?文章源自JAVA秀-https://www.javaxiu.com/39705.html
在 Redis 执行「写」指令过程中,内存数据会一直变化。所谓的内存快照,指的就是 Redis 内存中的数据在某一刻的状态数据。文章源自JAVA秀-https://www.javaxiu.com/39705.html
好比时间定格在某一刻,当我们拍照的,通过照片就能把某一刻的瞬间画面完全记录下来。文章源自JAVA秀-https://www.javaxiu.com/39705.html
Redis 跟这个类似,就是把某一刻的数据以文件的形式拍下来,写到磁盘上。这个快照文件叫做 RDB 文件,RDB 就是 Redis DataBase 的缩写。文章源自JAVA秀-https://www.javaxiu.com/39705.html
文章源自JAVA秀-https://www.javaxiu.com/39705.html
RDB内存快照文章源自JAVA秀-https://www.javaxiu.com/39705.html
在做数据恢复时,直接将 RDB 文件读入内存完成恢复。文章源自JAVA秀-https://www.javaxiu.com/39705.html
❝文章源自JAVA秀-https://www.javaxiu.com/39705.html
面试官:在生成 RDB 期间,Redis 可以同时处理写请求么?文章源自JAVA秀-https://www.javaxiu.com/39705.html
可以的,Redis 使用操作系统的多进程写时复制技术 COW(Copy On Write) 来实现快照持久化,保证数据一致性。文章源自JAVA秀-https://www.javaxiu.com/39705.html
Redis 在持久化时会调用 glibc 的函数fork
产生一个子进程,快照持久化完全交给子进程来处理,父进程继续处理客户端请求。文章源自JAVA秀-https://www.javaxiu.com/39705.html
当主线程执行写指令修改数据的时候,这个数据就会复制一份副本, bgsave
子进程读取这个副本数据写到 RDB 文件。文章源自JAVA秀-https://www.javaxiu.com/39705.html
这既保证了快照的完整性,也允许主线程同时对数据进行修改,避免了对正常业务的影响。文章源自JAVA秀-https://www.javaxiu.com/39705.html
写时复制技术保证快照期间数据可修改文章源自JAVA秀-https://www.javaxiu.com/39705.html
❝文章源自JAVA秀-https://www.javaxiu.com/39705.html
面试官:那 AOF 又是什么?文章源自JAVA秀-https://www.javaxiu.com/39705.html
AOF 日志记录了自 Redis 实例创建以来所有的修改性指令序列,那么就可以通过对一个空的 Redis 实例顺序执行所有的指令,也就是「重放」,来恢复 Redis 当前实例的内存数据结构的状态。文章源自JAVA秀-https://www.javaxiu.com/39705.html
Redis 提供的 AOF 配置项appendfsync
写回策略直接决定 AOF 持久化功能的效率和安全性。文章源自JAVA秀-https://www.javaxiu.com/39705.html
always :同步写回,写指令执行完毕立马将
aof_buf
缓冲区中的内容刷写到 AOF 文件。文章源自JAVA秀-https://www.javaxiu.com/39705.htmleverysec :每秒写回,写指令执行完,日志只会写到 AOF 文件缓冲区,每隔一秒就把缓冲区内容同步到磁盘。文章源自JAVA秀-https://www.javaxiu.com/39705.html
no: 操作系统控制,写执行执行完毕,把日志写到 AOF 文件内存缓冲区,由操作系统决定何时刷写到磁盘。文章源自JAVA秀-https://www.javaxiu.com/39705.html
没有两全其美的策略,我们需要在性能和可靠性上做一个取舍。文章源自JAVA秀-https://www.javaxiu.com/39705.html
❝文章源自JAVA秀-https://www.javaxiu.com/39705.html
面试官:既然 RDB 有两个性能问题,那为何不用 AOF 即可。文章源自JAVA秀-https://www.javaxiu.com/39705.html
AOF 写前日志,记录的是每个「写」指令操作。不会像 RDB 全量快照导致性能损耗,但是执行速度没有 RDB 快,同时日志文件过大也会造成性能问题。文章源自JAVA秀-https://www.javaxiu.com/39705.html
所以,Redis 设计了一个杀手锏「AOF 重写机制」,Redis 提供了 bgrewriteaof
指令用于对 AOF 日志进行瘦身。文章源自JAVA秀-https://www.javaxiu.com/39705.html
其原理就是开辟一个子进程对内存进行遍历转换成一系列 Redis 的操作指令,序列化到一个新的 AOF 日志文件中。序列化完毕后再将操作期间发生的增量 AOF 日志追加到这个新的 AOF 日志文件中,追加完毕后就立即替代旧的 AOF 日志文件了,瘦身工作就完成了。文章源自JAVA秀-https://www.javaxiu.com/39705.html
文章源自JAVA秀-https://www.javaxiu.com/39705.html
图片文章源自JAVA秀-https://www.javaxiu.com/39705.html
AOF重写机制(3条变一条)文章源自JAVA秀-https://www.javaxiu.com/39705.html
❝文章源自JAVA秀-https://www.javaxiu.com/39705.html
面试官:如何实现数据尽可能少丢失又能兼顾性能呢?文章源自JAVA秀-https://www.javaxiu.com/39705.html
重启 Redis 时,我们很少使用 rdb 来恢复内存状态,因为会丢失大量数据。我们通常使用 AOF 日志重放,但是重放 AOF 日志性能相对 rdb 来说要慢很多,这样在 Redis 实例很大的情况下,启动需要花费很长的时间。文章源自JAVA秀-https://www.javaxiu.com/39705.html
Redis 4.0 为了解决这个问题,带来了一个新的持久化选项——混合持久化 。将 rdb 文件的内容和增量的 AOF 日志文件存在一起。这里的 AOF 日志不再是全量的日志,而是自持久化开始到持久化结束的这段时间发生的增量 AOF 日志 ,通常这部分 AOF 日志很小。文章源自JAVA秀-https://www.javaxiu.com/39705.html
于是在 Redis 重启的时候,可以先加载 rdb 的内容,然后再重放增量 AOF 日志就可以完全替代之前的 AOF 全量文件重放,重启效率因此大幅得到提升 。文章源自JAVA秀-https://www.javaxiu.com/39705.html
Redis 主从架构数据同步
Redis 提供了主从模式,通过主从复制,将数据冗余一份复制到其他 Redis 服务器。文章源自JAVA秀-https://www.javaxiu.com/39705.html
❝文章源自JAVA秀-https://www.javaxiu.com/39705.html
面试官:主从之间数据如何保证一致性?文章源自JAVA秀-https://www.javaxiu.com/39705.html
为了保证副本数据的一致性,主从架构采用了读写分离的方式。文章源自JAVA秀-https://www.javaxiu.com/39705.html
读操作:主、从库都可以执行;文章源自JAVA秀-https://www.javaxiu.com/39705.html
写操作:主库先执行,之后将写操作同步到从库;文章源自JAVA秀-https://www.javaxiu.com/39705.html
Redis 读写分离文章源自JAVA秀-https://www.javaxiu.com/39705.html
❝文章源自JAVA秀-https://www.javaxiu.com/39705.html
面试官:主从复制还有其他作用么?文章源自JAVA秀-https://www.javaxiu.com/39705.html
故障恢复:当主节点宕机,其他节点依然可以提供服务;文章源自JAVA秀-https://www.javaxiu.com/39705.html
负载均衡:Master 节点提供写服务,Slave 节点提供读服务,分担压力;文章源自JAVA秀-https://www.javaxiu.com/39705.html
高可用基石:是哨兵和 cluster 实施的基础,是高可用的基石。文章源自JAVA秀-https://www.javaxiu.com/39705.html
❝文章源自JAVA秀-https://www.javaxiu.com/39705.html
面试官:主从复制如何实现的?文章源自JAVA秀-https://www.javaxiu.com/39705.html
同步分为三种情况:文章源自JAVA秀-https://www.javaxiu.com/39705.html
第一次主从库全量复制;文章源自JAVA秀-https://www.javaxiu.com/39705.html
主从正常运行期间的同步;文章源自JAVA秀-https://www.javaxiu.com/39705.html
主从库间网络断开重连同步。文章源自JAVA秀-https://www.javaxiu.com/39705.html
❝文章源自JAVA秀-https://www.javaxiu.com/39705.html
面试官:第一次同步怎么实现?文章源自JAVA秀-https://www.javaxiu.com/39705.html
主从库第一次复制过程大体可以分为 3 个阶段:连接建立阶段(即准备阶段)、主库同步数据到从库阶段、发送同步期间新写命令到从库阶段 ;文章源自JAVA秀-https://www.javaxiu.com/39705.html
Redis全量同步文章源自JAVA秀-https://www.javaxiu.com/39705.html
建立连接:从库会和主库建立连接,从库执行 replicaof 并发送 psync 命令并告诉主库即将进行同步,主库确认回复后,主从库间就开始同步了 。文章源自JAVA秀-https://www.javaxiu.com/39705.html
主库同步数据给从库:master 执行
bgsave
命令生成 RDB 文件,并将文件发送给从库,同时主库 为每一个 slave 开辟一块 replication buffer 缓冲区记录从生成 RDB 文件开始收到的所有写命令。从库保存 RDB 并清空数据库再加载 RDB 数据到内存中。文章源自JAVA秀-https://www.javaxiu.com/39705.html发送 RDB 之后接收到的新写命令到从库:在生成 RDB 文件之后的写操作并没有记录到刚刚的 RDB 文件中,为了保证主从库数据的一致性,所以主库会在内存中使用一个叫 replication buffer 记录 RDB 文件生成后的所有写操作。并将里面的数据发送到 slave。文章源自JAVA秀-https://www.javaxiu.com/39705.html
❝文章源自JAVA秀-https://www.javaxiu.com/39705.html
面试官:主从库间的网络断了咋办?断开后要重新全量复制么?文章源自JAVA秀-https://www.javaxiu.com/39705.html
在 Redis 2.8 之前,如果主从库在命令传播时出现了网络闪断,那么,从库就会和主库重新进行一次全量复制,开销非常大。文章源自JAVA秀-https://www.javaxiu.com/39705.html
从 Redis 2.8 开始,网络断了之后,主从库会采用增量复制的方式继续同步。文章源自JAVA秀-https://www.javaxiu.com/39705.html
增量复制:用于网络中断等情况后的复制,只将中断期间主节点执行的写命令发送给从节点,与全量复制相比更加高效 。文章源自JAVA秀-https://www.javaxiu.com/39705.html
断开重连增量复制的实现奥秘就是 repl_backlog_buffer
缓冲区,不管在什么时候 master 都会将写指令操作记录在 repl_backlog_buffer
中,因为内存有限, repl_backlog_buffer
是一个定长的环形数组,如果数组内容满了,就会从头开始覆盖前面的内容 。文章源自JAVA秀-https://www.javaxiu.com/39705.html
master 使用 master_repl_offset
记录自己写到的位置偏移量,slave 则使用 slave_repl_offset
记录已经读取到的偏移量。文章源自JAVA秀-https://www.javaxiu.com/39705.html
repl_backlog_buffer文章源自JAVA秀-https://www.javaxiu.com/39705.html
当主从断开重连后,slave 会先发送 psync 命令给 master,同时将自己的 runID
,slave_repl_offset
发送给 master。文章源自JAVA秀-https://www.javaxiu.com/39705.html
master 只需要把 master_repl_offset
与 slave_repl_offset
之间的命令同步给从库即可。文章源自JAVA秀-https://www.javaxiu.com/39705.html
增量复制执行流程如下图:文章源自JAVA秀-https://www.javaxiu.com/39705.html
文章源自JAVA秀-https://www.javaxiu.com/39705.html
图片文章源自JAVA秀-https://www.javaxiu.com/39705.html
Redis增量复制文章源自JAVA秀-https://www.javaxiu.com/39705.html
❝文章源自JAVA秀-https://www.javaxiu.com/39705.html
面试官:那完成全量同步后,正常运行过程中如何同步数据呢?文章源自JAVA秀-https://www.javaxiu.com/39705.html
当主从库完成了全量复制,它们之间就会一直维护一个网络连接,主库会通过这个连接将后续陆续收到的命令操作再同步给从库,这个过程也称为基于长连接的命令传播,使用长连接的目的就是避免频繁建立连接导致的开销。文章源自JAVA秀-https://www.javaxiu.com/39705.html
哨兵原理连环问
❝文章源自JAVA秀-https://www.javaxiu.com/39705.html
面试官:可以呀,知道这么多,你知道 哨兵集群原理么?文章源自JAVA秀-https://www.javaxiu.com/39705.html
哨兵是 Redis 的一种运行模式,它专注于对 Redis 实例(主节点、从节点)运行状态的监控,并能够在主节点发生故障时通过一系列的机制实现选主及主从切换,实现故障转移,确保整个 Redis 系统的可用性 。文章源自JAVA秀-https://www.javaxiu.com/39705.html
他的架构图如下:文章源自JAVA秀-https://www.javaxiu.com/39705.html
Redis哨兵集群文章源自JAVA秀-https://www.javaxiu.com/39705.html
Redis 哨兵具备的能力有如下几个:文章源自JAVA秀-https://www.javaxiu.com/39705.html
监控 :持续监控 master 、slave 是否处于预期工作状态。文章源自JAVA秀-https://www.javaxiu.com/39705.html
自动切换主库 :当 Master 运行故障,哨兵启动自动故障恢复流程:从 slave 中选择一台作为新 master。文章源自JAVA秀-https://www.javaxiu.com/39705.html
通知 :让 slave 执行 replicaof ,与新的 master 同步;并且通知客户端与新 master 建立连接。文章源自JAVA秀-https://www.javaxiu.com/39705.html
❝文章源自JAVA秀-https://www.javaxiu.com/39705.html
面试官:哨兵之间是如何知道彼此的?文章源自JAVA秀-https://www.javaxiu.com/39705.html
哨兵与 master 建立通信,利用 master 提供发布/订阅机制发布自己的信息,比如身高体重、是否单身、IP、端口……文章源自JAVA秀-https://www.javaxiu.com/39705.html
master 有一个 __sentinel__:hello
的专用通道,用于哨兵之间发布和订阅消息。这就好比是 __sentinel__:hello
微信群,哨兵利用 master 建立的微信群发布自己的消息,同时关注其他哨兵发布的消息 。文章源自JAVA秀-https://www.javaxiu.com/39705.html
❝文章源自JAVA秀-https://www.javaxiu.com/39705.html
面试官:哨兵之间虽然建立连接了,但是还需要和 slave 建立连接,不然没法监控他们呀,如何知道 slave 并监控他们的?文章源自JAVA秀-https://www.javaxiu.com/39705.html
关键还是利用 master 来实现,哨兵向 master 发送 INFO
命令, master 掌门自然是知道自己门下所有的 salve 小弟的。所以 master 接收到命令后,便将 slave 列表告诉哨兵。文章源自JAVA秀-https://www.javaxiu.com/39705.html
哨兵根据 master 响应的 slave 名单信息与每一个 salve 建立连接,并且根据这个连接持续监控哨兵。文章源自JAVA秀-https://www.javaxiu.com/39705.html
INFO命令获取slave信息文章源自JAVA秀-https://www.javaxiu.com/39705.html
Cluster 集群连环炮
❝文章源自JAVA秀-https://www.javaxiu.com/39705.html
面试官:除了哨兵以外,还有其他的高可用手段么?文章源自JAVA秀-https://www.javaxiu.com/39705.html
有 Cluster 集群实现高可用,哨兵集群监控的 Redis 集群是主从架构,无法横向拓展。使用 Redis Cluster 集群,主要解决了大数据量存储导致的各种慢问题,同时也便于横向拓展。文章源自JAVA秀-https://www.javaxiu.com/39705.html
在面向百万、千万级别的用户规模时,横向扩展的 Redis 切片集群会是一个非常好的选择。文章源自JAVA秀-https://www.javaxiu.com/39705.html
❝文章源自JAVA秀-https://www.javaxiu.com/39705.html
面试官:什么是 Cluster 集群?文章源自JAVA秀-https://www.javaxiu.com/39705.html
Redis 集群是一种分布式数据库方案,集群通过分片(sharding)来进行数据管理(「分治思想」的一种实践),并提供复制和故障转移功能。文章源自JAVA秀-https://www.javaxiu.com/39705.html
将数据划分为 16384 的 slots,每个节点负责一部分槽位。槽位的信息存储于每个节点中。文章源自JAVA秀-https://www.javaxiu.com/39705.html
它是去中心化的,如图所示,该集群由三个 Redis 节点组成,每个节点负责整个集群的一部分数据,每个节点负责的数据多少可能不一样。文章源自JAVA秀-https://www.javaxiu.com/39705.html
Redis 集群架构文章源自JAVA秀-https://www.javaxiu.com/39705.html
三个节点相互连接组成一个对等的集群,它们之间通过 Gossip
协议相互交互集群信息,最后每个节点都保存着其他节点的 slots 分配情况。文章源自JAVA秀-https://www.javaxiu.com/39705.html
❝文章源自JAVA秀-https://www.javaxiu.com/39705.html
面试官:哈希槽又是如何映射到 Redis 实例上呢?文章源自JAVA秀-https://www.javaxiu.com/39705.html
根据键值对的 key,使用 CRC16 算法,计算出一个 16 bit 的值;文章源自JAVA秀-https://www.javaxiu.com/39705.html
将 16 bit 的值对 16384 执行取模,得到 0 ~ 16383 的数表示 key 对应的哈希槽。文章源自JAVA秀-https://www.javaxiu.com/39705.html
根据该槽信息定位到对应的实例。文章源自JAVA秀-https://www.javaxiu.com/39705.html
键值对数据、哈希槽、Redis 实例之间的映射关系如下:文章源自JAVA秀-https://www.javaxiu.com/39705.html
数据、Slot与实例的映射文章源自JAVA秀-https://www.javaxiu.com/39705.html
❝文章源自JAVA秀-https://www.javaxiu.com/39705.html
面试官:Cluster 如何实现故障转移?文章源自JAVA秀-https://www.javaxiu.com/39705.html
Redis 集群节点采用 Gossip
协议来广播自己的状态以及自己对整个集群认知的改变。比如一个节点发现某个节点失联了 (PFail),它会将这条信息向整个集群广播,其它节点也就可以收到这点失联信息。文章源自JAVA秀-https://www.javaxiu.com/39705.html
如果一个节点收到了某个节点失联的数量 (PFail Count) 已经达到了集群的大多数,就可以标记该节点为确定下线状态 (Fail),然后向整个集群广播,强迫其它节点也接收该节点已经下线的事实,并立即对该失联节点进行主从切换。文章源自JAVA秀-https://www.javaxiu.com/39705.html
❝文章源自JAVA秀-https://www.javaxiu.com/39705.html
面试官:客户端又怎么确定访问的数据分布在哪个实例上呢?文章源自JAVA秀-https://www.javaxiu.com/39705.html
Redis 实例会将自己的哈希槽信息通过 Gossip 协议发送给集群中其他的实例,实现了哈希槽分配信息的扩散。文章源自JAVA秀-https://www.javaxiu.com/39705.html
这样,集群中的每个实例都有所有哈希槽与实例之间的映射关系信息。文章源自JAVA秀-https://www.javaxiu.com/39705.html
当客户端连接任何一个实例,实例就将哈希槽与实例的映射关系响应给客户端,客户端就会将哈希槽与实例映射信息缓存在本地。文章源自JAVA秀-https://www.javaxiu.com/39705.html
当客户端请求时,会计算出键所对应的哈希槽,再通过本地缓存的哈希槽实例映射信息定位到数据所在实例上,再将请求发送给对应的实例。文章源自JAVA秀-https://www.javaxiu.com/39705.html
Redis 客户端定位数据所在节点文章源自JAVA秀-https://www.javaxiu.com/39705.html
❝文章源自JAVA秀-https://www.javaxiu.com/39705.html
面试官:什么是 Redis 重定向机制?文章源自JAVA秀-https://www.javaxiu.com/39705.html
哈希槽与实例之间的映射关系由于新增实例或者负载均衡重新分配导致改变了,客户端将请求发送到实例上,这个实例没有相应的数据,该 Redis 实例会告诉客户端将请求发送到其他的实例上 。文章源自JAVA秀-https://www.javaxiu.com/39705.html
Redis 通过 MOVED 错误和 ASK 错误告诉客户端。文章源自JAVA秀-https://www.javaxiu.com/39705.html
MOVED
MOVED 错误(负载均衡,数据已经迁移到其他实例上):当客户端将一个键值对操作请求发送给某个实例,而这个键所在的槽并非由自己负责的时候,该实例会返回一个 MOVED 错误指引转向正在负责该槽的节点。文章源自JAVA秀-https://www.javaxiu.com/39705.html
同时,客户端还会更新本地缓存,将该 slot 与 Redis 实例对应关系更新正确 。文章源自JAVA秀-https://www.javaxiu.com/39705.html
文章源自JAVA秀-https://www.javaxiu.com/39705.html
MOVED 指令文章源自JAVA秀-https://www.javaxiu.com/39705.html
ASK
如果某个 slot 的数据比较多,部分迁移到新实例,还有一部分没有迁移。文章源自JAVA秀-https://www.javaxiu.com/39705.html
如果请求的 key 在当前节点找到就直接执行命令,否则时候就需要 ASK 错误响应了。文章源自JAVA秀-https://www.javaxiu.com/39705.html
槽部分迁移未完成的情况下,如果需要访问的 key 所在 Slot 正在从 实例 1 迁移到 实例 2(如果 key 已经不在实例 1),实例 1 会返回客户端一条 ASK 报错信息:客户端请求的 key 所在的哈希槽正在迁移到实例 2 上,你先给实例 2 发送一个 ASKING 命令,接着发发送操作命令 。文章源自JAVA秀-https://www.javaxiu.com/39705.html
比如客户端请求定位到 key = 「码哥字节」的槽 16330 在实例 172.17.18.1 上,节点 1 如果找得到就直接执行命令,否则响应 ASK 错误信息,并指引客户端转向正在迁移的目标节点 172.17.18.2。文章源自JAVA秀-https://www.javaxiu.com/39705.html
A文章源自JAVA秀-https://www.javaxiu.com/39705.html
注意:ASK 错误指令并不会更新客户端缓存的哈希槽分配信息 。文章源自JAVA秀-https://www.javaxiu.com/39705.html
- END -文章源自JAVA秀-https://www.javaxiu.com/39705.html
欢迎加入我的知识星球,一起探讨架构,交流源码。加入方式,长按下方二维码噢:文章源自JAVA秀-https://www.javaxiu.com/39705.html
文章源自JAVA秀-https://www.javaxiu.com/39705.html
已在知识星球更新源码解析如下:文章源自JAVA秀-https://www.javaxiu.com/39705.html
文章源自JAVA秀-https://www.javaxiu.com/39705.html
文章源自JAVA秀-https://www.javaxiu.com/39705.html
文章源自JAVA秀-https://www.javaxiu.com/39705.html
文章源自JAVA秀-https://www.javaxiu.com/39705.html
最近更新《芋道 SpringBoot 2.X 入门》系列,已经 20 余篇,覆盖了 MyBatis、Redis、MongoDB、ES、分库分表、读写分离、SpringMVC、Webflux、权限、WebSocket、Dubbo、RabbitMQ、RocketMQ、Kafka、性能测试等等内容。文章源自JAVA秀-https://www.javaxiu.com/39705.html
提供近 3W 行代码的 SpringBoot 示例,以及超 4W 行代码的电商微服务项目。文章源自JAVA秀-https://www.javaxiu.com/39705.html
获取方式:点“在看”,关注公众号并回复 666 领取,更多内容陆续奉上。文章源自JAVA秀-https://www.javaxiu.com/39705.html
文章源自JAVA秀-https://www.javaxiu.com/39705.html
文章有帮助的话,在看,转发吧。谢谢支持哟 (*^__^*)文章源自JAVA秀-https://www.javaxiu.com/39705.html
阅读原文文章源自JAVA秀-https://www.javaxiu.com/39705.html

评论