下载地址:http://redis.io/download
安装步骤:
# 安装gcc
yum install gcc
# 把下载好的redis-5.0.3.tar.gz放在/usr/local文件夹下(可以使用Xftp传输)
wget http://download.redis.io/releases/redis-5.0.3.tar.gz
# 解压
tar xzf redis-5.0.3.tar.gz
cd redis-5.0.3
# 进入到解压好的redis-5.0.3目录下,进行编译与安装
make
# 修改配置
daemonize yes #后台启动
protected-mode no #关闭保护模式,开启的话,只有本机才可以访问redis
# 需要注释掉bind,如果开启只能通过自己服务器连接redis,多个redis之间无法连接,无法通信
#bind 127.0.0.1(bind绑定的是自己机器网卡的ip,如果有多块网卡可以配多个ip,代表允许客户端通过机器的哪些网卡ip去访问,内网一般可以不配置bind,注释掉即可)
protected-mode no #设置Redis可远程访问
# 启动服务
src/redis-server redis.conf
# 验证启动是否成功
ps -ef | grep redis
# 进入redis客户端
src/redis-cli
# 退出客户端
quit
# 退出redis服务:
(1)pkill redis-server
(2)kill 进程号
(3)src/redis-cli shutdown
如果使用make命令编译redis失败,出现
“没有名为‘xxx’的成员”
“警告:在有返回值的函数中,控制流程到达函数尾”等情况时,可能是gcc版本过低,具体请看此链接
解决make编译redis失败的方式
redis主从架构可以配置一主一从、一主多从等结构,主要是从主节点读写数据,从节点多用于备份数据!
1、复制一份redis.conf文件
2、将相关配置修改为如下值:
port 6380
pidfile /var/run/redis_6380.pid # 把pid进程号写入pidfile配置的文件
logfile "6380.log"
dir /usr/local/redis-5.0.3/data/6380 # 指定数据存放目录
# 需要注释掉bind
# bind 127.0.0.1(bind绑定的是自己机器网卡的ip,如果有多块网卡可以配多个ip,代表允许客户端通过机器的哪些网卡ip去访问,内网一般可以不配置bind,注释掉即可)
3、配置主从复制
replicaof 192.168.0.60 6379 # 从本机6379的redis实例复制数据,Redis 5.0之前使用slaveof
replica-read-only yes # 配置从节点只读
4、启动从节点
redis-server redis.conf
5、连接从节点
redis-cli -p 6380
6、测试在6379实例上写数据,6380实例是否能及时同步新修改数据
如果为reids配置了主从架构,那么从服务器启动时是怎么从主服务器中同步数据的呢?具体的流程图如下:
注意: 如果master收到了多个slave并发连接请求,它只会进行一次持久化,而不是一个连接一次,然后再把这一份持久化的数据发送给多个并发连接的slave。
当salve因为网络等原因接收到一半数据时挂掉了,经过一段时间后,人为的重启了salve从节点,那么此时的数据传输是怎么处理呢?
答:从redis2.8版本开始,redis改用可以支持部分数据复制的命令PSYNC去master同步数据,slave与master能够在网络连接断开重连后只进行部分数据复制(断点续传)。流程图如下:
# repl-backlog-size 1mb redis命令缓存池,默认大小为1Mb
主从复制风暴:多个从节点同时复制主节点导致主节点压力过大
为了解决主从复制风暴问题,可以让部分从节点与从节点同步数据,架构如下设计:
sentinel哨兵是特殊的redis服务,不提供读写服务,主要用来监控redis实例节点的状态!
哨兵架构下client端第一次请求redis服务时,会通过哨兵找出redis的主节点,后续就直接访问redis的主节点,不会每次都通过sentinel代理访问redis的主节点,当redis的主节点发生变化,哨兵会第一时间感知到,并且将新的redis主节点通知给client端(这里面redis的client端一般都实现了订阅功能,订阅sentinel发布的节点变动消息)
1、复制一份sentinel.conf文件
cp sentinel.conf sentinel-26379.conf
2、将相关配置修改为如下值:
port 26379
daemonize yes
pidfile "/var/run/redis-sentinel-26379.pid"
logfile "26379.log"
dir /usr/local/redis-5.0.3/data
# sentinel monitor <master-redis-name> <master-redis-ip> <master-redis-port> <quorum>
# quorum是一个数字,指明当有多少个sentinel认为一个master失效时(值一般为:sentinel总数/2 + 1),master才算真正失效
# 推荐哨兵设为最少3个
# mymaster这个名字随便取,客户端访问时会用到
sentinel monitor mymaster 192.168.0.60 6379 2
3、启动sentinel哨兵实例
src/redis-sentinel sentinel-26379.conf
4、查看sentinel的info信息
src/redis-cli -p 26379
127.0.0.1:26379>info
可以看到Sentinel的info里已经识别出了redis的主从
5、可以自己再配置两个sentinel,端口26380和26381
注意:每个哨兵sentinel中的唯一标志myid一定不能一样,如果一样则只代表一个哨兵。删掉启动时会自动生成
创建完成后可以进入监听的master客户端,使用info命令查看哨兵、从服务器配置情况
Info
:查看redis服务运行信息,分为 9 大块,每个块都有非常多的参数,这 9 块分别是:
Server 服务器运行的环境参数
Clients 客户端相关信息
Memory 服务器运行内存统计数据
Persistence 持久化信息
Stats 通用统计数据
Replication 主从复制相关信息
CPU CPU 使用情况
Cluster 集群信息
KeySpace 键值对统计数量信息
当哨兵启动完成,会往sentinel-xxx.conf配置文件的底部增添一些master的从节点信息,如下所示,如果有这些说明哨兵搭建成功!
现象: 当master节点挂掉后,服务端控制台会打印 连接超时错误,当过一段时间后,又恢复正常,可以继续向redis中写入数据!
原因: 原因就是哨兵会时刻监视着master节点,当master节点挂掉,此时服务端控制台会打印连接超时错误。但同时哨兵经过半数机制确认master挂掉,会选举出一个slave作为新的master(默认3分钟),选举的这顿时间内,控制台还是会打印错误,一旦选举成功,就会通知client并恢复正常连接,这也是出现以上现象的原因!!当原来挂掉的master节点重新恢复时,将自动称为新的master的丛节点,完成哨兵高可用架构!
注意: 当master节点挂掉,哨兵选举新节点的这个时间内,整个redis架构会挂掉,无法对外提供服务!
当一个master服务器被某sentinel视为下线状态后,哨兵会选举出一个新的master,大概流程如下:
为了高可用一般都推荐至少部署三个哨兵节点,且节点数为基数,这样做的原理和redis集群类似
在redis3.0以前的版本要实现集群一般是借助哨兵sentinel来监控master节点的状态,如果master节点异常,则会做主从切换,将某一台slave作为master。这种方式足以应对一般的QPS,但在面对很高并发的场景时,也会存在以下几个缺点:
总之,在高并发场景下哨兵架构的配置略微复杂,并且性能和高可用性等各方面表现一般。为了应对这个问题,redis3.0以后引入了redis集群架构,redis集群架构如下:
redis集群是一个由多个主从节点群 组成的分布式服务器群,它具有复制、高可用和数据分片存储特性。redis集群包含哨兵的功能,所以不需要配置哨兵也可以完成半数选举新节点的功能!
redis集群在处理数据时是分片存储 且 没有中心节点,故可水平扩展,可根据实际场景(双11和平时)进行服务器动态扩容缩容,官方推荐不超过1000个节点。redis集群的性能和高可用性均优于之前版本的哨兵模式,且集群配置非常简单!
集群搭建步骤如下,可以单机搭建,也可多台服务器搭建
第一步:在第一台机器的/usr/local下创建文件夹redis-cluster,然后在其下面分别创建2个文件夾如下
(1)mkdir -p /usr/local/redis-cluster
(2)mkdir 8001 8004
第一步:把之前的redis.conf配置文件copy到8001下,修改如下内容:
(1)daemonize yes
(2)port 8001(分别对每个机器的端口号进行设置)
(3)pidfile /var/run/redis_8001.pid # 把pid进程号写入pidfile配置的文件
(4)dir /usr/local/redis-cluster/8001/(指定数据文件存放位置,必须要指定不同的目录位置,不然会丢失数据)
//5、6、7步是针对集群的操作
(5)cluster-enabled yes(启动集群模式)
(6)cluster-config-file nodes-8001.conf(集群节点信息文件,相当于cluster nodes命令查看节点信息,这里800x最好和port对应上)
(7)cluster-node-timeout 10000
(8)# bind 127.0.0.1(bind绑定的是自己机器网卡的ip,如果有多块网卡可以配多个ip,代表允许客户端通过机器的哪些网卡ip去访问,内网一般可以不配置bind,注释掉即可)
(9)protected-mode no (关闭保护模式)
(10)appendonly yes (开启Aof持久化)
如果要设置密码需要增加如下配置:
(11)requirepass zhuge (设置redis访问密码)
(12)masterauth zhuge (设置集群节点间访问密码,跟上面一致)
第三步:把修改后的配置文件,copy到8004,修改第2、3、4、6项里的端口号,可以用批量替换:
:%s/源字符串/目的字符串/g
第四步:另外两台机器也需要做上面几步操作,第二台机器用8002和8005,第三台机器用8003和8006
第五步:分别启动6个redis实例,然后检查是否启动成功
(1)/usr/local/redis-5.0.3/src/redis-server /usr/local/redis-cluster/800*/redis.conf
(2)ps -ef | grep redis 查看是否启动成功
第六步:用redis-cli创建整个redis集群(redis5以前的版本集群是依靠ruby脚本redis-trib.rb实现)
# 下面命令里的1代表为每个创建的主服务器节点创建一个从服务器节点
# 执行这条命令需要确认三台机器之间的redis实例要能相互访问,
# 可以先简单把所有机器防火墙关掉,如果不关闭防火墙则需要打开redis服务端口和集群节点gossip通信端口16379(默认是在redis端口号上加1W)
# 关闭防火墙
# systemctl stop firewalld # 临时关闭防火墙
# systemctl disable firewalld # 禁止开机启动
(1)/usr/local/redis-5.0.3/src/redis-cli -a zhuge
--cluster create --cluster-replicas 1 192.168.0.61:8001 192.168.0.62:8002 192.168.0.63:8003 192.168.0.61:8004 192.168.0.62:8005 192.168.0.63:8006
第七步:验证集群:
(1)连接任意一个客户端即可:./redis-cli -c -h -p (-a访问服务端密码,-c表示集群模式,指定ip地址和端口号)
如:/usr/local/redis-5.0.3/src/redis-cli -a zhuge -c -h 192.168.0.61 -p 800*
(2)进行验证: cluster info(查看集群信息)、cluster nodes(查看节点列表)
(3)进行数据操作验证
(4)关闭集群则需要逐个进行关闭,使用命令:
/usr/local/redis-5.0.3/src/redis-cli -a zhuge -c -h 192.168.0.60 -p 800* shutdown
问题一: 如何创建集群?
在这里采用一台机器部署redis集群的方式,分别启动8001、8002、8003、8004、8005、8006端口的redis后,结果如下:
此时要组成集群的6个redis节点已启动成功,但他们之间还没有任何关系,通过客户端命令可以为他们组建集群关系,下面的组建集群关系命令只在redis5.0以后有用,redis5.0之前的是依靠ruby脚本redis-trib.rb实现的!!
# --cluster create 创建集群关系
# --cluster-replicas 1 每一个集群主节点配置一个从节点
redis-6.0.9/src/redis-cli --cluster create --cluster-replicas 1 192.168.100.100:8001 192.168.100.100:8002 192.168.100.100:8003 192.168.100.100:8004 192.168.100.100:8005 192.168.100.100:8006
输入命令后,会为每个集群节点分配槽位!那为什么会有槽位分配呢?
因为redis集群的数据是分片(分槽位slot)来存储的,所以每个集群节点在建立集群关系时,会分到不同区间的槽位,一共16384个槽位供数据存储!
在集群模式下,当操作redis客户端存储数据时,数据的key会与16384进行%运算,运算结果决定存储在哪个槽位上,这个槽位又被分配在redis集群中的某个master节点上,所以要存储的数据也就存储在这个master节点上!
分配槽位详情如下:
然后确定槽位分配,集群关系组建成功的标志如下:
连接任意一个客户端进行验证:
# -c 代表集群模式
src/redis-cli -c -h 192.168.100.100 -p 8001
进入8001的客户端,在客户端中使用cluster info命令查看集群信息
在客户端中使用cluster nodes命令查看集群信息如下:
三主三从,每一台服务器对应一个唯一id,通过观察从服务器依赖的id,可以知道从服务器依赖于哪一台master!
问题二:集群关闭后,如何重新启动?
如果要重启集群,千万不要使用上边的创建集群命令!!只需要把之前已经搭建好的6个集群节点使用 redis-server config 的方式逐一启动即可! 启动完成后,他们会自动建立集群关系!
因为我们之前在节点的配置文件中配置了节点信息存储目录,节点启动时会自动去该目录下找到并根据该配置建立集群关系
cluster-config-file nodes-8001.conf(集群节点信息文件,相当于cluster nodes命令查看节点信息,这里800x最好和port对应上)
8001的节点信息nodes-8001.conf配置如下,可以看到和上边的cluster nodes命令结果一样!
问题三:如果集群中的某个master节点挂掉,整个集群还可用吗?
redis集群默认一个master节点挂掉,整个集群不可用,但是可以修改配置文件使集群可用
注意:
# 为no表示当负责一个插槽的主库下线且没有相应的从库进行故障恢复时,集群仍然可用
# 为yes则不可用
cluster-require-full-coverage = no
问题四: redis集群为什么至少需要三个master节点,并且推荐节点数为奇数?
因为新master的选举需要大于半数的集群master节点同意才能选举成功,如果只有两个master节点,当其中一个挂了,是达不到选举新master的条件的。
奇数个master节点可以在满足选举该条件的基础上节省一个节点,比如三个master节点和四个master节点的集群相比,大家如果都挂了一个master节点都能选举新master节点,如果都挂了两个master节点都没法选举新master节点了(因为选举需要大于2票,就剩余两个master,最大=2),所以奇数的master节点更多的是从节省机器资源角度出发说的。
问题五:redis集群支持批量操作命令吗?
如果直接使用mset,mget这样的多个key的原生批量操作命令,会执行失败,因为redis集群只支持所有key落在同一槽位slot的情况,多个key可能落在不同的槽位slot中!
如果有多个key一定要用mset命令在redis集群上操作,则可以在key的前面加上{XX},这样参数数据分片hash计算的只会是大括号里的值,这样能确保不同的key能落到同一slot里去,示例如下:
mset {user1}:1:name zhuge {user1}:1:age 18
假设name和age计算的hash slot值不一样,但是这条命令在集群下执行,redis只会用大括号里的 user1 做hash slot计算,所以算出来的slot值肯定相同,最后都能落在同一槽位slot中!
Redis Cluster 将所有数据划分为 16384 个 slots(槽位),每个节点负责其中一部分槽位。槽位的信息存储于每个节点中。在Springboot项目中,当 Redis Cluster 的客户端(jedis、redistemplate等) 来连接集群时,它也会得到一份集群的槽位配置信息并将其缓存在本地。这样当客户端要查找某个 key 时,通过槽位定位算法可以直接定位到目标节点。同时因为槽位的信息可能会存在客户端与服务器不一致的情况(比如集群数据迁移、集群扩容缩容等),还需要纠正机制来实现槽位信息的校验调整。
槽位定位算法
redis集群默认会对 key 值使用 crc16 算法进行 hash 得到一个整数值,然后用这个整数值对 16384 进行取模来得到具体槽位。核心算法如下:HASH_SLOT = CRC16(key) mod 16384
跳转重定位
当时用redis客户端命令set向redis存储k-v值时
redis集群中一个节点挂掉后,别的节点可以及时感知到,并选举出新的节点,这其中就依赖于节点之间的通信机制!维护集群的元数据通信方式大概有两种:集中式和gossip 。redis cluster节点间采取gossip协议进行通信! 两种通信方式介绍如下:
集中式:
zookeeper就是借助集中式来哦存储数据信息!
gossip:
每个节点都有一个专门用于节点间gossip通信的端口,就是自己提供服务的端口号+10000,比如7001,那么用于节点间通信的就是17001端口。
gossip协议包含多种消息,包括ping,pong,meet,fail等等。
通信超时时间设置
机房网络往往并不是风平浪静的,比如网络波动就是非常常见的一种现象,突然之间部分连接变得不可访问,然后很快又恢复正常。如果集群节点的通信因为网络波动而认为有节点挂掉,进而触发选举机制,那么网络波动结束后,旧的master节点恢复了连接,然而又选举出了一个新的master节点!两个master节点同时对外提供服务,就会出现脑裂问题。
为了防止因网络波动产生脑裂,Redis Cluster 提供了一种选项cluster-node-timeout 设置连接超时时间,表示当某个节点持续 timeout 的时间失联时,才可以认定该节点出现故障,需要进行主从切换。如果没有这个选项,网络抖动会导致主从频繁切换 (数据的重新复制)。
当slave发现自己的master变为FAIL状态时,并不是马上尝试发起选举,而是有一定延迟,一定的延迟确保我们等待FAIL状态在集群中传播完毕,slave如果立即尝试选举,其它masters或许尚未意识到FAIL状态,可能会拒绝投票。延迟之后slave便尝试成为新的master。
延迟计算公式:
DELAY = 500ms + random(0 ~ 500ms) + SLAVE_RANK * 1000ms
;
说明:SLAVE_RANK
表示此slave已经从master复制数据的总量的rank。Rank越小代表已复制的数据越新。这种方式下,持有最新数据的slave将会首先发起选举(理论上)。
由于挂掉的master可能会有多个slave,从而存在多个slave竞争成为master节点的过程, 其过程如下:
如果多个slave第一次竞选票数一样,则会把集群选举次数currentEpoch加1,再次执行上面的步骤进行选举!
redis的脑裂问题在哨兵模式和集群模式都可能存在:
哨兵模式的脑裂:
如上图,1个master
与3个slave
组成的哨兵模式(哨兵独立部署于其它机器),刚开始时,2个应用服务器server1、server2都连接在master上,视为正常情况。
如果master
与slave
及哨兵sentinel
之间的网络发生故障,但是哨兵与slave之间
通讯正常,这时3个slave
其中1个经过哨兵投票后,提升为新master
,如果恰好此时server1
仍然连接的是旧的master,而server2
连接到了新的master上。数据就不一致了,基于setNX
指令的分布式锁,可能会拿到相同的锁;基于incr
生成的全局唯一id,也可能出现重复。
集群模式的脑裂:
上文说到为防止网络波动产生脑裂问题,可以配置cluster-node-timeout 设置连接超时时间来预防。但是当连接时间超出cluster-node-timeout设置的值,还是会选举出新的master节点,此时旧的master节点如果恢复连接,还是会因为存在多个master节点对外提供服务而产生脑裂!!
生产环境下,由于redis master节点和redis salve节点处于不同的网络分区,当网络分区恢复(ping通)之前,有两个master
节点对外提供服务,在提供服务期间:可能会存在数据不一致问题:
setNX
指令的分布式锁,可能会拿到相同的锁incr
指令生成的全局唯一id,也可能出现重复当网络分区恢复之后,redis会把旧的master节点变为新的master节点的slave节点,并同步新的master节点的数据。但同时会带来一个新的问题,旧节点上的数据会被删除,如果它在作为master对外提供服务的时间内,处理了很多命令,那么被删除的话就会丢失一部分数据!具体过程如下图所示:
正常状态:
网络发生中断:如果此时master服务器所在区域网络通信出现异常,导致和两台slave机器无法正常通信,但是和客户端的连接是正常的。那么sentinel就会从两台slave机器中选举其中一个作为新的master来处理客户端请求。如图
网络恢复后: 旧master节点变为新master节点的从节点,旧master上的数据会被删除。在网络发生中断期间,旧master从客户端接收到的数据全部丢失!
脑裂问题的解决方案:
可以在redis配置里加上参数(这种方法不可能百分百避免数据丢失):
# 写数据成功最少同步的slave数量
# 这个数量可以模仿大于半数机制配置,比如集群总共三个节点可以配置1,加上leader就是2,超过了半数
min-slaves-to-write 1
# 主从数据同步超时时间,10秒。
min-slaves-max-lag 10
根据以上配置可以将master通信异常期间的数据丢失控制在10秒以内,但同时在一定程度上会影响集群的可用性,比如slave要是少于1个,这个集群就算leader正常也不能提供服务了,需要具体场景权衡选择。