什么是Redis-Cluster
为何要搭建Redis集群。Redis是在内存中保存数据的,而我们的电脑一般内存都不大,这也就意味着Redis不适合存储大数据,适合存储大数据的是Hadoop生态系统的Hbase或者是MogoDB。Redis更适合处理高并发,一台设备的存储能力是很有限的,但是多台设备协同合作,就可以让内存增大很多倍,这就需要用到集群。
Redis集群搭建的方式有多种,例如使用客户端分片、Twemproxy、Codis等,但从redis 3.0之后版本支持redis-cluster集群,它是Redis官方提出的解决方案,Redis-Cluster采用无中心结构,每个节点保存数据和整个集群状态,每个节点都和其他所有节点连接。其redis-cluster架构图如下:
客户端与 redis 节点直连,不需要中间 proxy 层.客户端不需要连接集群所有节点连接集群中任何一个可用节点即可。
所有的 redis 节点彼此互联(PING-PONG 机制),内部使用二进制协议优化传输速度和带宽.
分布式存储机制——槽
redis-cluster 把所有的物理节点映射到[0-16383]slot 上,cluster 负责维护
node<->slot<->value
Redis 集群中内置了 16384 个哈希槽,当需要在 Redis 集群中放置一个 key-value 时,redis 先对 key 使用 crc16 算法算出一个结果,然后把结果对 16384 求余数,这样每个key 都会对应一个编号在 0-16383 之间的哈希槽,redis 会根据节点数量大致均等的将哈希槽映射到不同的节点。
例如三个节点:槽分布的值如下:
SERVER1: 0-5460
SERVER2: 5461-10922
SERVER3: 10923-16383
容错机制-投票
选举过程是集群中所有master参与,如果半数以上master节点与故障节点通信超过(cluster-node-timeout),认为该节点故障,自动触发故障转移操作. 故障节点对应的从节点自动升级为主节点
什么时候整个集群不可用(cluster_state:fail)?
如果集群任意master挂掉,且当前master没有slave.集群进入fail状态,也可以理解成集群的slot映射[0-16383]不完成时进入fail状态.
需要搭建6台服务器。这边我们只在一台服务器上来搭建一个伪集群
需要运行在不同的端口 7001-7006
首先,需要安装gcc,因为redis需要c语言的编译环境
使用 yum 命令安装 ruby (需要使用 ruby 脚本来实现集群搭建)
将redis源码包上传到linux系统,然后进行解压
编译redis源码,进入redis源码文件夹
看到以下输出结果,表示编译成功
创建目录/usr/local/redis-cluster目录, 安装6个redis实例,分别安装在以下目录
复制配置文件 将 /redis-3.0.0/redis.conf 复制到redis下的bin目录下
修改每个redis节点的配置文件redis.conf 修改运行端口为7001 (7002 7003 .....)
默认是6379
将cluster-enabled yes 前的注释去掉(632行)
剩余一样配置
启动每个实例
以第一个实例为例,命令如下
复制窗口,启动剩余redis
查看是否均启动成功
上传redis-3.0.0.gem ,安装 ruby用于搭建redis集群的脚本
使用 ruby 脚本搭建集群
进入redis源码目录中的src目录 执行下面的命令
./redis-trib.rb create --replicas 1 10.1.130.93:7001 10.1.130.93:7002 10.1.130.93:7003 10.1.130.93:7004 10.1.130.93:7005 10.1.130.93:7006
刚才的redis启动界面发生了变化
增加了从节点
通过redis客户端工具连接redis集群
Redis-cli 连接集群:
redis-cli -p 主机ip -p 端口(集群中任意端口) -c |
-c:代表连接的是 redis 集群
测试值的存取:
(1)从本地连接到集群redis 使用7001端口 加 -c 参数
(2)存入name值为abc ,系统提示此值被存入到了7002端口所在的redis (槽是5798)
(3)提取name的值,可以提取。
(4)退出(quit)
(5)再次以7001端口进入 ,不带-c
(6)查询name值,无法获取,因为值在7002端口的redis上
(7)我们以7002端口进入,获取name值发现是可以获取的,而以其它端口进入均不能获取
SpringDataRedis 连接 Redis 集群
applicationContext-redis-cluster.xml
<?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:p="http://www.springframework.org/schema/p"
xmlns:context="http://www.springframework.org/schema/context"
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">
<!-- 加载配置属性文件 -->
<context:property-placeholder ignore-unresolvable="true" location="classpath:properties/redis-cluster-config.properties" />
<bean id="redis-clusterConfiguration" class="org.springframework.data.redis.connection.redis-clusterConfiguration">
<property name="maxRedirects" value="${redis.maxRedirects}"></property>
<property name="clusterNodes">
<set>
<bean class="org.springframework.data.redis.connection.redis-clusterNode">
<constructor-arg name="host" value="${redis.host1}"></constructor-arg>
<constructor-arg name="port" value="${redis.port1}"></constructor-arg>
</bean>
<bean class="org.springframework.data.redis.connection.redis-clusterNode">
<constructor-arg name="host" value="${redis.host2}"></constructor-arg>
<constructor-arg name="port" value="${redis.port2}"></constructor-arg>
</bean>
<bean class="org.springframework.data.redis.connection.redis-clusterNode">
<constructor-arg name="host" value="${redis.host3}"></constructor-arg>
<constructor-arg name="port" value="${redis.port3}"></constructor-arg>
</bean>
<bean class="org.springframework.data.redis.connection.redis-clusterNode">
<constructor-arg name="host" value="${redis.host4}"></constructor-arg>
<constructor-arg name="port" value="${redis.port4}"></constructor-arg>
</bean>
<bean class="org.springframework.data.redis.connection.redis-clusterNode">
<constructor-arg name="host" value="${redis.host5}"></constructor-arg>
<constructor-arg name="port" value="${redis.port5}"></constructor-arg>
</bean>
<bean class="org.springframework.data.redis.connection.redis-clusterNode">
<constructor-arg name="host" value="${redis.host6}"></constructor-arg>
<constructor-arg name="port" value="${redis.port6}"></constructor-arg>
</bean>
</set>
</property>
</bean>
<bean id="jedisPoolConfig" class="redis.clients.jedis.JedisPoolConfig">
<property name="maxIdle" value="${redis.maxIdle}" />
<property name="maxTotal" value="${redis.maxTotal}" />
</bean>
<bean id="jeidsConnectionFactory" class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory" >
<constructor-arg ref="redis-clusterConfiguration" />
<constructor-arg ref="jedisPoolConfig" />
</bean>
<bean id="redisTemplate" class="org.springframework.data.redis.core.RedisTemplate">
<property name="connectionFactory" ref="jeidsConnectionFactory" />
</bean>
</beans>
添加属性文件redis-cluster-config.properties
#cluster configuration
redis.host1=10.1.130.93
redis.port1=7001
redis.host2=10.1.130.93
redis.port2=7002
redis.host3=10.1.130.93
redis.port3=7003
redis.host4=10.1.130.93
redis.port4=7004
redis.host5=10.1.130.93
redis.port5=7005
redis.host6=10.1.130.93
redis.port6=7006
redis.maxRedirects=3
redis.maxIdle=100
redis.maxTotal=600
模拟集群异常测试
关闭节点命令
./redis-cli -p 端口 shutdown |
- 测试关闭7001 和7004, 看看会发生什么。
- 测试关闭7001、7002、7003 会发生什么。