Redis为什么快如何实现高可用及持久化
来源:脚本之家
时间:2023-01-01 15:14:14 224浏览 收藏
来到golang学习网的大家,相信都是编程学习爱好者,希望在这里学习数据库相关编程知识。下面本篇文章就来带大家聊聊《Redis为什么快如何实现高可用及持久化》,介绍一下高可用、Redis持久化,希望对大家的知识积累有所帮助,助力实战开发!
前言
作为Java程序员,在面试过程中,缓存相关的问题是躲不掉的,肯定会问,例如缓存一致性问题,缓存雪崩、击穿、穿透等。说到缓存,那肯定少不了Redis,我在面试的时候也是被问了很多关于Redis相关的知识,但是Redis的功能太强大了,并不是一时半会儿能掌握好的,因为有些高级特性或是知识平时并不会用到。
所以回答的不好,人家就会觉得你对自己平时使用的工具都没有了解,自然就凉凉了。其实很早就有这个打算,打算好好总结一下Redis的知识,但也是由于自己都没有好好的了解Redis呢,所以一直没有开始。这次准备慢慢的来总结。
Redis为什么这么快
Redis是一个由C语言编写的开源的,基于内存,支持多种数据结构可持久化的NoSQL数据库。
它速度快主要是有以下几个原因:
- 基于内存运行,性能高效;
- 数据结构设计高效,例如String是由动态字符数组构成,zset内部的跳表;
- 采用单线程,避免了线程的上下文切换,也避免了线程竞争产生的死锁等问题;
- 使用I/O多路复用模型,非阻塞IO;
官网上给出单台Redis的可以达到10w+的QPS的, 一台服务器上在使用Redis的时候单核的就够了,但是目前服务器都是多核CPU,要想不浪费资源,又能提交效率,可以在一台服务器上部署多个Redis实例。
高可用方案
虽然单台Redis的的性能很好,但是Redis的单节点并不能保证它不会挂了啊,毕竟单节点的Redis是有上限的,而且人家单节点又要读又要写,小身板扛不住咋办,所以为了保证高可用,一般都是做成集群。
主从(Master-Slave)
Redis官方是支持主从同步的,而且还支持从从同步,从从同步也可以理解为主从同步,只不过从从同步的主节点是另一个主从的从节点。
有了主从同步的集群,那么主节点就负责提供写操作,而从节点就负责支持读操作。
那么他们之间是如何进行数据同步的呢?
如果Slave(从节点)是第一次跟Master进行连接,
- 那么会首先会向Master发送同步请求
psync
; - 主节点接收到同步请求,开始fork主子进程开始进行全量同步,然后生成RDB文件;
- 这个时候主节点同时会将新的写请求,保存到缓存区(buffer)中;
- 从节点接收到RDB文件后,先清空老数据,然后将RDB中数据加载到内存中;
- 等到从节点将RDB文件同步完成后再同步缓存区中的写请求。
数组被占满之后就会覆盖掉最早之前的数据。
所以如果由于网络或是其他原因,造成缓存区中的数据被覆盖了,那么当从节点处理完主节点的RDB文件后,就不得不又要进行一全量的RDB文件的复制,才能保证主从节点的数据一致。
如果不设置好合理的buffer区空间,是会造成一个RDB复制的死循环。
当主从间的数据同步完成之后,后面主节点的每次写操作就都会同步到从节点,这样进行增量同步了。
由于负载的不断上升就导致了主从之间的延时变大,所以就有了上面我说的从从同步了,主节点先同步到一部分从节点,然后由从节点去同步其他的从节点。
在Redis从2.8.18开始支持无盘复制,主节点通过套接字,一边遍历内存中的数据,一边让数据发送给从节点,从节点和之前一样,先将数据存储在磁盘文件中,然后再一次性加载。
另外由于主从同步是异步的,所以从Redis3.0之后出现了同步复制,就是通过wait命令来进行控制,wait命令有两个参数,第一个是从库数量,第二个是等待从库的复制时间,如果第二个参数设置为0,那么就是代表要等待所有从库都复制完才去执行后面的命令。
但是这样就会存在一个隐患,当网络异常后,wait命令会一直阻塞下去,导致Redis不可用。
哨兵(Sentinel)
哨兵可以监控Redis集群的健康状态,当主节点挂掉之后,选举出新的主节点。客户端在使用Redis的时候会先通过Sentinel来获取主节点地址,然后再通过主节点来进行数据交互。当主节点挂掉之后,客户端会再次向Sentinel获取主节点,这样客户端就可以无感知的继续使用了。
哨兵集群工作过程,主节点挂掉之后会选举出新的主节点,然后监控挂掉的节点,当挂掉的节点恢复后,原先的主节点就会变成从节点,从新的主节点那里建立主从关系。
集群分片(Redis Cluster)
Redis Cluster是Redis官方推荐的集群模式,Redis Cluster将所有数据划分到16384个槽(slots
)中,每个节点负责一部分槽位的读写操作。
存储
Redis Cluster默认是通过CRC16算法获取到key的hash值,然后再对16384进行取余(CRC16(key)%16384
),获取到的槽位在哪个节点负责的范围内(这里一般是会有一个槽位和节点的映射表来进行快速定位节点的,通常使用bitmap来实现),就存储在哪个节点上。
重定向
当Redis Cluster的客户端在和集群建立连接的时候,也会获得一份槽位和节点的配置关系(槽位和节点的映射表),这样当客户端要查找某个key时,可以直接定位到目标节点。
但是当客户端发送请求时,如果接收请求的节点发现该数据的槽位并不在当前节点上,那么会返回MOVED
指令将正确的槽位和节点信息返回给客户端,客户接着请求正确的节点获取数据。
一般客户端在接收到MOVED
指令后,也会更新自己本地的槽位和节点的映射表,这样下次获取数据时就可以直接命中了。这整个重定向的过程对客户端是透明的。
数据迁移
当集群中新增节点或删除节点后,节点间的数据迁移是按槽位为单位的,一个槽位一个槽位的迁移,当迁移时原节点状态处于:magrating
,目标节点处于:importing
。
在迁移过程中,客户端首先访问旧节点,如果数据还在旧节点,那么旧节点正常处理,如果不在旧节点,就会返回一个-ASK + 目标节点地址
的指令,客户端收到这个-ASK
指令后,向目标节点执行一个asking
指令(告诉新节点,必须处理客户端这个数据),然后再向目标节点执行客户端的访问数据的指令。
容错
Redis Cluster可以为每个主节点设置多个从节点,当单个主节点挂掉后,集群会自动将其中某个从节点提升为主节点,若没有从节点,那么集群将处于不可用状态。
Redis提供了一个参数:cluster-require-full-coverage
,用来配置可以允许部分节点出问题后,还有其他节点在运行时可以正常提供服务。
另外一点比较特殊的是,Cluster中当一个节点发现某个其他节点出现失联了,这个时候问题节点只是PFail
(Possibly
-可能下线),然后它会把这个失联信息广播给其他节点,当一个节点接收到某个节点的失联信息达到集群的大多数时,就可以将失联节点标记为下线,然后将下线信息广播给其他节点。若失联节点为主节点,那么将立即对该节点进行主从切换。
Redis高可用就先说到这里吧,后面其实还有Codis,但是目前Cluster逐渐流行起来了,Codis的竞争力逐渐被蚕食,而且对新版本的支持,更新的也比较慢,所以这里就不说它了,感兴趣的可以自己去了解一下,国人开源的Redis集群模式Codis。
持久化
Redis持久化的意义在于,当出现宕机问题后,能将数据恢复到缓存中,它提供了两种持久化机制:一种是快照(RDB),一种是AOF日志。
快照是一次全量备份,而AOF是增量备份。快照是内存数据的二进制序列化形式,存储上非常紧凑,而AOF日志记录的是内存数据修改的指令记录文本。
快照备份(RDB)
因为Redis是单线程的,所以在做快照持久化的时候,通常有两个选择,save命令,会阻塞线程,直到备份完成;bgsave会异步的执行备份,其实是fork出了一个子进程,用子进程去执行快照持久化操作,将数据保存在一个.rdb文件中。
子进程刚刚产生的时候,是和父进程共享内存中的数据的,但是子进程做持久化时,是不会修改数据的,而父进程是要持续提供服务的,所以父进程就会持续的修改内存中的数据,这个时候父进程就会将内存中的数据,Copy出一份来进行修改。
父进程copy的数据是以数据页为单位的(4k一页),对那一页数据进行修改就copy哪一页的数据。
子进程由于数据没有变化就会一直的去遍历数据,进程持久化操作了,这就是只保留了创建子进程的时候的快照。
那么RDB是在什么时候触发的呢?
# savesave 60 10000 save 300 10
上这段配置就是在redis.conf文件中配置的,第一个参数是时间单位是秒,第二个参数执行数据变化的次数。
意思就是说:300秒之内至少发生10次写操作、
60秒之内发生至少10000次写操作,只要满足任一条件,均会触发bgsave
增量日志备份(AOF)
Redis在接收到客户端请求指令后,会先进行校验,校验成功后,立即将指令存储到AOF日志文件中,就是说,Redis是先记录日志,再执行命令。这样即使命令还没执行突然宕机了,通过AOF日志文件也是可以恢复的。
AOF重写
AOF日志文件,随着时间的推移,会越来越大,所以就需要进行重写瘦身。AOF重写的原理就是,fork一个子进程,对内存进行遍历,然后生成一系列的Redis指令,然后序列化到一个新的aof文件中。然后再将遍历内存阶段的增量日志,追加到新的aof文件中,追加完成后立即替换旧的aof文件,这样就完成了AOF的瘦身重写。
fsync
因为AOF是一个写文件的IO操作,是比较耗时。所以AOF日志并不是直接写入到日志文件的,而是先写到一个内核的缓存中,然后通过异步刷脏,来将数据保存到磁盘的。
由于这个情况,就导致了会有还没来得急刷脏然后就宕机了,导致数据丢失的风险。
所以Redis提供了一个配置,可以手动的来选择刷脏的频率。
- always:每一条AOF记录都立即同步到文件,性能很低,但较为安全。
- everysec:每秒同步一次,性能和安全都比较中庸的方式,也是redis推荐的方式。如果遇到物理服务器故障,可能导致最多1秒的AOF记录丢失。
- no:Redis永不直接调用文件同步,而是让操作系统来决定何时同步磁盘。性能较好,但很不安全。
AOF默认是关闭的,需要在配置文件中手动开启。
# 只有在“yes”下,aof重写/文件同步等特性才会生效 appendonly yes ## 指定aof文件名称 appendfilename appendonly.aof ## 指定aof操作中文件同步策略,有三个合法值:always everysec no,默认为everysec appendfsync everysec ## 在aof-rewrite期间,appendfsync是否暂缓文件同步,"no"表示“不暂缓”,“yes”表示“暂缓”,默认为“no” no-appendfsync-on-rewrite no ## aof文件rewrite触发的最小文件尺寸(mb,gb),只有大于此aof文件大于此尺寸是才会触发rewrite,默认“64mb”,建议“512mb” auto-aof-rewrite-min-size 64mb
Redis4.0混合持久化
Redis4.0提供了一种新的持久化机制,就是RDB和AOF结合使用,将rdb文件内容和aof文件存在一起,AOF中保存的不再是全部数据了,而是从RDB开始的到结束的增量日志。
这样在Redis恢复数据的时候,可以先假装RDB文件中的内容,然后在顺序执行AOF日志中指令,这样就将Redis重启时恢复数据的效率得到了大幅度提升。
结尾
恩,这次就先总结到这里吧,后面会继续总结Redis相关知识,LRU、LFU、内存淘汰策略,管道等等。
以上就是本文的全部内容了,是否有顺利帮助你解决问题?若是能给你带来学习上的帮助,请大家多多支持golang学习网!更多关于数据库的相关知识,也可关注golang学习网公众号。
-
317 收藏
-
175 收藏
-
495 收藏
-
433 收藏
-
190 收藏
-
342 收藏
-
361 收藏
-
159 收藏
-
164 收藏
-
221 收藏
-
156 收藏
-
- 前端进阶之JavaScript设计模式
- 设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
- 立即学习 542次学习
-
- GO语言核心编程课程
- 本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
- 立即学习 507次学习
-
- 简单聊聊mysql8与网络通信
- 如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
- 立即学习 497次学习
-
- JavaScript正则表达式基础与实战
- 在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
- 立即学习 487次学习
-
- 从零制作响应式网站—Grid布局
- 本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
- 立即学习 484次学习
-
- 玩命的钢笔
- 很详细,mark,感谢楼主的这篇文章内容,我会继续支持!
- 2023-03-11 20:29:44
-
- 烂漫的黑夜
- 这篇技术贴太及时了,老哥加油!
- 2023-02-14 20:49:38
-
- 害怕的老虎
- 太给力了,一直没懂这个问题,但其实工作中常常有遇到...不过今天到这,看完之后很有帮助,总算是懂了,感谢师傅分享技术贴!
- 2023-02-12 11:42:13
-
- 背后的花卷
- 这篇文章真及时,太细致了,写的不错,mark,关注师傅了!希望师傅能多写数据库相关的文章。
- 2023-02-04 05:50:33
-
- 陶醉的灯泡
- 很好,一直没懂这个问题,但其实工作中常常有遇到...不过今天到这,看完之后很有帮助,总算是懂了,感谢大佬分享博文!
- 2023-02-03 11:32:04
-
- 危机的河马
- 这篇文章内容太及时了,作者大大加油!
- 2023-02-02 11:08:12
-
- 包容的鼠标
- 这篇文章太及时了,很详细,很好,收藏了,关注作者了!希望作者能多写数据库相关的文章。
- 2023-02-02 01:33:25
-
- 迷路的小鸽子
- 太全面了,收藏了,感谢老哥的这篇技术文章,我会继续支持!
- 2023-02-01 04:42:22
-
- 含蓄的可乐
- 细节满满,已收藏,感谢博主的这篇文章内容,我会继续支持!
- 2023-01-28 09:02:19
-
- 冷静的花生
- 这篇博文太及时了,作者加油!
- 2023-01-23 03:30:56
-
- 合适的红牛
- 这篇技术文章真是及时雨啊,细节满满,很有用,码住,关注作者大大了!希望作者大大能多写数据库相关的文章。
- 2023-01-19 14:59:30
-
- 干净的蚂蚁
- 写的不错,一直没懂这个问题,但其实工作中常常有遇到...不过今天到这,帮助很大,总算是懂了,感谢大佬分享文章内容!
- 2023-01-12 21:38:21
-
- 爱笑的大侠
- 这篇文章内容真是及时雨啊,博主加油!
- 2023-01-06 20:40:23
-
- 现实的酒窝
- 感谢大佬分享,一直没懂这个问题,但其实工作中常常有遇到...不过今天到这,看完之后很有帮助,总算是懂了,感谢博主分享文章!
- 2023-01-06 17:33:39
-
- 昏睡的小懒虫
- 这篇博文真是及时雨啊,太详细了,受益颇多,已加入收藏夹了,关注博主了!希望博主能多写数据库相关的文章。
- 2023-01-06 12:54:19
-
- 甜美的凉面
- 这篇博文出现的刚刚好,细节满满,受益颇多,码起来,关注up主了!希望up主能多写数据库相关的文章。
- 2023-01-04 13:02:40
-
- 欣慰的雪糕
- 这篇博文太及时了,很详细,感谢大佬分享,已加入收藏夹了,关注大佬了!希望大佬能多写数据库相关的文章。
- 2023-01-03 06:47:47
-
- 典雅的电话
- 太给力了,一直没懂这个问题,但其实工作中常常有遇到...不过今天到这,看完之后很有帮助,总算是懂了,感谢作者分享文章内容!
- 2023-01-03 05:27:31
-
- 阔达的蓝天
- 太细致了,mark,感谢作者大大的这篇文章,我会继续支持!
- 2023-01-02 23:26:21
-
- 贤惠的蜻蜓
- 这篇文章出现的刚刚好,很详细,受益颇多,已加入收藏夹了,关注作者了!希望作者能多写数据库相关的文章。
- 2023-01-02 01:35:03
-
- 甜美的睫毛膏
- 太给力了,一直没懂这个问题,但其实工作中常常有遇到...不过今天到这,帮助很大,总算是懂了,感谢老哥分享博文!
- 2023-01-01 20:37:57