Redis复制(replica)
就是主从复制,master以写为主,Slave以读为主。当master数据变化的时候,自动将新的数据异步同步到其它slave数据库。
能干嘛:读写分离,容灾恢复,数据备份,水平扩容支撑高并发
怎么玩:
1.配从(库)不配主(库)
2.权限细节
master如果配置了requirepass参数,需要密码登陆,那么slave就要配置masterauth来设置校验密码,否则的话master会拒绝slave的访问请求
基本操作命令:
info replication:可以查看复制节点的主从关系和配置信息。
replicaof 主库IP 主库端口:—般写入进redis.conf配置文件内
slaveof 主库IP 主库端口:
1.每次与master断开之后,都需要重新连接,除非你配置进redis.conf文件
2.在运行期间修改slave节点的信息,如果该数据库已经是某个主数据库的从数据库,那么会停止和原主数据库的同步关系转而和新的主数据库同步,重新拜码头
slaveof no one:使当前数据库停止与其他数据库的同步,转成主数据库,自立为王。
案例演示:
1.架构说明
一个Master两个Slave
拷贝多个redis.conf文件:redis6379.conf,redis6380.conf,redis6381.conf,三边网络相互ping通且注意防火墙配置
三大命令:
1.主动复制:replicaof 主库IP 主库端口,配从(库)不配主(库)
2.改换门庭:slaveof 新主库IP 新主库端口
3.自立为王:slaveof no one
2.修改配置文件细节操作:
redis6379.conf为例,步骤
1.开启daemonize yes
2.注释掉bind 127.0.0.1
3.protected-mode no
4.指定端口
5.指定当前工作目录,dir
6.pid文件名字,pidfile
7.log文件名字,logfile
8.requirepass
9.dump.rdb名字
10.aof文件,appendfilename
11.从机访问主机的通行密码masterauth,必须(从机需要配置,主机不用)
常用3招
1.一主二仆:
方案1:配置文件固定写死
配置文件执行,replicaof 主库IP 主库端口。配从(库)不配主(库)
配置从机6380
配置从机6381
先master后两台slave依次启动
主从关系查看:
日志:
主机日志:
从机日志:
命令:通过info replication命令查看
主从问题演示:
1.从机可以执行写命令吗?
2.从机切入点问题
3.主机shutdown后,从机会上位吗?
4.主机shutdown后,重启后主从关系还在吗?从机还能否顺利复制?
方案2:命令操作手动指定
从机停机去掉配置文件中的配置项,3台目前都是主机状态,各不从属
在预设的从机上执行命令:slaveof 主库IP 主库端口
用命令使用的话,2台从机重启后,关系还在吗?
结论:使用配置,持久稳定,使用命令,当次生效
2.薪火相传:
1.上—个slave可以是下一个slave的master,slave同样可以接收其他slaves的连接和同步请求,那么该slave作为了链条中下一个的master,可以有效减轻主master的写压力
2.中途变更转向:会清除之前的数据,重新建立拷贝最新的
3.slaveof 新主库IP 新主库端口
3.反客为主:
SLAVEOF no one
使当前数据库停止与其他数据库的同步,转成主数据库
复制原理和工作流程
1.slave启动,同步初请:
slave启动成功连接到master后会发送一个sync命令
slave首次全新连接master,一次完全同步(全量复制)将被自动执行,slave自身原有数据会被master数据覆盖清除
2.首次连接,全量复制
master节点收到sync命令后会开始在后台保存快照(即RDB持久化,主从复制时会触发RDB),同时收集所有接收到的用于修改数据集命令缓存起来,master节点执行RDB持久化完后,master将rdb快照文件和所有缓存的命令发送到所有slave,以完成一次完全同步。
而slave服务在接收到数据库文件数据后,将其存盘并加载到内存中,从而完成复制初始化。
3.心跳持续,保持通信
repl-ping-replica-period 10
4.进入平稳,增量复制
Master继续将新的所有收集到的修改命令自动依次传给slave,完成同步
5.从机下线,重连续传
master会检查backlog里面的offset,master和slave都会保存一 个 复制的offset还有一个masterId,
offset是保存在backlog中的。Master只 会把已经复制的offset后面的数据复制给Slave,类似断点续传
复制的缺点
复制延时,信号哀减
由于所有的写操作都是先在Master上操作,然后同步更新到Slave上,所以从Master同步到Slave机器有一定的延迟,当系统很繁忙的时候,延迟问题会更加严重,Slave机器数量的增加也会使这个问题更加严重。
master挂了如何办?
默认情况下,不会在slave节点中自动重选一个master。那每次都要人工干预?无人值守安装变成刚需
Redis哨兵(sentinel)
吹哨人巡查监控后台master主机是否故障,如果故障了根据投票数自动将某一个从库转换为新主库,继续对外服务。俗称,无人值守运维
哨兵的作用:
1、监控redis运行状态,包括master和slave
2、当master down机,能自动将slave切换成新master
能干嘛:
主从监控:
监控主从redis库运行是否正常
消息通知:
哨兵可以将故障转移的结果发送给客户端
故障转移:
如果Master异常,则会进行主从切换,将其中一个Slave作为新Master
配置中心:
客户端通过连接哨兵来获得当前Redis服务的主节点地址
案例演示实战步骤
Redis sentinel架构,前提说明:
3个哨兵:
自动监控和维护集群,不存放数据,只是吹哨人
1主2从:
用于数据读取和存放
案例步骤:
1./myredis目录下新建或者拷贝sentinel.conf文件,名字绝不能错
2.先看看/opt目录下默认的sentinel.conf文件的内容
3.重点参数项说明
bind:服务监听地址,用于客户端连接,默认本机地址
daemonize:是否以后台daemon方式运行
protected-mode:安全保护模式
port:端口
logfile:日志文件路径
pidfile:pid文件路径
dir:工作目录
sentinel monitor < master-name > < ip > < redis-port > < quorum >:设置要监控的master服务器。
quorum表示最少有几个哨兵认可客观下线,同意故障迁移的法定票数。
sentinel auth-pass < master-name > < password >:master设置了密码,连接master服务的密码
sentinel down-after-milliseconds < master-name> < milliseconds>:
指定多少毫秒之后,主节点没有应答哨兵,此时哨兵主观上认为主节点下线
sentinel parallel-syncs < master-name> < nums>:
表示允许并行同步的slave个数,当Master挂了后,哨兵会选出新的Master,此时,剩余的slave会向新的master发起同步数据
sentinel failover-timeout < master-name> < milliseconds>:
故障转移的超时时间,进行故障转移时,如果超过设置的毫秒,表示故障转移失败
sentinel notification-script < master-name> < script-path> :
配置当某一事件发生时所需要执行的脚本
sentinel client-reconfig-script < master-name> < script-path>:
客户端重新配置主节点参数脚本
本次案例哨兵sentinel文件通用配置
由于机器硬件关系,我们的3个哨兵都同时配置进192.168.111.169同一台机器
sentinel26379.conf
sentinel26380.conf
sentinel26381.conf
master主机配置文件说明:
先启动一主二从3个redis实例,测试正常的主从复制
主机6379 redis6379.conf
6380 redis6380.conf
6381 redis6381.conf
3台不同的虚拟机实例,启动三部真实机器实例并连接
redis-cli -a 111111 -p 6379
redis-cli -a 111111 -p 6380
redis-cli -a 111111 -p 6381
再启动3个哨兵,完成监控
redis-sentinel sentinel26379.conf --sentinel
redis-sentinel sentinel26380.conf --sentinel
redis-sentinel sentinel26381.conf --sentinel
启动3个哨兵监控后再测试一次主从复制
原有的master挂了:
1.我们自己手动关闭6379服务器,模拟master挂了
两台从机数据是否OK? 数据OK。两个小问题
6380:
6381:
了解 Broken Pipe:
是否会从剩下的2台机器上选出新的master? 投票新选
sentinel26379.log
sentinel26380.log
sentinel26381.log
谁是master,限本次案例
6381被选为新master,上位成功
以前的6379从master降级变成了slave
6380还是slave,只不过换了个新老大6381(6379变6381),6380还是slave
结论:
1.文件的内容,在运行期间会被sentinel动态进行更改。
2.Master-Slave切换后,master_redis.conf、slave_redis.conf和sentinel.conf的内容都会发生改变,即master_redis.conf中会多一行slaveof的配置,sentinel.conf的监控目标会随之调换
其他备注:
生产都是不同机房不同服务器,很少出现3个哨兵全挂掉的情况。可以同时监控多个master,一行一个
哨兵运行流程和选举原理
当一个主从配置中的master失效之后,sentinel可以选举出一个新的master。用于自动接替原master的工作,主从配置中的其他redis服务器自动指向新的master同步数据。一般建议sentinel采取奇数台,防止某一台sentinel无法连接到master导致误切换。
运行流程,故障切换
1.三个哨兵监控一主二从,正常运行中
2.SDown主观下线(Subjectively Down)
SDOWN(主观不可用)是单个sentinel自己主观上检测到的关于master的状态,从sentinel的角度来看,如果发送了PING心跳后,在一定时间内没有收到合法的回复,就达到了SDOWN的条件。
sentinel配置文件中的down-after-milliseconds设置了判断主观下线的时间长度
所谓主观下线(Subjectively Down, 简称 SDOWN)指的是单个Sentinel实例对服务器做出的下线判断,即单个sentinel认为某个服务下线(有可能是接收不到订阅,之间的网络不通等等原因)。主观下线就是说如果服务器在[sentinel down-after-milliseconds]给定的毫秒数之内没有回应PING命令或者返回一个错误消息, 那么这个Sentinel会主观的(单方面的)认为这个master不可以用了,
sentinel down-after-milliseconds < masterName> < timeout>
表示master被当前sentinel实例认定为失效的间隔时间,这个配置其实就是进行主观下线的一个依据
master在多长时间内一直没有给Sentine返回有效信息,则认定该master主观下线。也就是说如果多久没联系上redis-servevr,认为这个redis-server进入到失效(SDOWN)状态。
3.ODown客观下线(objectively Down)
ODOWN需要一定数量的sentinel,多个哨兵达成一致意见才能认为一个master客观上已经宕掉
四个参数含义:
masterName是对某个master+slave组合的一个区分标识(一套sentinel可以监听多组master+slave这样的组合)
quorum这个参数是进行客观下线的一个依据,法定人数/法定票数
意思是至少有quorum个sentinel认为这个master有故障才会对这个master进行下线以及故障转移。因为有的时候,某个sentinel节点可能因为自身网络原因导致无法连接master,而此时master并没有出现故障,所以这就需要多个sentinel都一致认为该master有问题,才可以进行下一步操作,这就保证了公平性和高可用。
3.选举出领导者哨兵(哨兵中选出兵王)
当主节点被判断客观下线以后,各个哨兵节点会进行协商,先选举出一个领导者哨兵节点(兵王)并由该领导者节点,也即被选举出的兵王进行failover(故障迁移),通过Raft算法选举
sentinel26380.log
由兵王开始推动故障切换流程并选出—个新master
3步骤:
新主登基:某个Slave被选中成为新Master
选出新master的规则,剩余slave节点健康前提下
1.redis.conf文件中,优先级slave-priority或者replica-priority最高的从节点(数字越小优先级越高)
2.复制偏移位置offset最大的从节点
3.最小Run ID的从节点(字典顺序,ASCII码)
群臣俯首: —朝天子—朝臣,换个码头重新拜
1.执行slaveof no one命令让选出来的从节点成为新的主节点,并通过slaveof命令让其他节点成为其从节点
2.Sentinel leader会对选举出的新master执行slaveof no one操作,将其提升为master节点
3.Sentinel leader向其它slave发送命令,让剩余的slave成为新的master节点的slave
旧主拜服: 老master回来也认怂
1.将之前已下线的老master设置为新选出的新master的从节点,当老master重新上线后,它会成为新master的从节点
2.Sentinel leader会让原来的master降级为slave并恢复正常工作。
小总结:
上述的failover操作均由sentinel自己独自完成,完全无需人工干预。
哨兵使用建议
1.哨兵节点的数量应为多个,哨兵本身应该集群,保证高可用
2.哨兵节点的数量应该是奇数
3.各个哨兵节点的配置应一致
4.如果哨兵节点部署在Docker等容器里面,尤其要注意端口的正确映射
5.哨兵集群+主从复制,并不能保证数据零丢失。所以要引入集群
Redis集群(cluster))
由于数据量过大,单个Master复制集难以承担,因此需要对多个复制集进行集群,形成水平扩展每个复制集只负责存储整个数据集的一部分,这就是Redis的集群,其作用是提供在多个Redis节点间共享数据的程序集。
Redis集群是一个提供在多个Redis节点间共享数据的程序集。Redis集群可以支持多个Master。
能干嘛:
Redis集群支持多个Master,每个Master又可以挂载多个Slave。1读写分离。2支持数据的高可用。
3.支持海量数据的读写存储操作
由于Cluster自带Sentinel的故障转移机制,内置了高可用的支持,无需再去使用哨兵功能。
客户端与Redis的节点连接,不再需要连接集群中所有的节点,只需要任意连接集群中的一个可用节点即可。
槽位slot负责分配到各个物理服务节点,由对应的集群来负责维护节点、插槽和数据之间的关系
集群算法-分片-槽位slot
redis集群的槽位slot
redis集群的分片
优势:
slot槽位映射,一般业界有3种解决方案
1.哈希取余分区:
2.一致性哈希算法分区
一致性哈希算法在1997年由麻省理工学院中提出的,设计目标是为了解决分布式缓存数据变动和映射问题,某个机器宕机了,分母数量改变了,自然取余数不OK了。提出一致性Hash解决方案。目的是当服务器个数发生变动时,尽量减少影响客户端到服务器的映射关系
3大步骤:
1)算法构建一致性哈希环
一致性哈希算法必然有个hash函数并按照算法产生hash值,这个算法的所有可能哈希值会构成一个全量集,这个集合可以成为一个hash空间[0,2 ^ 32-1 ],这个是一个线性空间,但是在算法中,我们通过适当的逻辑控制将它首尾相连(0 = 2^32),这样让它逻辑上形成了一个环形空间。
它也是按照使用取模的方法,前面笔记介绍的节点取模法是对节点(服务器)的数量进行取模。而一致性Hash算法是对2 ^ 32取模,简单来说,一致性Hash算法将整个哈希值空间组织成一个虚拟的圆环,如假设某哈希函数H的值空间为0-2 ^ 32-1(即哈希值是一个32位无符号整形),整个哈希环如下图:整个空间按顺时针方向组织,圆环的正上方的点代表0,0点右侧的第一个点代表1,以此类推,2、3、4、……直到2 ^ 32-1,也就是说0点左侧的第一个点代表2 ^ 32-1, 0和2 ^ 32-1在零点中方向重合,我们把这个由2^32个点组成的圆环称为Hash环。
2).redis服务器IP节点映射
将集群中各个IP节点映射到环上的某一个位置。将各个服务器使用Hash进行一个哈希,具体可以选择服务器的IP或主机名作为关键字进行哈希,这样每台机器就能确定其在哈希环上的位置。假如4个节点NodeA、B、C、D,经过IP地址的哈希函数计算(hash(ip)),使用IP地址哈希后在环空间的位置如下:
3.)key落到服务器的落键规则
当我们需要存储一个kv键值对时,首先计算key的hash值,hash(key),将这个key使用相同的函数Hash计算出哈希值并确定此数据在环上的位置,从此位置沿环顺时针“行走”,第一台遇到的服务器就是其应该定位到的服务器,并将该键值对存储在该节点上。如我们有Object A、Object B、Object C、Object D四个数据对象,经过哈希计算后,在环空间上的位置如下:根据一致性Hash算法,数据A会被定为到Node A上,B被定为到Node B上,C被定为到Node C上,D被定为到Node D上。
优点:
容错性
假设Node C宕机,可以看到此时对象A、B、D不会受到影响。一般的,在一致性Hash算法中,如果一台服务器不可用,则受影响的数据仅仅是此服务器到其环空间中前一台服务器(即沿着逆时针方向行走遇到的第一台服务器)之间数据,其它不会受到影响。简单说,就是C挂了,受到影响的只是B、C之间的数据且这些数据会转移到D进行存储。
扩展性
数据量增加了,需要增加一台节点NodeX,X的位置在A和B之间,那收到影响的也就是A到X之间的数据,重新把A到X的数据录入到X上即可,不会导致hash取余全部数据重新洗牌。
缺点:
Hash环的数据倾斜问题
一致性Hash算法在服务节点太少时,容易因为节点分布不均匀而造成数据倾斜(被缓存的对象大部分集中缓存在某一台服务器上)问题,例如系统中只有两台服务器:
总结:
为了在节点数目发生改变时尽可能少的迁移数据
将所有的存储节点排列在收尾相接的Hash环上,每个key在计算Hash后会顺时针找到临近的存储节点存放。而当有节点加入或退出时仅影响该节点在Hash环上顺时针相邻的后续节点。
优点
加入和删除节点只影响哈希环中顺时针方向的相邻的节点,对其他节点无影响。
缺点
数据的分布和节点的位置有关,因为这些节点不是均匀的分布在哈希环上的,所以数据在进行存储时达不到均匀分布的效果。
哈希槽分区
HASH_SLOT = CRC16(key) mod 16384
Redis 集群中内置了 16384 个哈希槽,redis 会根据节点数量大致均等的将哈希槽映射到不同的节点。当需要在 Redis 集群中放置一个 key-value时,redis先对key使用crc16算法算出一个结果然后用结果对16384求余数[ CRC16(key) % 16384],这样每个 key 都会对应一个编号在 0-16383 之间的哈希槽,也就是映射到某个节点上。如下代码,key之A 、B在Node2, key之C落在Node3上
经典面试题
为什么redis集群的最大槽数是16384个?
Redis集群并没有使用一致性hash而是引入了哈希槽的概念。Redis 集群有16384个哈希槽,每个key通过CRC16校验后对16384取模来决定放置哪个槽,集群的每个节点负责一部分hash槽。但为什么哈希槽的数量是16384(2^14)个呢?
CRC16算法产生的hash值有16bit,该算法可以产生2^16=65536个值。
换句话说值是分布在0~65535之间,有更大的65536不用为什么只用16384就够?
作者在做mod运算的时候,为什么不mod65536,而选择mod16384? HASH_SLOT = CRC16(key) mod 65536为什么没启用
github
正常的心跳数据包带有节点的完整配置,可以用幂等方式用旧的节点替换旧节点,以便更新旧的配置。
这意味着它们包含原始节点的插槽配置,该节点使用2k的空间和16k的插槽,但是会使用8k的空间(使用65k的插槽)。
同时,由于其他设计折衷,Redis集群不太可能扩展到1000个以上的主节点。
因此16k处于正确的范围内,以确保每个主机具有足够的插槽,最多可容纳1000个矩阵,但数量足够少,可以轻松地将插槽配置作为原始位图传播。请注意,在小型群集中,位图将难以压缩,因为当N较小时,位图将设置的slot / N位占设置位的很大百分比。
(1)如果槽位为65536,发送心跳信息的消息头达8k,发送的心跳包过于庞大。
在消息头中最占空间的是myslots[CLUSTER_SLOTS/8]。 当槽位为65536时,这块的大小是: 65536÷8÷1024=8kb
在消息头中最占空间的是myslots[CLUSTER_SLOTS/8]。 当槽位为16384时,这块的大小是: 16384÷8÷1024=2kb
因为每秒钟,redis节点需要发送一定数量的ping消息作为心跳包,如果槽位为65536,这个ping消息的消息头太大了,浪费带宽。
(2)redis的集群主节点数量基本不可能超过1000个。
集群节点越多,心跳包的消息体内携带的数据越多。如果节点过1000个,也会导致网络拥堵。因此redis作者不建议redis cluster节点数量超过1000个。 那么,对于节点数在1000以内的redis cluster集群,16384个槽位够用了。没有必要拓展到65536个。
(3)槽位越小,节点少的情况下,压缩比高,容易传输
Redis主节点的配置信息中它所负责的哈希槽是通过一张bitmap的形式来保存的,在传输过程中会对bitmap进行压缩,但是如果bitmap的填充率slots / N很高的话(N表示节点数),bitmap的压缩率就很低。 如果节点数很少,而哈希槽数量很多的话,bitmap的压缩率就很低。
计算结论:
说明:
Redis集群不保证强一致性,这意味着在特定的条件下,Redis集群可能会丢掉一些被系统收到的写入请求命令
集群环境案例步骤
1.3主3从redis集群配置
找3台真实虚拟机,各自新建:mkdir -p /myredis/cluster
新建6个独立的redis实例服务:
IP: 192.168.111.175+端口6381/端口6382
vim /myredis/cluster/redisCluster6381.conf
vim /myredis/cluster/redisCluster6382.conf
IP: 192.168.111.172+端口6383/端口6384
vim /myredis/cluster/redisCluster6383.conf
vim /myredis/cluster/redisCluster6384.conf
IP: 192.168.111.174+端口6385/端口6386
vim /myredis/cluster/redisCluster6385.conf
vim /myredis/cluster/redisCluster6386.conf
启动6台redis主机实例
redis-server /myredis/cluster/redisCluster6381.conf
。。。。。
redis-server /myredis/cluster/redisCluster6386.conf
通过redis-cli命令为6台机器构建集群关系:
构建主从关系命令
链接进入6381作为切入点,查看并检验集群状态
2.3主3从redis集群读写
对6381新增两个key,看看效果如何
为什么报错
如何解决?防止路由失效加参数-c并新增两个key
查看集群信息
查看某个key该属于对应的槽位值CLUSTER KEYSLOT键名称
3.主从容错切换迁移案例
容错切换迁移:
主6381和从机切换,先停止主机6381:6381主机停了,对应的真实从机上位,6381作为1号主机分配的从机以实际情况为准,具体是几号机器就是几号
再次查看集群信息,本次6381主6384从:
停止主机6381,再次查看集群信息
6384成功上位并正常使用
随后,6381原来的主机回来了,是否会上位?
恢复前:
恢复后:
集群不保证数据一致性100%OK,一定会有数据丢失情况,Redis集群不保证强─致性,这意味着在特定的条件下,Redis集群可能会丢掉一些被系统收到的写入请求命令
4.主从扩容案例
新建6387、6388两个服务实例配置文件+新建后启动,lP: 192.168.111.174+端口6387/端口6388,
vim /myredis/cluster/redisCluster6387.conf
vim /myredis/cluster/redisCluster6388.conf
启动87/88两个新的节点实例,此时他们自己都是master
将新增的6387节点(空槽号)作为master节点加入原集群
检查集群情况第一次
重新分派槽号(reshard)
检查集群情况第二次
槽号分派说明
为主节点6387分配从节点6388
检查集群情况第3次
5.主从缩容案例
目的:6387和6388下线
检查集群情况第一次,先获得从节点6388的节点ID
从集群中将4号从节点6388删除
将6387的槽号清空,重新分配,本例将清出来的槽号都给6381
检查集群情况第二次
将6387删除
检查集群情况第三次,6387/6388被彻底祛除自
集群常用操作命令和CRC16算法分析
不在同一个slot槽位下的多键操作支持不好,通识占位符登场
Redis集群有16384个哈希槽,每个key通过CRC16校验后对16384取模来决定放置哪个槽。集群的每个节点负责一部分hash槽。CRC16源码浅谈
常用命令:
集群是否完整才能对外提供服务:cluster-require-full-coverage
CLUSTER COUNTKEYSINSLOT槽位数字编号:1,该槽位被占用,0,该槽位没占用
CLUSTER KEYSLOT 键名称 该键应该存在哪个槽位上