持久化
RDB : Redis database
默认持久化方式,将内存数据隔固定时长存储到窗磁盘文件
Redis默认的方式,redis通过快照来将数据持久化到磁盘中。
- 持久化文件存储的目录
在redis.conf中可以指定持久化文件存储的目录默认dum.rdb
AOF: append only file
是将每次写操作存储到aof持久化文件中,默认是关闭的,仍然会有可能会丢失数据(os也会有缓存),强制每次执行
Aof方式的持久化,是操作一次redis数据库,则将操作的记录存储到aof持久化文件中。
第一步:开启aof方式的持久化方案
将redis.conf中的appendonly改为yes,即开启aof方式的持久化方案。
启动: 672行 ;appendonly yes
Aof文件存储的目录和rdb方式的一样。
Aof文件存储的名称”appendfilename "apendonly.aof"
结论
在使用aof和rdb方式时,如果redis重启,则数据从aof文件加载。
Redis的主从复制
1.1 什么是主从复制
持久化保证了即使redis服务重启也不会丢失数据,因为redis服务重启后会将硬盘上持久化的数据恢复到内存中,但是当redis服务器的硬盘损坏了可能会导致数据丢失,如果通过redis的主从复制机制就可以避免这种单点故障,如下图:
说明:
主redis中的数据有两个副本(replication)即从redis1和从redis2,即使一台redis服务器宕机其它两台redis服务也可以继续提供服务。
主redis中的数据和从redis上的数据保持实时同步,当主redis写入数据时通过主从复制机制会复制到两个从redis服务上。
只有一个主redis,可以有多个从redis。
主从复制不会阻塞master,在同步数据时,master 可以继续处理client 请求
一个redis可以即是主又是从,如下图:
主从复制设置
主机配置
无需配置
从机配置
复制出一个主机
cp -r 主机 从机
修该从机的redis.conf
查找slaverof
添加配置信息:语法:slaveof masterip masterport
slaveof 192.168.2.101 6379
修改从机的port为6380
在redis.conf文件中修改
搜索修改即可
清除主机中的持久化文件 *.rdb;* aof
根据从机的配置文件启动从机
./redis-server redis.conf
启动6380的客户端
./redis-cli -h 192.168.2.101 -p 6380
注意
主机一旦发生增删改操作,那么从机会将数据同步到从机中
从机不能执行写操作
set s2 2221 2
| 127.0.0.1:6380> set s2 222 (error) READONLY You can't write against a read only slave.
|
Redis集群
redis-cluster架构图
1 2 3 4 5 6 7 8 9 10
| 架构细节
(1) 所有的redis节点彼此互联(PING-PONG机制),内部使用二进制协议优化传输速度和带宽.
(2)节点的fail是通过集群中 超过半数的节点检测 失效时才生效.
(3)客户端与redis节点直连,不需要中间proxy层.客户端不需要连接集群所有节点,连接集群中任何一个可用节点即可
(4)redis-cluster把所有的物理节点映射到[0-16383]slot上,cluster 负责维护node<->slot<->value
|
Redis 集群中内置了 16384 个哈希槽,当需要在 Redis 集群中放置一个 key-value 时,redis 先对 key 使用 crc16 算法算出一个结果,然后把结果对 16384 求余数,这样每个 key 都会对应一个编号在 0-16383 之间的哈希槽,redis 会根据节点数量大致均等的将哈希槽映射到不同的节点
redis-cluseter master宕机后重新选举的过程
redis-cluster不可用情况
1、集群主库半数宕机
2、集群某个节点的主从全数宕机
当某个master挂掉后,在cluster集群仍然可用的前提夏,由于某个master有多个slave,某个slave提升为master,这个过程称为选举。
currentEpoch 这是一个集群状态相关的概念,可以当作记录集群状态变更的递增版本号。每个集群节点,都会通过server.cluster->currentEpoch记录当前的currentEpoch。
集群节点创建时,不管是master还是slave,都置currentEpoch为0.当前节点接收到来自其他节点的包时,如果发送者的currentEpoch大于当前节点会更新currentEpoch为发送者的currentEpoch。因此,集群的所有节点从currentEpoch最终会达成一致,相当于对集群状态的认知达成了一致。
过程如下
1、slave发现自己的master变为FAIL
2、发起选举前,slave先给自己的epoch加1,然后请求其它master给自己投票。slave是通过广播FAILOVER_AUTH_REQUEST包给集中的每个masters。
3、slave发起投票后,会等待至少两倍NODE_TIMEOUT时长接收自己投票结果,不管NODE_TIMEOUT何值,也至少会等待2秒。
4、master接收投票后给slave响应FAILOVER_AUTH_ACK,并且在NODE_TIMEOUT*2 时间内不会给同一个master的其他slave投票。
5、如果slave收到FAILOVER_AUTH_ACK响应的epoch值小于自己的epoch,则会直接丢弃。以但slave收到多数master的FAILOVER_AUTH_ACK则声明自己赢得选举。
6、如果slave在两倍的NODE_TIMEOUT时间内至少2秒未赢得选举,则放弃本次选举,然后4倍NODE_TIMEOUT时间发起再次选举
之所以强制延迟至少0.5秒选举,是为确保master的fail状态在整个集群内传开,否则可能只有小部分master知晓,而master只会给处于fail状态的master的slaves投票。
如果一个slave的master状态不是fail,则其他的master不会给它投票,redis通过八卦协议传播fail。而在固定延迟上再加一个随机延迟是为了多高slaves同时发起选举。
延迟计算公式:
DELAY = 500 + RANDOM(0~500)+SLAVE_RANK*1000ms
SLAVE_RANK表示此slave已经从master复制的数据越新。这种方式下,持有最新数据的slave将会首先发起选举。
redis-cluste 投票 :容错
1 2 3 4 5 6 7
| (1)集群中所有master参与投票,如果半数以上master节点与其中一个master节点通信超过(cluster-node-timeout),认为该master节点挂掉.
(2):什么时候整个集群不可用(cluster_state:fail)? 1. 如果集群任意master挂掉,且当前master没有slave,则集群进入fail状态。也可以理解成集群的[0-16383]slot映射不完全时进入fail状态。
2. 如果集群超过半数以上master挂掉,无论是否有slave,集群进入fail状态。
|
安装ruby
集群管理工具:(redis-trib.rb)是使用ruby脚本语言编写的。
第一步:安装ruby
1 2 3
| [root@hdp01 bin2]# yum install ruby
[root@hdp01 bin2]# yum install rubygems
|
第二步:将以下文件上传到linux系统
redis-3.0.0.gem
第三步,安装ruby和redis接口
[root@hdp01 ~]# gem install redis-3.0.0.gem
第四步:将redis-3.0.0包下src目录中的以下文件拷贝到redis19/redis-cluster/
1 2 3 4
| [root@hdp01 src]# cd /usr/local/redis/ [root@hdp01 redis19]# mkdir redis-cluster [root@hdp01 redis19]# cd /root/redis-3.0.0/src/ [root@hdp01 src]# cp redis-trib.rb /usr/local/redis1/redis-cluster
|
搭建集群
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85
| 搭建集群最少也得需要3台主机,如果每台主机再配置一台从机的话,则最少需要6台机器。
端口设计如下:6380-6385
第一步:复制出一个7001机器 [root@hdp01 redis]# cp bin ./redis-cluster/7001 –r
第二步:如果存在持久化文件,则删除 [root@hdp01 7001]# rm -rf appendonly.aof dump.rdb
第三步:设置集群参数 cluster-enabled yes
第四步:修改端口 port 6380
第五步:复制出7002-7006机器 [root@hdp01 redis-cluster]# cp 7001/ 7002 -r [root@hdp01 redis-cluster]# cp 7001/ 7003 -r [root@hdp01 redis-cluster]# cp 7001/ 7004 -r [root@hdp01 redis-cluster]# cp 7001/ 7005 -r [root@hdp01 redis-cluster]# cp 7001/ 7006 –r
第六步:修改7002-7006机器的端口
第七步:启动7001-7006这六台机器
第八步:修改start-all.sh文件的权限 [root@hdp01 redis-cluster]# chmod u+x start-all.sh
第九步:创建集群 [root@hdp01 redis-cluster]# ./redis-trib.rb create --replicas 1 192.168.2.102:6380 192.168.2.102:6381 192.168.2.102:6382 192.168.2.102:6383 192.168.2.102:6384 192.168.2.102:6385 >>> Creating cluster Connecting to node 192.168.242.137:7001: OK Connecting to node 192.168.242.137:7002: OK Connecting to node 192.168.242.137:7003: OK Connecting to node 192.168.242.137:7004: OK Connecting to node 192.168.242.137:7005: OK Connecting to node 192.168.242.137:7006: OK >>> Performing hash slots allocation on 6 nodes... Using 3 masters: 192.168.242.137:7001 192.168.242.137:7002 192.168.242.137:7003 Adding replica 192.168.242.137:7004 to 192.168.242.137:7001 Adding replica 192.168.242.137:7005 to 192.168.242.137:7002 Adding replica 192.168.242.137:7006 to 192.168.242.137:7003 M: 8240cd0fe6d6f842faa42b0174fe7c5ddcf7ae24 192.168.242.137:7001 slots:0-5460 (5461 slots) master M: 4f52a974f64343fd9f1ee0388490b3c0647a4db7 192.168.242.137:7002 slots:5461-10922 (5462 slots) master M: cb7c5def8f61df2016b38972396a8d1f349208c2 192.168.242.137:7003 slots:10923-16383 (5461 slots) master S: 66adf006fed43b3b5e499ce2ff1949a756504a16 192.168.242.137:7004 replicates 8240cd0fe6d6f842faa42b0174fe7c5ddcf7ae24 S: cbb0c9bc4b27dd85511a7ef2d01bec90e692793b 192.168.242.137:7005 replicates 4f52a974f64343fd9f1ee0388490b3c0647a4db7 S: a908736eadd1cd06e86fdff8b2749a6f46b38c00 192.168.242.137:7006 replicates cb7c5def8f61df2016b38972396a8d1f349208c2 Can I set the above configuration? (type 'yes' to accept): yes >>> Nodes configuration updated >>> Assign a different config epoch to each node >>> Sending CLUSTER MEET messages to join the cluster Waiting for the cluster to join.. >>> Performing Cluster Check (using node 192.168.242.137:7001) M: 8240cd0fe6d6f842faa42b0174fe7c5ddcf7ae24 192.168.242.137:7001 slots:0-5460 (5461 slots) master M: 4f52a974f64343fd9f1ee0388490b3c0647a4db7 192.168.242.137:7002 slots:5461-10922 (5462 slots) master M: cb7c5def8f61df2016b38972396a8d1f349208c2 192.168.242.137:7003 slots:10923-16383 (5461 slots) master M: 66adf006fed43b3b5e499ce2ff1949a756504a16 192.168.242.137:7004 slots: (0 slots) master replicates 8240cd0fe6d6f842faa42b0174fe7c5ddcf7ae24 M: cbb0c9bc4b27dd85511a7ef2d01bec90e692793b 192.168.242.137:7005 slots: (0 slots) master replicates 4f52a974f64343fd9f1ee0388490b3c0647a4db7 M: a908736eadd1cd06e86fdff8b2749a6f46b38c00 192.168.242.137:7006 slots: (0 slots) master replicates cb7c5def8f61df2016b38972396a8d1f349208c2 [OK] All nodes agree about slots configuration. >>> Check for open slots... >>> Check slots coverage... [OK] All 16384 slots covered. [root@hdp01 redis-cluster]#
|
连接集群
./redis-cli -h 192.168.2.102 -p 7001 –c
-c:指定是集群连接
查看集群信息
查看集群信息
192.168.242.137:7002> cluster info
1 2 3 4 5 6 7 8 9 10 11 12 13
| 192.168.2.102:6381> cluster info cluster_state:ok cluster_slots_assigned:16384 cluster_slots_ok:16384 cluster_slots_pfail:0 cluster_slots_fail:0 cluster_known_nodes:6 cluster_size:3 cluster_current_epoch:6 cluster_my_epoch:2 cluster_stats_messages_sent:1034 cluster_stats_messages_received:1034 192.168.2.102:6381>
|
查看节点信息
1 2 3 4 5 6 7 8
| 192.168.2.102:6381> cluster nodes 46ca0fd9417a2bc172aa2863df2cab0da6617d2d 192.168.2.102:6382 master - 0 1537696177521 3 connected 10923-16383 f53c4df09307ee23d5e06d9a7e936c581ac80b3d 192.168.2.102:6384 slave fed12b118c7d4c6ab95a3c2d3ffe49a71e9b2afa 0 1537696173493 5 connected ef29970508e820fe2f7b7c2a57befc69e9ed2589 192.168.2.102:6385 slave 46ca0fd9417a2bc172aa2863df2cab0da6617d2d 0 1537696174499 6 connected 781aa9cb4deea53b8dce7405277d33176153fdc3 192.168.2.102:6383 slave d7cad0dd5dd1607eb4ca31e7be5344014d2a4cce 0 1537696176512 4 connected fed12b118c7d4c6ab95a3c2d3ffe49a71e9b2afa 192.168.2.102:6381 myself,master - 0 0 2 connected 5461-10922 d7cad0dd5dd1607eb4ca31e7be5344014d2a4cce 192.168.2.102:6380 master - 0 1537696175505 1 connected 0-5460 192.168.2.102:6381>
|
jedis连接集群
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
| package test;
import java.util.HashSet; import java.util.Set;
import org.junit.Test;
import redis.clients.jedis.HostAndPort; import redis.clients.jedis.JedisCluster;
public class test3 { @Test public void test() { Set<HostAndPort> nodes = new HashSet<>(); nodes.add(new HostAndPort("192.168.2.102", 6380)); nodes.add(new HostAndPort("192.168.2.102", 6381)); nodes.add(new HostAndPort("192.168.2.102", 6382)); nodes.add(new HostAndPort("192.168.2.102", 6383)); nodes.add(new HostAndPort("192.168.2.102", 6384)); nodes.add(new HostAndPort("192.168.2.102", 6385));
JedisCluster cluster = new JedisCluster(nodes); cluster.set("s4", "sunfuliang");
String result = cluster.get("s4"); System.out.println(result); cluster.close(); } }
|
Spring jedis连接redis集群
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
| package test;
import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import redis.clients.jedis.JedisCluster;
@RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(locations = "classpath:applicationContext.xml") public class test4 { @Autowired JedisCluster jedisCluster; @Test public void testJedisCluster() {
jedisCluster.set("name", "zhangsan"); String value = jedisCluster.get("name"); System.out.println(value); } }
|
applicationContext.xml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77
| <?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xmlns:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">
<bean id="jedisPoolConfig" class="redis.clients.jedis.JedisPoolConfig"> <property name="maxTotal" value="30" /> <property name="maxIdle" value="10" /> <property name="numTestsPerEvictionRun" value="1024" /> <property name="timeBetweenEvictionRunsMillis" value="30000" /> <property name="minEvictableIdleTimeMillis" value="1800000" /> <property name="softMinEvictableIdleTimeMillis" value="10000" /> <property name="maxWaitMillis" value="1500" /> <property name="testOnBorrow" value="false" /> <property name="testWhileIdle" value="true" /> <property name="blockWhenExhausted" value="false" /> </bean>
<bean id="jedisCluster" class="redis.clients.jedis.JedisCluster"> <constructor-arg index="0"> <set> <bean class="redis.clients.jedis.HostAndPort"> <constructor-arg index="0" value="192.168.2.102"></constructor-arg> <constructor-arg index="1" value="6380"></constructor-arg> </bean> <bean class="redis.clients.jedis.HostAndPort"> <constructor-arg index="0" value="192.168.2.102"></constructor-arg> <constructor-arg index="1" value="6381"></constructor-arg> </bean> <bean class="redis.clients.jedis.HostAndPort"> <constructor-arg index="0" value="192.168.2.102"></constructor-arg> <constructor-arg index="1" value="6382"></constructor-arg> </bean> <bean class="redis.clients.jedis.HostAndPort"> <constructor-arg index="0" value="192.168.2.102"></constructor-arg> <constructor-arg index="1" value="6383"></constructor-arg> </bean> <bean class="redis.clients.jedis.HostAndPort"> <constructor-arg index="0" value="192.168.2.102"></constructor-arg> <constructor-arg index="1" value="6384"></constructor-arg> </bean> <bean class="redis.clients.jedis.HostAndPort"> <constructor-arg index="0" value="192.168.2.102"></constructor-arg> <constructor-arg index="1" value="6385"></constructor-arg> </bean> </set> </constructor-arg> <constructor-arg index="1" ref="jedisPoolConfig"></constructor-arg> </bean>
</beans>
|