Redis源码,主从同步与对象模型

文章目录

  • 一、持久化与持久化选择
    • redis持久化
    • redis持久化相关配置
    • aof
    • aof rewrite
    • rdb
    • 混合持久化
    • 数据安全策略
  • 二、Redis主从复制
    • 数据同步
    • 增量数据同步
    • 服务器 RUN ID
    • 复制偏移量 offset
  • 三、redis哨兵模式
    • 分布式中的cap原则
    • Codis集群
  • 四、Redis cluster集群
    • 集群配置


一、持久化与持久化选择

redis持久化

redis 的数据全部在内存中,如果突然宕机,数据就会全部丢失,因此需要持久化来保证 Redis 的数据不会因为故障而丢失,redis 重启的时候可以重新加载持久化文件来恢复数据。
我们的目的呢就是把持久化的数据从内存转入磁盘当中,跟其他kv数据库不同的是,redis所有数据都在内存当中

redis持久化相关配置

###### aof ###### 
# redis.cnf 
appendonly no 
appendfilename "appendonly.aof" #指定aof的文件名
#aof就由以上两个命令进行开启
# appendfsync always # 这个策略表示每个命令都会持久化
appendfsync everysec # 这个就是策略,everysec就是每秒钟持久化一次
# appendfsync no 
# auto-aof-rewrite-percentage 为 0 则关闭 aof 复写 
auto-aof-rewrite-percentage 100 
auto-aof-rewrite-min-size 64mb
 # yes 如果 aof 数据不完整,尽量读取最多的格式正确的数据; 
 # no 如果 aof 数据不完整 报错,可以通过 redis-check-aof 来修复 aof 文件; 
 aof-load-truncated yes 
 # 开启混合持久化 
 aof-use-rdb-preamble yes 
 ###### rdb ###### 
 # save "" 
 # save 3600 1 
 # save 300 100 
 # save 60 10000 

默认配置下,只开启 rdb 持久化;
提供这几种持久化配置
1、aof,2、rdb,3、aof复写,4、abd-rbd混用
1和2进行比较,3解决了1什么问题,4解决了3什么问题,这是主体思路

aof

aof就是append only file,在文件当中辅加数据的意思
aof 日志存储的是 Redis 服务器的顺序指令序列,aof 日志只记录对内存修改的指令记录;
我们来看看aof到底是怎样持久化的一个过程,当我们调用call函数(例如写操作),执行完以后会去检测是否开启aof,开启以后将数据写入aof buf当中,然后根据策略刷到磁盘当中去
Redis源码,主从同步与对象模型_第1张图片
我们需要知道策略怎么选择
aof是一个单线程执行的,需要占用逻辑主线程
我们还需要知道持久化什么内容,是这条命令的协议数据
协议命令是什么数据呢,我们来看一下redis.conf这个文件的内容
在这里插入图片描述
在把这块基本的配置复制进去
Redis源码,主从同步与对象模型_第2张图片
我们看看这基本的配置,pidfile表示这个进程所对应的pid会写到这个文件中,我们修改一下它们
在这里插入图片描述
我们照着之前的配置去修改,最后那个x就是保存的意思
Redis源码,主从同步与对象模型_第3张图片
我们用redis-server redis.conf这条命令去启动配置文件,我们可以用ps aux | grep redis-server看到启动成功
Redis源码,主从同步与对象模型_第4张图片
用命令去关闭所有的redis-server
Redis源码,主从同步与对象模型_第5张图片
然后再启动新的redis-server,我们就运行了新的6379端口的server

在这里插入图片描述
我们用命令行redis-cli,我们看见已经生成

Redis源码,主从同步与对象模型_第6张图片
打开后我们发现就是我们要找的协议的内容
Redis源码,主从同步与对象模型_第7张图片
我们开启另外一个客户端,然后输入redis的set key的命令,再去看aof里边新加了内容,这都是协议的内容
Redis源码,主从同步与对象模型_第8张图片
以上就是aof的内容,我们再来思考一下具体原理是什么,进程宕机了,下一次进程重启的时候,我们就会去读这个aof文件,读aof文件就是把这个数据一条一条取出来,在我们的redis server命令中重放,也就是重新执行之前宕机了的redis命令,这就是重放。
要恢复,就需要通过重放(replay)aof 日志中指令序列来恢复 Redis 当前实例的内存数据结构的状态;

配置

set key val 
# 开启 aof 
appendonly yes 
# 关闭 aof复写 
auto-aof-rewrite-percentage 0 
# 关闭 混合持久化 
aof-use-rdb-preamble no 
# 关闭 rdb 
save ""

策略

# 1. 每条命令刷盘 redis 事务才具备持久性 
# appendfsync always 
# 2. 每秒刷盘 
appendfsync everysec 
# 3. 交由系统刷盘 
# appendfsync no

缺点呢,就是随着时间越长,aof 日志会越来越长,如果 redis 重启,重放整个 aof 日志会非常耗时,导致redis 长时间无法对外提供服务;重放的过程中是阻塞的。
我们的解决方案就是aof rewrite,会解决aof日志越来越长的问题

aof rewrite

aof呢在每条命令执行都会被添加数据,redis中执行添加数据的命令,我们来看看这个场景
先执行lpush list whistle,然后再执行lpop list,也就是添加完以后再执行把刚刚加入的数据弹出来,这两个步骤是不是可以删掉没有任何的意义,我们的aof rewrite就是来解决这个问题的。将aof某些数据进行删除。
再比如说,我们连续执行三次lpush list xx,xx表示三次不同的数据,我们还能把它们合并成一个命令,我们的aof rewrite就是来解决这个问题的。
aof是来占用io主线程的,只会是一个网络io,还提供了一个磁盘io,这会影响我们整个服务器性能。rewrite也能解决这个问题。
Redis源码,主从同步与对象模型_第9张图片
我们怎么做呢,我们通过fork进程的方式来进行持久化,跟rdb类似,为啥要这么做呢,他们几个进程都会有一个虚拟内存指向同一个物理内存,子进程在父进程这里拷贝了一个镜像,这里对主数据库的修改并不影响从数据库,从数据库不提供服务,我们就可以利用从数据库把它持久化到磁盘中去。这就是fork的目的,不会占用主进程,会另起一个进程进行持久化
第一种做法就是用命令的方式进行持久化,bgrewriteaof,这样就会立马fork一个进程对aof进行一个瘦身,fork出去的进程对文件进行修改然后覆盖原来的aof文件。
我们有个前提是aof rewrite的开启是开启aof
也可以用配置文件的方式去rewrite
aof 持久化策略会持久化所有修改命令;里面的很多命令其实可以合并或者删除;
aof rewrite 在 aof 的基础上,满足一定策略则 fork 进程,根据当前内存状态,转换成一系列的 redis 命令,序列化成一个新的 aof 日志文件中,序列化完毕后再将操作期间发生的增量 aof 日志追加到新的 aof 日志文件中国你,追加完毕后替换旧的 aof 日志文件;以此达到对 aof 日志瘦身的目的;
注意:aof rewrite 开启的前提是开启 aof;
我们来看看配置文件是怎么做的

# 开启 
aof appendonly yes 
# 开启 aof复写 
auto-aof-rewrite-percentage 100 #下一次变成128m的时候,我们再来fork一次,下次256的时候再fork一次
auto-aof-rewrite-min-size 64mb #aof的文件数据超过64mb的时候就会进行一次aof fork进程
# 关闭 混合持久化 
aof-use-rdb-preamble no 
# 关闭 
rdb save "" 

根据内存当前数据的状态,自动生成aof文件,这个aof文件依然是我们的协议,是根据内存当中的数据生成的,这是最终的状态生成的,所以不会出现push一个数据再pop一个数据的情况,最后生成的文件只有增加操作的文件,是比较精简的aof。
那么父进程在干什么呢,我们在fork进程当中,我们对父进程进行修改的时候,是写在内存当中,记录一些统计状态和指标,当我们在流程图当中结束之后,通过管道的方式去通知父进程,通过管道pipe发送命令的方式去告诉主进程已经执行完了,统一把内存当中的数据写到磁盘中去,写的数据是辅加在aof复写文件的末尾,这就是持久化复写的过程。

策略

# 1. redis 会记录上次aof复写时的size,如果之后累计超过了原来的size,则会发生aof复写; 
auto-aof-rewrite-percentage 100 
# 2. 为了避免策略1中,小数据量时产生多次发生aof复写,策略2在满足策略1的前提下需要超过 64mb 才会发生aof复写; 
auto-aof-rewrite-min-size 64mb

aof rewrite缺点是什么呢,aof复写在 aof 基础上实现了瘦身,但是 aof 复写的数据量仍然很大;加载会非常慢。我们来看一下rdb

rdb

基于 aof 或 aof 复写文件大的缺点,rdb 是一种快照持久化;它通过 fork 主进程,在子进程中将内存当中的数据键值对按照存储方式持久化到 rdb 文件中;rdb 存储的是经过压缩的二进制数据;
aof里边存储的是协议的内容,rdb不是根据具体命令的协议,它用的一种很快的方式,根据内存存储数据的状态,直接持久化成rdb文件,未来加载的时候,直接把rdb的数据加载到我们的内存中,内存根据数据直接进行读写。根据每一种数据类型的存储状态写到我们的磁盘当中去
Redis源码,主从同步与对象模型_第10张图片
配置

# 关闭 aof 同时也关闭了 aof复写 
appendonly no 
# 关闭 aof复写 
auto-aof-rewrite-percentage 0 
# 关闭 混合持久化 
aof-use-rdb-preamble no 
# 开启 rdb 也就是注释 save "" 
# save "" 
# save 3600 1 
# save 300 100 
# save 60 10000

之前我们提到的持久化方式,134都需要打开aof,2‘、rdb不用’,我们就需要把这三个都关闭。最后默认的是rdb的持久化。
那么什么时候fork进程呢,它会记录一个脏数据的个数
例如save 3600 1,一个小时修改一次,接下来的三个命令 save 3600 1 ,save 300 100,save 60 10000达成条件就会进行fork

策略

# redis 默认策略如下: 
# 注意:写了多个 save 策略,只需要满足一个则开启rdb持久化 
# 3600 秒内有以1次修改 
save 3600 1 
# 300 秒内有100次修改 
save 300 100 
# 60 秒内有10000次修改 
save 60 10000

缺点是什么呢,若采用 rdb 持久化,一旦 redis 宕机,redis 将丢失一段时间的数据;为什么会丢失数据呢,我们考虑一下什么时候不会出现以上save的情况,3600秒我们做了99次修改,99次修改不满足第二个save 300 100的这种条件,也不满足下边这条命令的条件。我们在3600秒之内宕机了,丢失了99次修改的数据。那会丢失多长时间呢,我们做完fork之后,我们对redis进行了99次修改,我们不会满足save 300与60的条件,我们就没有进行持久化,没有根据持久化重启,就会有数据丢失。
RDB 需要经常 fork 子进程来保存数据集到硬盘上,当数据集比较大的时候,fork 的过程是非常耗
时的,可能会导致 Redis 在一些毫秒级内不能响应客户端的请求。如果数据集巨大并且 CPU 性能不是很好的情况下,这种情况会持续1秒,AOF 也需要 fork,但是你可以调节重写日志文件的频率来提高数据集的耐久度。

当我们开启always的时候,一个数据都不会丢。开启aof的时候为啥要先处理写事件呢,读完之后要持久化,下一次数据循环,把数据发给对端,源码当中有个invert会进行一个读写调转顺序,要先处理aof持久化再把数据发给对端
everysec最多会丢失1s的数据
Redis源码,主从同步与对象模型_第11张图片
appendfsync no是什么意思呢,表示系统内核根据自己的数据状态帮我们刷盘。后果是不确定我们会丢失多少数据

混合持久化

这种持久化是需要开启aof的
从上面知道,rdb 文件小且加载快但丢失多,aof 文件大且加载慢但丢失少;混合持久化是吸取rdb 和 aof 两者优点的一种持久化方案;aof 复写的时候实际持久化的内容是 rdb,等持久化后,持久化期间修改的数据以 aof 的形式附加到文件的尾部;
混合持久化实际上是在 aof rewrite 基础上进行优化;所以需要先开启 aof rewrite;
解决的问题在于数据量还是很大的时候,在fork子进程时不再根据内存的数据去生成协议,aof存的就是协议的数据,我们直接根据内存的数据进行一个压缩,也就是生成rdb文件,生成rdb文件之后通过管道的方式去缓存aof命令,最后就会生成RDB FILE AOF FILE这样的持久化文件
混合持久化就是通过把aof复写流程是用rdb来替代的。通过管道发送信号告诉aof,把累积的aof发过去就会生成一个aof,最后生成的文件持久化到磁盘当中
Redis源码,主从同步与对象模型_第12张图片
配置

# 开启 aof 
appendonly yes 
# 开启 aof复写 
auto-aof-rewrite-percentage 100 
auto-aof-rewrite-min-size 64mb 
# 开启 混合持久化
aof-use-rdb-preamble yes
# 关闭 rdb 
save "" 
# save 3600 1
# save 300 100
# save 60 10000

应用
MySQL 缓存方案中,redis 不开启持久化,redis 只存储热点数据,数据的依据来源于MySQL;若某些数据经常访问需要开启持久化,此时可以选择 rdb 持久化方案,也就是允许丢失一段时间数据;
对数据可靠性要求高,在机器性能,内存也安全 (fork 写时复制 最差的情况下 96G)的情况下,可以让 redis 同时开启 aof 和 rdb,当我们aof与rdb都存在的情况下,是优先根据aof去重启我们的redis,aof能保证我们的数据是最新的,注意此时不是混合持久化;redis 重启优先从 aof 加载数据,理论上 aof 包含更多最新数据;如果只开启一种,那么使用混合持久化;
在允许丢失的情况下,亦可采用主redis不持久化(96G 90G),从redis进行持久化;
伪装从库(leveldb);mysql同步到redis也用了这种方法,我们做一个中间件,伪装成从数据库,把redis修改的数据拉到其他的kv数据库进行持久化,这样可以避免写时复制造成的两倍内存的问题。在游戏开发中这个用的比较多

数据安全策略

问题:拷贝持久化文件是否安全?
是安全的,持久化 文件一旦被创建, 就不会进行任何修改。 当服务器要创建一个新的持久化文件时, 它先将文件的内容保存在一个临时文件里面, 当临时文件写入完毕时, 程序才使用rename(2) 原子地用临时文件替换原来的持久化文件。scp这种拷贝命令是否安全,其实是安全的,因为有rename(2)这样的原子操作
数据安全要考虑两个问题:

  1. 节点宕机(redis 是内存数据库,宕机数据会丢失)
  2. 磁盘故障
    创建一个定期任务(cron job), 每小时将一个 RDB 文件备份到一个文件夹, 并且每天将一个RDB 文件备份到另一个文件夹。确保快照的备份都带有相应的日期和时间信息, 每次执行定期任务脚本时, 使用 find 命令来删除过期的快照: 比如说, 你可以保留最近 48 小时内的每小时快照, 还可以保留最近一两个月的每日快照。至少每天一次, 将 RDB 备份到你的数据中心之外, 或者至少是备份到你运行 Redis 服务器的物理
    机器之外

二、Redis主从复制

主要用来实现 redis 数据的可靠性;防止主 redis 所在磁盘损坏,造成数据永久丢失;主从之间采用异步复制的方式;
解决单点故障的问题,只有一个redis,机器损坏了怎么办,需要提供一个异地部署的存储的问题,会有两个从数据库。Redis源码,主从同步与对象模型_第13张图片
命令
指定从数据库
命令: redis-server --replicaof 127.0.0.1 7001 在 redis 5.0 以前使用 slaveof ;redis 5.0 之后使用 replicaof ;
用配置文件指定从数据库

# redis.conf 
replicaof 127.0.0.1 7002 
info replication

数据同步

全量数据同步,把主数据库所有的数据复制到从数据库中
tcp keeplive用户层的心跳包,探查目标进程是否有响应。
我们收到回包过后,发送同步命令,收到同步怎么做呢,master进程收到同步命令之后根据内存数据生成rdb文件,即便使用了aof的持久化,还是会生成rdb的。
Redis源码,主从同步与对象模型_第14张图片

增量数据同步

出现增量数据同步的原因是,主从数据库在不同的机器当中会发生网络抖动,会导致一些数据没有发送过去,就会造成主从数据不一致的情况,这里我们就必须容忍某一段时间数据不一致,我们就引入了下图当中的一个环形缓冲区,解决主从之间发生网络抖动,先把抖动的数据先写到环形缓冲区,从数据库会借助这个偏移值,每次同步的时候把这个偏移值发送给我们主数据库,看这个偏移值是否在环形缓冲区当中,如果在缓冲区当中,那么就会根据这个偏移值到我们的起始地址,把这一块抖动的数据同步给从数据库,避免全量数据类型的大量同步的效率降低。
这里抓这几个,偏移地址,runid(谁是谁的从数据库的id),就拿这两个去主数据库认证
Redis源码,主从同步与对象模型_第15张图片

服务器 RUN ID

无论主库还是从库都有自己的 RUN ID,RUN ID 启动时自动产生,RUN ID 由40个随机的十六进制字符组成;
当从库对主库初次复制时,主库将自身的 RUN ID 传送给从库,从库会将 RUN ID 保存;
当从库断线重连主库时,从库将向主库发送之前保存的 RUN ID;
从库 RUN ID 和主库 RUN ID 一致,说明从库断线前复制的就是当前的主库;主库尝试执行增量同步操作;
若不一致,说明从库断线前复制的主库并不时当前的主库,则主库将对从库执行全量同步操作;

复制偏移量 offset

偏移值会从0到2的64次方一直加,在redis当中用2的64次方以内的数作为偏移值,永远也耗不尽,所以在环形缓冲区当中不会出现回绕的问题。
主从都会维护一个复制偏移量;
主库向从库发送N个字节的数据时,将自己的复制偏移量上加N;
从库接收到主库发送的N个字节数据时,将自己的复制偏移量加上N;
通过比较主从偏移量得知主从之间数据是否一致;偏移量相同则数据一致;偏移量不同则数据不一致;
环形缓冲区(复制积压缓冲区)
本质:固定长度先进先出队列;
存储内容:如下图;
当因某些原因(网络抖动或从库宕机)从库与主库断开连接,避免重新连接后开始全量同步,在主
库设置了一个环形缓冲区;该缓冲区会在从库失联期间累计主库的写操作;当从库重连,会发送自
身的复制偏移量到主库,主库会比较主从的复制偏移量:
若从库offset还在复制积压缓冲区中,则进行增量同步;
否则,主库将对从库执行全量同步;

# redis.conf 
repl-backlog-size 1mb #环形缓冲区大小
# 如果所有从库断开连接 3600 秒后没有从库连接,则释放环形缓冲区
repl-backlog-ttl 3600

大小确定: disconnect_time * write_size_per_second disconnect_time :从库断线后重连主库所需的平均时间(以秒为单位);
write_size_per_second :主库平均每秒产生的写命令数据量;
在这里插入图片描述
主从数据库连接的整体流程
Redis源码,主从同步与对象模型_第16张图片

三、redis哨兵模式

哨兵模式是Redis可用性的解决方案;它由一个或多个 sentinel 实例构成 sentinel 系统;该系统可以监视任意多个主库以及这些主库所属的从库;当主库处于下线状态,自动将该主库所属的某个从库升级为新的主库;
客户端来连接集群时,会首先连接 sentinel,通过 sentinel 来查询主节点的地址,然后再连接主节点进行数据交互。当主节点发生故障时,客户端会重新向 sentinel 索要主库地址,sentinel 会将最新的主库地址告诉客户端。通过这样客户端无须重启即可自动完成节点切换。
哨兵模式当中涉及多个选举流程采用的是 Raft 算法的领头选举方法的实现;
原理图
哨兵模式由两部分组成,其中一个就是主从,一主二从三哨兵,这三个哨兵会组成一个系统,也就是图中的框框部分,进行选举的,因为主从之间是不能进行选举的,这里使用了一个Raft算法
client需要所有哨兵的地址,ip+port,client首先连接哨兵集群,哨兵集群会返回给我们的主数据库主redis的地址,然后我们的client就会去连接我们的主redis,未来这个主redis宕机了,写数据失败后,又去连接哨兵集群,哨兵集群又会返回给client另一个主redis的地址。哨兵的作用就是选举主redis。
哨兵的连接,每个哨兵之间都有个cmd命令的连接,每个数据库都会和哨兵有个cmd和pud/sub连接
Redis源码,主从同步与对象模型_第17张图片
一致性算法
一个就是paxos还有raft算法(raft必须掌握)
一致性算法解决两类问题,一个是leader选举,还有一个是日志复制。
日志复制是什么意思呢,用来实现数据的强一致性,这里一个主三个从可以用raft一致性算法,让这个数据半数地同步成功了,就可以返回了,这就是raft算法解决的问题,半数同意。
Redis源码,主从同步与对象模型_第18张图片
配置呢用得比较少

# sentinel.cnf 
# sentinel 只需指定检测主节点就行了,通过主节点自动发现从节点 
sentinel monitor mymaster 127.0.0.1 6379 2 
# 判断主观下线时长 
sentinel down-after-milliseconds mymaster 30000 
# 指定可以有多少个Redis服务同步新的主机,一般而言,这个数字越小同步时间越长,而越大,则对网 络资源要求越高 
sentinel parallel-syncs mymaster 1 
# 指定故障切换允许的毫秒数,超过这个时间,就认为故障切换失败,默认为3分钟 
sentinel failover-timeout mymaster 180000

缺点呢就是我们的redis主从复制用的还是那种异步的复制,没有实现强一致性,最后还是实现的最终一致性,所以宕机的时候,我们的主数据库与从数据库还是会有数据不一致的情况。
我们的哨兵是以什么为依据来选举主数据库呢,依据就是偏移值,偏移值越大,谁的数据最新,谁的数据最新我们就选谁作为主节点。
redis 采用异步复制的方式,意味着当主节点挂掉时,从节点可能没有收到全部的同步消息,这部分未同步的消息将丢失。如果主从延迟特别大,那么丢失可能会特别多。sentinel 无法保证消息完全不丢失,但是可以通过配置来尽量保证少丢失。

# 主库必须有一个从节点在进行正常复制,否则主库就停止对外写服务,此时丧失了可用性 
min-slaves-to-write 1 
# 这个参数用来定义什么是正常复制,该参数表示如果在10s内没有收到从库反馈,就意味着从库 同步不正常; 
min-slaves-max-lag 10

分布式中的cap原则

c表示一致性,所有的节点在一个时间完全一致,强一致性(整个系统必须随时保持一致性),最终一致性(redis,mysql)
a表示可用性,写读操作总是成功的,服务一直可用,合理时间内返回合理的值
可用性可以用下图表示,在服务器连接数据库的时候,主数据库宕机导致连接失败,这个时候其他数据库转为主数据库的时候就会连接正常,这就是可用性
那么合理时间内返回合理的值是怎么回事呢,合理时间也就是主从切换到数据库提供响应所用的时间。合理的值就是数据库对服务器的正常返回值
p表示分区容阔性,某个节点或者网络分区故障时仍然能够对外提供一致性或者可用性的服务,优先考虑p,再去考虑c和a
Redis源码,主从同步与对象模型_第19张图片
base理论
基本可用,表示这个分布式系统,响应时间,功能上是否有损失,也就是合理时间内返回合理的值
软状态,同步的时候允许一部分是延时的
最终一致性

Codis集群

这样的代理层是非常重要的一个概念
Redis源码,主从同步与对象模型_第20张图片
codis 由国内团队开发,由于有诸多限制(事务不支持,更新延迟),现在几乎很少有团队使用;
图中可开启多个 codis 实例,client 与 codis 之间通过 redis 协议进行交互;用户可不感知 codis
的存在;

四、Redis cluster集群

我们的分布式定时器就可以用redis cluster集群来实现
Redis cluster 将所有数据划分为 16384(2的14次方 )个槽位,每个 redis 节点负责其中一部分槽位。
这里我们为啥要固定槽位呢,为了确保我们分布式的扩容和缩容,不会造成数据丢失与失效。hash(key)%nodes为了确保删除节点增加节点数据不失效,那么就要固定算法,也就是nodes,nodes = 2的14次方。
hash(key)%2^32,槽位增加这么多怎么确保我们的数据呢,我们就需要再加上一个映射层,槽位我们可以看成一个数组,我们在里边增加节点,比如说三个节点,我们就把2的32次方分成三等份,如果我们增加槽位,就必须使用分布式迁移
Redis源码,主从同步与对象模型_第21张图片

cluster 集群是一种去中心化的集群方式;
如图,该集群由三个 redis 节点组成,每个节点负责整个集群的一部分数据,每个节点负责的数据多少可能不一样。这三个节点相互连接组成一个对等的集群,它们之间通过一种特殊的二进制协议交互集群信息;
当 redis cluster 的客户端来连接集群时,会得到一份集群的槽位配置信息。这样当客户端要查找
某个 key时,可以直接定位到目标节点。
客户端为了可以直接定位(对 key 通过 crc16 进行 hash 再对 2的14次方取余)某个具体的 key 所在节点,需要缓存槽位相关信息,这样才可以准确快速地定位到相应的节点。同时因为可能会存在客户端与服务器存储槽位的信息不一致的情况,还需要纠正机制(通过返回 -MOVED 3999 127.0.0.1:6479 ,客户端收到后需要立即纠正本地的槽位映射表)来实现槽位信息的校验调整。
另外,redis cluster 的每个节点会将集群的配置信息持久化到配置文件中,这就要求确保配置文件是可写的,而且尽量不要依靠人工修改配置文件;
Redis源码,主从同步与对象模型_第22张图片
先建立集群然后分配槽位,这系统就可以工作了,还需要解决客户端访问的问题,比如说set key val操作,我们就需要把这个key映射到这个槽位当中,这个槽位会指向一个具体的地址,又会映射一个真实节点,set key val会映射到那个redis当中。
我们的Redis cluster是一个去中心化的集群
去中心化的意思就是没有中心,可以从任意一个节点出发访问我们整个集群,包括从数据库
我们可以看到这个图里边从数据库之间没有连接,中间有三个主数据库,每个有俩从数据库
通过key去算槽位,根据槽位找真实节点。
Redis源码,主从同步与对象模型_第23张图片我们来操作一下这个,可能就清楚了

集群配置

创建文件夹

# 创建 6 个文件夹 
mkdir -p 7001 7002 7003 7004 7005 7006 
cd 7001 
vi 7001.conf 
# 7001.conf 中的内容如下 

编辑 7001.conf

pidfile "/home/mark/redis-data/7001/7001.pid" 
logfile "/home/mark/redis-data/7001/7001.log" 
dir /home/mark/redis-data/7001/ 
port 7001 
daemonize yes 
cluster-enabled yes #以cluster进行开启的
cluster-config-file nodes-7001.conf 
cluster-node-timeout 15000 

复制配置

cp 7001/7001.conf 7002/7002.conf 
cp 7001/7001.conf 7003/7003.conf 
cp 7001/7001.conf 7004/7004.conf 
cp 7001/7001.conf 7005/7005.conf 
cp 7001/7001.conf 7006/7006.conf

修改配置

sed -i 's/7001/7002/g' 7002/7002.conf 
sed -i 's/7001/7003/g' 7003/7003.conf 
sed -i 's/7001/7004/g' 7004/7004.conf 
sed -i 's/7001/7005/g' 7005/7005.conf 
sed -i 's/7001/7006/g' 7006/7006.conf

创建启动配置

#!/bin/bash redis-server 7001/7001.conf 
redis-server 7002/7002.conf 
redis-server 7003/7003.conf 
redis-server 7004/7004.conf 
redis-server 7005/7005.conf 
redis-server 7006/7006.conf

手动创建集群,手动容易出问题

# 节点会面 
cluster meet ip port 
# 分配槽位 
cluster addslots slot 
# 分配主从 
cluster replicate node-id

智能创建集群

redis-cli --cluster help 
# --cluster-replicas 后面对应的参数 为 一主对应几个从数据库 
redis-cli --cluster create host1:port1 ... hostN:portN --cluster-replicas 
<arg> 
redis-cli --cluster create 127.0.0.1:7001 127.0.0.1:7002 127.0.0.1:7003
127.0.0.1:7004 127.0.0.1:7005 127.0.0.1:7006 --cluster-replicas 1# 利用这条命令创建集群节点的关系

下面就构造齐了主从关系,这里一个主数据带一个从数据库,我们可以看到图中0-5460的槽位对应7001这个节点,这个是由hash算法中对16384进行取余操作落到这个区间内的就去访问对应的节点,比如在0-5460访问7001
Redis源码,主从同步与对象模型_第24张图片

接下来我们就可以测试集群了
设置值
redis-cli -c -p 7004
set name nbsp
我们可以看到这个name对应算出来的hash值是5798,对应的是7002当中,而且下方的连接变成7002了,连接已经发生跳转,从7004到7002
在这里插入图片描述
在这里get name,发现能找到nbsp
在这里插入图片描述
我们到7005,我们发现又重新定位到7002了,返回值是正确的

在这里插入图片描述
这就是去中心化集群,我们原来都只去访问一个主的数据库,写也只能写这一个主,我们现在可以从任意一个节点出发访问一个集群。
那么我们可以在从数据库当中写数据吗,当然不能,7004就是从数据库,都会去重定向映射到主数据库7002。7006是7002的从数据库
我们在7006操作get name,返回值正常且正常跳转到7002
我们实现了一个异步复制,从数据库认为数据不是最新的,要从主数据库拿数据
在这里插入图片描述

主节点宕机
redis-cli -p 7002 shutdown
7002宕机以后,我们再去看看7006,我们可以简单7002已关了
但是master依然是三个,原来的7006替换了7002
Redis源码,主从同步与对象模型_第25张图片然后我们get name也没发生跳转
在这里插入图片描述

主节点重启
redis-server 7002/7002.conf
我们可以看到重启后7002变成slave了,也就是变成7006的从数据库了
Redis源码,主从同步与对象模型_第26张图片

你可能感兴趣的:(笔记,redis)