文章目录
上一篇文章主要围绕着redis的持久化,过期策略和缓存清除策略进行讲解。这一节来介绍redis的分片机制,哨兵机制以及集群的搭建。
1.Redis分片机制
说明:将多台redis搭建成分片的结构.实现内存的扩容.
如果没有分片机制,Redis就被局限于单机所支持的内存容量。Redis的分片机制允许数据拆分存放在不同的Redis实例上,每个Redis实例只包含所有键的子集。可以减轻单台Redis的压力,提升Redis扩展能力和计算能力。如果我们只使用一个Redis实例,当Redis宕机将会直接停止服务,所以我们可以采取分片机制,将原本一台Redis实例维护的数据,改为由多个Redis实例共同维护这部分数据。
说白了就是更好的维护数据,提高响应效率。
1.1 分片实现
1.在redis根目录中创建shards文件
2. 分别复制redis.conf配置文件成3个
cp redis.conf shards/redis-6379.conf
cp redis.conf shards/redis-6380.conf
cp redis.conf shards/redis-6381.conf
3.修改端口号
根据配置文件的名称,修改各自的端口号。修改位置在92行
4.启动多台redis
redis-server redis-6379.conf
redis-server redis-6380.conf
redis-server redis-6381.conf
先关闭原先打开的redis,再打开刚配置的三个redis服务
OK。我们实验一下
/**
* redis分片测试
*/
@Test
public void testShards() {
String host = "192.168.180.160";
List<JedisShardInfo> shards = new ArrayList<JedisShardInfo>();
shards.add(new JedisShardInfo(host, 6379));
shards.add(new JedisShardInfo(host, 6380));
shards.add(new JedisShardInfo(host, 6381));
ShardedJedis jedis = new ShardedJedis(shards);
jedis.set("0419", "分片操作");
System.out.println(jedis.get("0419"));
}
那这个key为0419的值放在了哪个服务上了呢?
我们去看一下。分别连接上redis的三个服务。去找一下
我这里是放在了6381 的服务上
1.2 分片的原理(了解)
看到这朋友可能想知道他是如何进行实现的?这里说明一下
redis采用了一个算法叫做哈希一致性算法
它所解决的正是数据存储一致性问题
一致性Hash算法使用取模的方法,一致性Hash算法是对
(
2
32
)
\left(2^{32}\right)
(232)取模,什么意思呢?简单来说,一致性Hash算法将整个哈希值空间组织成一个虚拟的圆环,如假设某哈希函数H的值空间为0~
(
2
32
)
\left(2^{32}\right)
(232)-1(即哈希值是一个32位无符号整形)
整个空间按顺时针方向组织,圆环的正上方的点代表0,0点右侧的第一个点代表1,以此类推,2、3、4、5、6……直到
(
2
32
)
\left(2^{32}\right)
(232)-1,也就是说0点左侧的第一个点代表
(
2
32
)
\left(2^{32}\right)
(232)-1, 0和
(
2
32
)
\left(2^{32}\right)
(232)-1在零点中方向重合,我们把这个由
(
2
32
)
\left(2^{32}\right)
(232)个点组成的圆环称为Hash环。
这里我们有3个redis服务器,服务器A、服务器B、服务器C,那么,在生产环境中,这3台服务器肯定有自己的IP地址或主机名,我们使用它们各自的IP地址或主机名作为关键字进行哈希计算,使用哈希后的结果对2^32取模,可以使用如下公式示意:
hash(服务器IP地址) % 2^32
就会分别得到三个服务的哈希值。并将其服务映射到哈希环上。示范如下:
接下来将数据key使用相同的函数Hash计算出哈希值,并确定此数据在环上的位置,从此位置沿环顺时针“行走”,第一台遇到的服务器就是其应该定位到的服务器!
但这样也会暴露了一个问题。由于key的不确定性。那就可能会出现数据倾斜的问题。意思就是有的redis服务器数据偏多,而有的redis服务器数据偏少。也就出现了负载均衡不均的问题。所以这就不符合咱们的初衷了。
不过redis已经想到了这一点。redis又借此提出了虚拟节点的概念:再次 对每一个服务节点计算多个哈希,每个计算结果位置都放置一个此服务节点,称为虚拟节点。具体做法可以在服务器IP或主机名的后面增加编号来实现。
接下来再为每台服务器计算2个虚拟节点,于是可以分别计算 “Node 6079#1”、“Node 6079#2”、“Node 6080#1”、“Node 6080#2”、“Node 6081#1”、“Node 6081#2”的哈希值,于是形成六个虚拟节点:
当key的哈希值对应好虚拟节点后就会映射到真实的redis服务器上。
但是增加虚拟节点还是依然存在hash倾斜的问题,但是随着虚拟节点的大量增加,就会使得出现hash命中失败的现象降到最低。一致性hash算法公式:1-n/(n + m) * 100% n表示真实的redis节点,m表示增加的虚拟节点,可以看到随着虚拟节点的增加,命中失败越来越低。
1.3 分片机制分析
分片是由多台Redis实例共同运转,所以如果其中一个Redis实例宕机,则整个分片都将无法使用,所以分片机制无法实现高可用。
如果有不同的key映射到不同的Redis实例,这时候不能对这两个key做交集或者使用事务。
使用分片机制因为涉及多实例,数据处理比较复杂。
2.Redis哨兵机制
刚分析了若使用分片机制的话可以实现内存的扩容.但是如果某个redis节点宕机.则导致分片整体不能使用.采用redis高可用的形式.实现redis可持续性.
2.1 主从复制
哨兵的前提是主从机的存在。介绍如下:
- redis的复制功能是支持多个数据库之间的数据同步。一类是主数据库(master)一类是从数据库(slave),主数据库可以进行读写操作,当发生写操作的时候自动将数据同步到从数据库,而从数据库一般是只读的,并接收主数据库同步过来的数据,一个主数据库可以有多个从数据库,而一个从数据库只能有一个主数据库。
- 通过redis的复制功能可以很好的实现数据库的读写分离,提高服务器的负载能力。主数据库主要进行写操作,而从数据库负责读操作。
2. 2 哨兵策略
Redis的哨兵是用于管理多个redis服务。
它一共做三个工作:
- 监控(Monitoring): 哨兵(sentinel) 会不断地检查你的Master(主机)和Slave(从机)是否运作正常。
- 提醒(Notification):当被监控的某个 Redis出现问题时, 哨兵(sentinel) 可以通过 API 向管理员或者其他应用程序发送通知。
- 自动故障迁移(Automatic failover):当一个Master不能正常工作时,哨兵(sentinel) 会开始一次自动故障迁移操作,它会将失效Master的其中一个Slave升级为新的Master, 并让失效Master的其他Slave改为复制新的Master; 当客户端试图连接失效的Master时,集群也会向客户端返回新Master的地址,使得集群可以使用Master代替失效Master。
工作流程:
1.当哨兵启动时,会根据配置文件信息监控主机master.同时获取主机的状态信息(包含主从结构).
2.哨兵利用心跳检测机制(PING-PONG)时常校验主机是否存活.当连续3次发现主机没有响应.说明主机可能宕机.
3.哨兵根据获取的信息开始选举新的主机.同时修改其他服务器的主从配置.
2.3 哨兵配置
1.搭建主从同步
1).关闭分片所有redis服务器.
2). 复制分片信息
cp -r shards sentinel
3). 进入sentinel文件中删除持久化文件dump.rdb。(shards目录在redis根目录下)
4)启动此目录下的三个redis服务
2检查主从状态:默认条件下所有的节点都是主机.
info replication
3 实现主从挂载
规划: 6379主机 6380/6381从机
命令: slaveof IP port
SLAVEOF 192.168.180.160 6379
分别将6380/6381挂载到6379中
关于主从说明:
主机与从机可以互相通信.只有主机可以修改数据,从机只能同步数据.不能修改.
2.4 哨兵搭建
回到redis根目录,复制哨兵配置文件到sentinel目录下
cp sentinel.conf sentinel
并编辑此文件
关闭保护模式
开启后台启动
配置哨兵监控
sentinel monitor mymaster 127.0.0.1 6379 1
mymaster:主机的变量名称
最后的参数2: 几票生效. 一般哨兵超过半数投票生效.(这里改为1.因为一共三个。若主机宕机了,就剩下俩个。本着选举奇数原则。只有有一个投票,就可以从机变为主机。避免“投票出现平局”的情况)
修改哨兵宕机选举时间
113行
修改推选失败的超时时间
退出编辑状态,并保存退出
2.5 哨兵高可用测试
- 启动哨兵
redis-sentinel sentinel.conf
- 关闭主节点6379
- 检查哨兵推选状态。
- 检查新的主从结构是否正常.
可以看到我关掉主机6379后。主机变成了6381.因此6380 的主机就成了6381.
那要是6379重启了呢?
看效果。刚重启了后,要稍等片刻才能看到这个。我们上面设置了选举的时间。故也许不能马上看到效果
测试成功
2.6 哨兵重置说明
有时候因误操作,会导致哨兵机制失败。那就需要重置
删除哨兵配置文件最后的配置
删除后需要重新按照步骤进行设置一下。主要修改的其实是哨兵检测的主机。误操作可能会导致
检测的主机发生变化。所以重置的时候需要检查一下。
改好好。直接关闭所有redis服务。重启即可。
2.4 分片哨兵总结
分片特点:
优点:
- 可以实现redis内存扩容
- redis分片效率是最高的(哨兵/集群)
缺点:
3. 如果节点宕机.则整个分片不能使用
哨兵特点:
优点:
- 可以实现redis高可用.
缺点:
- 哨兵不能实现内存扩容.
- 哨兵本身不能实现高可用.
2.5 实例测试
/**
* 测试哨兵
*/
@Test
public void testSentinel() {
Set<String> sentinels = new HashSet<>();
sentinels.add("192.168.180.160:26379");
JedisSentinelPool pool =new JedisSentinelPool("mymaster", sentinels) ;
Jedis jedis = pool.getResource();
jedis.set("0420", "测试哨兵!!!!");
System.out.println("获取数据:"+jedis.get("0420"));
}
运行,看效果
可以看到主机和从机都有数据。主机的数据一旦删除,那从机的数据也会自动删除。
我们来拿我们的项目试验一下
首先更改redis配置文件。nodes是放的哨兵的服务。不要写错
#配置redis哨兵
redis.masterName=mymaster
redis.nodes=192.168.180.160:26379
RedisConfig修改:
将原有的都注释,添加新的内容
/**
* 配置哨兵
*/
@Value("${redis.masterName}")
private String masterName;
@Value("${redis.nodes}")
private String nodes;
@Bean("jedisSentinelPool")
public JedisSentinelPool jedisSentinelPool() {
Set<String> sentinels = new HashSet<>();
sentinels.add(nodes);
return new JedisSentinelPool(masterName, sentinels);
}
@Bean
public Jedis jedis(@Qualifier("jedisSentinelPool") JedisSentinelPool jedisSentinelPool) {
return jedisSentinelPool.getResource();
}
修改AOP注入属性
重启服务,看效果
3.Redis 集群
从上面我们可以看到无论是分片还是哨兵都是针对一个主redis服务器。分片(主从模式)下不能实现高可用,有一个宕机,整个就完蛋。哨兵模式呢也有一个问题,不能扩容啊。
这里采用集群的形式。
redis cluster是Redis的分布式解决方案,在3.0版本推出后有效地解决了redis分布式方面的需求
自动将数据进行分片,每个master上放一部分数据提供内置的高可用支持,部分master不可用时,还是可以继续工作的
支撑N个redis master node,每个master node都可以挂载多个slave node
高可用,因为每个master都有salve节点,那么如果mater挂掉,redis cluster这套机制,就会自动将某个slave切换成master。
通俗的说就是部署多个主从redis服务器。实现redis扩容以及高可用问题。
3.1 集群搭建
我们先来操作一下进行理解。将原有的redis服务全部关闭
准备
主从划分:3台主机 3台从机共6台 端口划分7000-7005
回到redis跟目录,创建目录cluster
mkdir cluster
在cluster文件夹中分别创建7000-7005文件夹
复制配置文件
将redis根目录中的redis.conf文件复制到cluster/7000/目录下 并以原名保存
cp redis.conf cluster/7000/
编辑配置文件 ,使用vim [文件名]
命令。此命令会在右下角部分显示行数
- 注释本地绑定IP地址
- 关闭保护模式
- 修改端口号
- 启动后台启动
- 修改pid文件 。修改目录为自己的目录
- 修改持久化文件路径
- 设定内存优化策略
- 关闭AOF模式
- 开启集群配置
- 开启集群配置文件,修改名字
- 修改集群超时时间
复制修改后的配置文件
说明:将7000文件夹下的redis.conf文件分别复制到7001-7005中
[root@localhost cluster]# cp 7000/redis.conf 7001/
[root@localhost cluster]# cp 7000/redis.conf 7002/
[root@localhost cluster]# cp 7000/redis.conf 7003/
[root@localhost cluster]# cp 7000/redis.conf 7004/
[root@localhost cluster]# cp 7000/redis.conf 7005/
批量修改
说明:分别将7001-7005文件中的7000改为对应的端口号的名称,修改时注意方向键的使用
使用vim进入文件后,直接输入:%s/7000/7001/g
。切记不要进入编辑状态
回车后,再次输入:wq
。保存
依次修改其他文件。改成各自的端口号
创建启动脚本 vim start.sh
#!/bin/sh
redis-server 7000/redis.conf &
redis-server 7001/redis.conf &
redis-server 7002/redis.conf &
redis-server 7003/redis.conf &
redis-server 7004/redis.conf &
redis-server 7005/redis.conf &
编辑关闭的脚本 vim shutdown.sh
#!/bin/sh
redis-cli -p 7000 shutdown &
redis-cli -p 7001 shutdown &
redis-cli -p 7002 shutdown &
redis-cli -p 7003 shutdown &
redis-cli -p 7004 shutdown &
redis-cli -p 7005 shutdown &
启动redis节点
sh start.sh
检查redis节点启动是否正常。可以看到都启动成功
创建redis集群
#5.0版本执行 使用C语言内部管理集群
redis-cli --cluster create --cluster-replicas 1 192.168.180.160:7000 192.168.180.160:7001 192.168.180.160:7002 192.168.180.160:7003 192.168.180.160:7004 192.168.180.160:7005
标记的是哈希槽。这个一会解释
3.2 Redis集群高可用测试
- 关闭redis主机.检查是否自动实现故障迁移.
- 再次启动关闭的主机.检查是否能够实现自动的挂载.
一般情况下 能够实现主从挂载
个别情况: 宕机后的节点重启,可能挂载到其他主节点中(7001-7002) 正确的
3.3 集群原理
刚才操作了一下,那他具体是怎么工作的呢?
Redis的所有节点都会保存当前redis集群中的全部主从状态信息.并且每个节点都能够相互通信.当一个节点发生宕机现象.则集群中的其他节点通过PING-PONG检测机制检查Redis节点是否宕机.当有半数以上的节点认为宕机.则认为主节点宕机.同时由Redis剩余的主节点进入选举机制.投票选举链接宕机的主节点的从机.实现故障迁移.
Redis 集群没有并使用传统的一致性哈希来分配数据,而是采用另外一种叫做哈希槽 (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.4 Redis集群宕机条件
特点:集群中如果主机宕机,那么从机可以继续提供服务,
当主机中没有从机时,则向其它主机借用多余的从机.继续提供服务.如果主机宕机时没有从机可用,则集群崩溃.
答案:9个redis节点,节点宕机5-7次时集群才崩溃.
3.5 Redis hash槽存储数据原理
说明: RedisCluster采用此分区,所有的键根据哈希函数(CRC16[key]&16383)映射到0-16384槽内,共16384个槽位,每个节点维护部分槽及槽所映射的键值数据.根据主节点的个数,均衡划分区间.
算法 :哈希函数: Hash()=CRC16[key]&16384按位与
当向redis集群中插入数据时,首先将key进行计算.之后将计算结果匹配到具体的某一个槽的区间内,之后再将数据set到管理该槽的节点中.
3.6 spring整合集群入门案例
@Test
public void testCluster() {
Set<HostAndPort> nodes = new HashSet<>();
nodes.add(new HostAndPort("192.168.180.160", 7000));
nodes.add(new HostAndPort("192.168.180.160", 7001));
nodes.add(new HostAndPort("192.168.180.160", 7002));
nodes.add(new HostAndPort("192.168.180.160", 7003));
nodes.add(new HostAndPort("192.168.180.160", 7004));
nodes.add(new HostAndPort("192.168.180.160", 7005));
JedisCluster cluster = new JedisCluster(nodes);
cluster.set("0421", "redis集群搭建完成!!!!");
System.out.println(cluster.get("0421"));
}
3.7 项目应用
编辑Pro文件。注意是自己虚拟机的ip。
#配置redis集群
redis.nodes=192.168.180.160:7000,192.168.180.160:7001,192.168.180.160:7002,192.168.180.160:7003,192.168.180.160:7004,192.168.180.160:7005
编辑配置类.将上节的哨兵的配置注释掉。换成集群的相关配置
@Configuration
@PropertySource("classpath:/properties/redis.properties")
public class RedisConfig {
@Value("${redis.nodes}")
private String nodes;
@Bean
public JedisCluster jedisCluster() {
Set<HostAndPort> nodes = getNodes();
return new JedisCluster(nodes);
}
private Set<HostAndPort> getNodes() {
Set<HostAndPort> set = new HashSet<>();
String[] nodesArray = nodes.split(","); //获取node数组
for (String node : nodesArray) {
String host = node.split(":")[0];
int port = Integer.parseInt(node.split(":")[1]);
set.add(new HostAndPort(host, port));
}
return set;
}
}
修改AOP属性注入
重启服务
我这出现了原来serviceImpl引用的Jedis错误。我们将此涉及到的方法注释到就可以了。
查看缓存
可以看到我这是7002,7003是放了缓存,而其他没有。这俩谁是主机和从机呢。我们去看看
github
https://github.com/lmy1965673628/jingtao.git
总结
这一节围绕着redis的核心技术分片,哨兵,集群进行讲解。
除了这些,redis还在单点登录,授权验证等业务还有重要作用,往后的教程将逐步进行讲解。
敬请期待。。。。。