目录
主从模式
主从模式的好处
主从模式的缺点
主从节点数据同步的原理
哨兵模式
哨兵模式的优点
哨兵模式的缺点
哨兵模式的原理
集群模式
集群模式的优点
集群模式的原理
现在开始写redis系列,虽然都是照着别人的博客在写,但是我尽量翻译成我自己的理解,平时项目中有用redis,但是对reids的关注比较少,所以自认为redis是自己比较薄弱的一环。其实在一年之前为了找工作,reids这块还是准备了一些的,但是感觉没有形成系统,所以从今天开始将redis的知识都梳理一遍。
今天写的这redis集群在面试的时候有两家公司被问到过,一家是中国平安,当时问redis 哨兵模式,自己一脸懵逼,后面再到松鼠拼拼去面试也被问到了,当时刚好自己看了,我觉得自己的回答令面试官挺满意的。不过最后都没有过,哈哈。
redis集群有三种方式:主从模式,哨兵模式,redis-cluster集群(我叫做多主结点模式)
即一个主节点多个从结点,能够实现主从数据同步。
1.实现数据备份。
2.实现读写分离,减轻主结点读操作的压力。
1.当主节点挂掉后,整个服务变得不可用,系统不能自动恢复。
2.每一个节点存储的值都是一样的,当达到存储上限后,很难在线扩容。
1.从节点连接上了主节点后,会发送一个fsync的命令,主节点收到fsync命令后会执行bgsave命令,将内存的数据生成一个RDB文件,并开辟一个缓存空间,用来缓存后面过来的写操作。
2.主节点将生成的RDB文件发送给从节点,发送完成后,再将缓存区里面的写操作发送给从节点。
3.从节点接受到主节点的RDB文件,丢弃以前旧的数据,载入RDB文件。载入完RDB文件后,开启接受来自缓存区的写命令。
4.后面主服务器每接受一个写命令后,会发送给从服务器,从服务器会执行相同的操作
哨兵模式是在加了主从节点基础之上,新增了哨兵结点,这些哨兵结点能将监控主从结点的状态,并能实现故障转移,当主节点挂掉之后,能选中一个从节点作为新的主节点,这样服务能接着正常运行。
1.实现数据备份和读写分离操作。
2.能监控主从结点的状态,并能通过api通知其他程序。
3.当主结点挂掉后,哨兵结点能选举出一个从节点作为新的主结点,这样程序还能接着运行。
哨兵模式虽然解决了故障转移,但是却没有解决系统动态扩容的问题,由于每一个结点存储的内容都一样,会出现空间大小的瓶颈。
先说一下PING命令和INFO命令的区别
PING命令是哨兵节点用来监控主从节点状态的,监控主从节点有没有下线。
INFO命令是哨兵节点用来获取主从节点信息,发现新的从节点,根据获取到的主节点信息来进行角色变更,这也是为什么哨兵节点只需要连接主节点就能发现其他从节点的原因。
1.哨兵结点会以每秒的速度向主节点,从节点和哨兵节点发送PING命令。当在规定的时间内还没有收到应答的时候,这个节点会被标记为主观下线。
2.如果被标记的主观下线的节点是主节点的话,其他哨兵节点会以每秒一次的频率(发送INFO命令)确认这个主节点是否主观下线,如果有足够多的哨兵节点(数量可以在配置文件里面配置)确认这个主节点主观下线了,那这个主节点就会被标注成客观下线。
3.当主节点被标注成客观下线后,哨兵节点会选举出一个从节点作为新的主节点。而选举的依据是:
A.先判断每个节点的优先级,选出优先级最高的从节点作为主节点。
B.如果大家优先级都一样高,选出从节点中复制偏移量最大的作为主节点。
C.如果大家复制偏移量一样大,选出id最小的从节点作为主节点。
1.故障自动转移。
2.能够实现自动扩容,有多个主节点,每一个主节点存储的值都不一样,能在线添加主节点和删除节点。
集群模式拥有多个主节点,每一个节点都有独立的读写能力,主节点直接相互通信,每一个主节点下面可以挂多个从节点。redis集群将划分了16384个哈希槽,所有的数据在存入到redis的时候只需对它的key进行CRC16计算,然后对16384进行取模运算,这样就可以确认这个值会被存入到哪一个哈希槽中。
并且每一个主节点都会有一个数据分布表,这个数据分布表里面会存储的有哈希槽和节点之间的映射关系,连接一个主节点,当向redis存储值的时候,如果这个值对应的哈希槽刚好落在了这个主节点上,那么直接执行命令后返回结果,如果这个值对应的哈希槽不在这个主节点上,那么主节点会返回一个MOVE错误,并且会带上这个值应该存放的主节点信息,这样客户端可以找连接正确的主节点进行操作,但是这样有一个坏处就是会进行两次访问。还有一种做法是客户端获取到整个的集群分布表,这样每次请求就能正确的找到节点。如果当根据数据分布表来查找对应的哈希槽出现错误,那么可以执行cluster nodes命令更新数据分布表。
当有一个节点的主从节点变得都不可用的时候,这个集群也会变得不可用,如果有一半以上的主节点宕机了,那这个集群也会变得不可用。
节点A会定期ping消息给节点B,如果节点B能接受消息,就会返回一个pong消息给节点A,节点A就会更新和B节点通信的最新时间
当在规定的时间内节点A还没有收到节点B给的返回时间后,节点A会将节点B标记为主观下线,当超过一半的主节点认为这个节点主观下线的时候,那这个节点会被标记为客观下线,然后从这个主节点下面的从节点选举出一个作为新的主节点。
开启redis集群模式的关键是要修改配置文件里面的:
cluster-enabled yes #开启集群模式
package com.pc.jedis.test;
import java.util.HashSet;
import java.util.Set;
import redis.clients.jedis.HostAndPort;
import redis.clients.jedis.JedisCluster;
/**
* Jedis集群测试
*
* @author Switch
* @data 2017年2月11日
* @version V1.0
*/
public class JedisClusterTest {
public static void main(String[] args) {
// 创建并填充节点信息
Set nodes = new HashSet<>();
nodes.add(new HostAndPort("192.168.37.131", 7001));
nodes.add(new HostAndPort("192.168.37.131", 7002));
nodes.add(new HostAndPort("192.168.37.131", 7003));
nodes.add(new HostAndPort("192.168.37.131", 7004));
nodes.add(new HostAndPort("192.168.37.131", 7005));
nodes.add(new HostAndPort("192.168.37.131", 7006));
// 创建JedisCluster对象
JedisCluster jedisCluster = new JedisCluster(nodes);
// 使用jedisCluster操作redis
String key = "jedisCluster";
String setResult = jedisCluster.set(key, "hello redis!");
System.out.println(setResult);
String getResult = jedisCluster.get(key);
System.out.println(getResult);
// 关闭jedisCluster(程序执行完后才能关闭,内部封装了连接池)
jedisCluster.close();
}
}