1.Redis集群介绍
什么是Redis集群?
Redis集群是一个实现分布式并且允许单点故障的Redis高级版本。
Redis集群没有最重要或者说中心节点,这个版本最主要的一个目标是设计一个线性可伸缩(可随意增删节点?)的功能。
Redis集群为了数据的一致性可能牺牲部分允许单点故障的功能,所以当网络故障和节点发生故障时这个系统会尽力去保证数据的一致性和有效性。(这里我们认为节点故障是网络故障的一种特殊情况)
为了解决单点故障的问题,我们同时需要masters 和 slaves。 即使主节点(master)和从节点(slave)在功能上是一致的,甚至说他们部署在同一台服务器上,从节点也仅用以替代故障的主节点。 实际上应该说 如果对从节点没有read-after-write(写并立即读取数据 以免在数据同步过程中无法获取数据)的需求,那么从节点仅接受只读操作。
已实现子集
Redis集群会把所有的单一key存储在非分布式版本的Redis中。对于复合操作比如求并集求交集之类则未实现。
在将来,有可能会增加一种为“Computation Node”的新类型节点。这种节点主要用来处理在集群中multi-key的只读操作,但是对于multi-key的只读操作不会以集群传输到Computation Node节点再进行计算的方式实现。
Redis集群版本将不再像独立版本一样支持多数据库,在集群版本中只有database 0,并且SELECT命令是不可用的。
Redis 集群是一个提供在多个Redis间节点间共享数据的程序集。
Redis集群并不支持处理多个keys的命令,因为这需要在不同的节点间移动数据,从而达不到像Redis那样的性能,在高负载的情况下可能会导致不可预料的错误.
Redis 集群通过分区来提供一定程度的可用性,在实际环境中当某个节点宕机或者不可达的情况下继续处理命令. Redis 集群的优势:
- 自动分割数据到不同的节点上。
- 整个集群的部分节点失败或者不可达的情况下能够继续处理命令。
参考 http://www.redis.cn/topics/cluster-tutorial.html
2.redis 集群安装
Redis集群部署文档(centos6系统)
3个主节点,在这里我们要创建6个redis节点,其中三个为主节点,三个为从节点,对应的redis节点的ip和端口对应关系如下)
127.0.0.1:7000
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
:下载redis。官网下载3.0.0版本,之前2.几的版本不支持集群模式
下载地址:https://github.com/antirez/redis/archive/3.0.0-rc2.tar.gz
:上传服务器,解压,编译
tar -zxvf redis-3.0.0-rc2.tar.gz mv redis-3.0.0-rc2.tar.gz redis3.0 cd /usr/local/redis3.0 make make install |
:创建集群需要的目录
mkdir -p /usr.local/cluster cd /usr.local/cluster mkdir 7000 mkdir 7001 mkdir 7002 mkdir 7003 mkdir 7004 mkdir 7005
|
:修改配置文件redis.conf
cp /usr/local/redis3.0/redis.conf /usr.local/cluster vi redis.conf 修改配置文件中的下面选项 port 7000 //端口7000,7002,7003 修改完redis.conf配置文件中的这些配置项之后把这个配置文件分别拷贝到7000/7001/7002/7003/7004/7005目录下面 cp /usr/local/cluster/redis.conf /usr/local/cluster/7000 cp /usr/local/cluster/redis.conf /usr/local/cluster/7001 cp /usr/local/cluster/redis.conf /usr/local/cluster/7002 cp /usr/local/cluster/redis.conf /usr/local/cluster/7003 cp /usr/local/cluster/redis.conf /usr/local/cluster/7004 cp /usr/local/cluster/redis.conf /usr/local/cluster/7005
注意:拷贝完成之后要修改7001/7002/7003/7004/7005目录下面redis.conf文件中的port参数,分别改为对应的文件夹的名称
|
:分别启动这6个redis实例
cd /usr/local/cluster/7000 redis-server redis.conf cd /usr/local/cluster/7001 redis-server redis.conf cd /usr/local/cluster/7002 redis-server redis.conf cd /usr/local/cluster/7003 redis-server redis.conf cd /usr/local/cluster/7004 redis-server redis.conf cd /usr/local/cluster/7005 redis-server redis.conf
启动之后使用命令查看redis的启动情况ps -ef|grep redis 如下图显示则说明启动成功
|
:执行redis的创建集群命令创建集群
cd /usr/local/redis3.0/src ./redis-trib.rb create --replicas 1 127.0.0.1:7000 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 |
执行上面的命令的时候会报错,因为是执行的ruby的脚本,需要ruby的环境
/usr/bin/env: ruby: No such file or directory
的环境,这里推荐使用yum install ruby安装
yum install ruby |
然后再执行第6步的创建集群命令,还会报错,提示缺少rubygems组件,使用yum安装
错误内容:
./redis-trib.rb:24:in `require': no such file to load -- rubygems (LoadError)
from ./redis-trib.rb:24
yum install rubygems |
再次执行第6步的命令,还会报错,提示不能加载redis,是因为缺少redis和ruby的接口,使用gem 安装
错误内容:
/usr/lib/ruby/site_ruby/1.8/rubygems/custom_require.rb:31:in `gem_original_require': no such file to load -- redis (LoadError)
from /usr/lib/ruby/site_ruby/1.8/rubygems/custom_require.rb:31:in `require'
from ./redis-trib.rb:25
gem install redis |
再次执行第6步的命令,正常执行
,然后配置完成。
集群即搭建成功!
:使用redis-cli命令进入集群环境
redis-cli -c -p 7000 |
简单说一下原理
redis cluster在设计的时候,就考虑到了去中心化,去中间件,也就是说,集群中的每个节点都是平等的关系,都是对等的,每个节点都保存各自的数据和整个集群的状态。每个节点都和其他所有节点连接,而且这些连接保持活跃,这样就保证了我们只需要连接集群中的任意一个节点,就可以获取到其他节点的数据。
哈希槽 (hash slot)
的方式来分配的。redis cluster 默认分配了 16384 个slot,当我们set一个key 时,会用CRC16
算法来取模得到所属的slot
,然后将这个key 分到哈希槽区间的节点上,具体算法就是:CRC16(key) % 16384。所以我们在测试的时候看到set 和 get 的时候,直接跳转到了7000端口的节点。
Redis 集群会把数据存在一个 master 节点,然后在这个 master 和其对应的salve 之间进行数据同步。当读取数据时,也根据一致性哈希算法到对应的 master 节点获取数据。只有当一个master 挂掉之后,才会启动一个对应的 salve 节点,充当 master 。
3个或以上
的主节点,否则在创建集群时会失败,并且当存活的主节点数小于总节点数的一半时,整个集群就无法提供服务了。
3.spring 整合redis集群
下面是具体的代码实现
1.maven依赖
1. <dependency>
2. <groupId>redis.clients</groupId>
3. <artifactId>jedis</artifactId>
4. 2.7.2</version>
5. </dependency>
2.applicationContext.xml中的配置
1. <!-- jedis cluster config -->
2. "genericObjectPoolConfig" class="org.apache.commons.pool2.impl.GenericObjectPoolConfig"
3. "maxWaitMillis" value="-1"
4. "maxTotal" value="1000"
5. "minIdle" value="8"
6. "maxIdle" value="100"
7. </bean>
8.
9. <bean id="jedisCluster" class="com.besttone.subscribe.util.JedisClusterFactory">
10. "addressConfig">
11. <value>classpath:redis-config.properties</value>
12. </property>
13. "addressKeyPrefix" value="address"
14.
15. "timeout" value="300000"
16. "maxRedirections" value="6"
17. "genericObjectPoolConfig" ref="genericObjectPoolConfig"
18. </bean>
3.JedisClusterFactory实现类
1. public class JedisClusterFactory implements
2.
3. private
4. private
5.
6. private
7. private
8. private
9. private
10.
11. private Pattern p = Pattern.compile("^.+[:]\\d{1,5}\\s*$");
12.
13. @Override
14. public JedisCluster getObject() throws
15. return
16. }
17.
18. @Override
19. public Class<? extends
20. return (this.jedisCluster != null ? this.jedisCluster.getClass() : JedisCluster.class);
21. }
22.
23. @Override
24. public boolean
25. return true;
26. }
27.
28.
29.
30. private Set<HostAndPort> parseHostAndPort() throws
31. try
32. new
33. this.addressConfig.getInputStream());
34.
35. new
36. for
37.
38. if
39. continue;
40. }
41.
42. String val = (String) prop.get(key);
43.
44. boolean
45.
46. if
47. throw new IllegalArgumentException("ip 或 port 不合法");
48. }
49. ":");
50.
51. new HostAndPort(ipAndPort[0], Integer.parseInt(ipAndPort[1]));
52. haps.add(hap);
53. }
54.
55. return
56. catch
57. throw
58. catch
59. throw new Exception("解析 jedis 配置文件失败", ex);
60. }
61. }
62.
63. @Override
64. public void afterPropertiesSet() throws
65. this.parseHostAndPort();
66.
67. new
68.
69. }
70. public void
71. this.addressConfig = addressConfig;
72. }
73.
74. public void setTimeout(int
75. this.timeout = timeout;
76. }
77.
78. public void setMaxRedirections(int
79. this.maxRedirections = maxRedirections;
80. }
81.
82. public void
83. this.addressKeyPrefix = addressKeyPrefix;
84. }
85.
86. public void
87. this.genericObjectPoolConfig = genericObjectPoolConfig;
88. }
89.
90. }
4.redis-config.properties文件
这是一个集群环境,六个节点(不同端口),三个master ,三个slaver
1. address1=192.168.30.139:7000
2. address2=192.168.30.139:7001
3. address3=192.168.30.139:7002
4. address4=192.168.30.139:7003
5. address5=192.168.30.139:7004
6. address6=192.168.30.139:7005
5.项目目录图
6.代码中使用(此代码为从redis中获取相关信息)
ok,运行之后,会发现redis会根据不同的key,把它们放入到不同的节点中,如下图
7.三个master节点中的数据
8.三个slave节点中的数据
实践过程中碰到的问题:
1.在一切准备好了之后,在操作redis的时候,却报错误:Too many Cluster redirections
由于,我是windows开发环境,在本机开了一个虚拟机,然后在虚拟机中搭建的linux集群环境,本机的ip和虚拟机中的ip不相同,所以报这个错误,
解决方法:在redis集群搭建过程中,在为每个节点分hash槽的时候,执行如下代码(其中,xxx为集群环境中的ip):
- ./redis-trib.rb create --replicas 1 xxx.xxx.xxx.xxx:7000 xxx.xxx.xxx.xxx:7001 xxx.xxx.xxx.xxx:7002 xxx.xxx.xxx.xxx:7003 xxx.xxx.xxx.xxx:7004 xxx.xxx.xxx.xxx:7005./redis-trib.rb create --replicas 1 127.0.0.1:7000 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
2.在一切搭建好后,我们使用redis-cli登陆,当命令:set msg XXX时,报错:
- (error) MOVED 6257 192.168.30.141:7001
解决:在使用客户端登陆时:加上-c参数,即:
- redis-cli -c -h 192.168.30.141 -p 7000
ok,以上满足使用,结束!!