2、redis集群

redis集群

集群的概念:通过添加多台服务器,提供相同的服务,从而使服务器达到高效率,高可用的状态。

redis使用集群的必要性:一台redis的服务器确实是可以基本达到我们读写的要求。

  1. 当数据量大的时候,读写的效率就会降低。

  2. 当单个redis服务宕机的时候,程序就无法正常的运行程序。

    总结:所以使用redis集群是很有必要的,能够提高程序的读写能力。

redis集群的学习

  1. redis集群中,每一台redis服务器为一个节点
  2. redis集群中,有两种的节点,主节点(master),从节点(slave)
  3. redis集群中,都是基于主从复制的。

redis主从复制

主从复制概念:

  • 主从复制有可以有很多节点,但有且只有一个主节点。
  • 主节点可以进行读和写的功能,从节点只有读的功能
  • 主节点写进数据时,其他从节点会从主节点中复制更新数据到自己的持久化数据中

主从复制的缺点:当主节点宕机了,就无法更新数据了,读的只能是以前的数据。

配置和实现

  1. 需求

    主节点 6380
    从节点(两个) 6381,6382
  2. 配置步骤

    1. 在/usr/local下创建一个 redis/master-slave目

      [root@node0719 local]# mkdir -p redis/master-slave
    2. 在master-slave目录创建3个子目录

      [root@node0719 master-slave]# mkdir 6380 6381 6382
    3. 依次拷贝redis解压目录下的redis.conf配置文件,到这三个子目录中。

      [root@node0719 master-slave]# cp /root/redis-4.0.14/redis.conf ./6380/
      [root@node0719 master-slave]# cp /root/redis-4.0.14/redis.conf ./6381/
      [root@node0719 master-slave]# cp /root/redis-4.0.14/redis.conf ./6382/
    4. 进入6380目录,修改redis.conf,将port端口修改成6380即可。

      [root@node0719 master-slave]# cd ./6380
      [root@node0719 6380]# vim redis.conf

    5. 进入6381目录,修改redis.conf,将port端口改成6381,同时指定开启主从复制。

      [root@node0719 6380]# cd ../6381
      [root@node0719 6381]# vim redis.conf

    6. 进入6382目录,修改redis.conf,将port端口改成6382,同时指定开启主从复制。

      [root@node0719 6380]# cd ../6382
      [root@node0719 6381]# vim redis.conf

      测试

      1. 打开三个xshell窗口,在每一个窗口中,启动一个redis节点。查看日志输出。(不要改成后台模式启动,看不到日志,不直观)

        1. [root@node0719 master-slave]# cd 6380 && redis-server ./redis.conf
        2. [root@node0719 master-slave]# cd 6381 && redis-server ./redis.conf
        3. [root@node0719 master-slave]# cd 6382 && redis-server ./redis.conf
      2. 另外再打开三个xshell窗口,在每一个窗口中,登陆一个redis节点

        1. [root@node0719 ~]# redis-cli -p 6380
        2. [root@node0719 ~]# redis-cli -p 6381
        3. [root@node0719 ~]# redis-cli -p 6382
      3. 在主节点6380上,进行读写操作,操作成功

        [root@node0719 ~]# redis-cli -p 6380
        127.0.0.1:6380> set user:name zs
        OK
        127.0.0.1:6380> get user:name
        "zs"
        127.0.0.1:6380>
      4. 在从节点6381上

        读操作执行成功,并且成功从6380上同步了数据

        [root@node0719 ~]# redis-cli -p 6381
        127.0.0.1:6381> get user:name
        "zs"

        写操作执行失败。(从节点,只能读,不能写)

        127.0.0.1:6381> set user:age 18
        (error) READONLY You can't write against a read only slave.

Sentinel哨兵模式

当一个主节点的服务器宕机时,无法正常就行写的功能,而哨兵模式能够在从节点中选出其中一个来当这个主节点,这样就可以正常的进行写的操作了。哨兵模式还可以监听多个主从复制的主节点。

哨兵的任务:Redis 的 Sentinel 系统用于管理多个 Redis 服务器(instance), 该系统执行以下三个任务

  • 监控(Monitoring): Sentinel 会不断地检查你的主服务器和从服务器是否运作正常。
  • 提醒(Notification): 当被监控的某个 Redis 服务器出现问题时, Sentinel 可以通过 API 向管理员或者其他应用程序发送通知。
  • 自动故障迁移(Automatic failover): 当一个主服务器不能正常工作时, Sentinel 会开始一次自动故障迁移操作, 它会进行选举,将其中一个从服务器升级为新的主服务器, 并让失效主服务器的其他从服务器改为复制新的主服务器; 当客户端试图连接失效的主服务器时, 集群也会向客户端返回新主服务器的地址, 使得集群可以使用新主服务器代替失效服务器。

监控(Monitoring)

  1. ​ Sentinel可以监控任意多个Master和该Master下的Slaves。(即多个主从模式)
  2. 同一个哨兵下的、不同主从模型,彼此之间相互独立。
  3. Sentinel会不断检查Master和Slaves是否正常。

自动故障切换(Automatic failover)

当主节点发生宕机时,哨兵就会进行投票,选出一个主节点。半票的原则。

投票

选举

而宕机后的主节有重新服务,此时会变成一个从节点。

哨兵模式的部署

前提:已运行了一个主从复制。还需要配置3个sentinel

配置

  1. 在/usr/local目录下,创建/redis/sentinels/目录

    [root@node0719 local]# mkdir -p redis/sentinels
  2. 在/sentinels目录下,以次创建s1、s2、s3三个子目录中

    [root@node0719 sentinels]# mkdir s1 s2 s3
  3. 依次拷贝redis解压目录下的sentinel.conf文件,到这三个子目录中

    [root@node0719 sentinels]# cp /root/redis-4.0.14/sentinel.conf ./s1/
    [root@node0719 sentinels]# cp /root/redis-4.0.14/sentinel.conf ./s2/
    [root@node0719 sentinels]# cp /root/redis-4.0.14/sentinel.conf ./s3/
  4. 依次修改s1、s2、s3子目录中的sentinel.conf文件,修改端口,并指定要监控的主节点。(从节点不需要指定,sentinel会自动识别)

    • S1哨兵配置如下:

    • S2哨兵配置如下:

    • S3哨兵配置如下:

  5. 再打开三个xshell窗口,在每一个窗口中,启动一个哨兵实例,并观察日志输出

    [root@node0719 sentinels]# redis-sentinel ./s1/sentinel.conf
    [root@node0719 sentinels]# redis-sentinel ./s2/sentinel.conf
    [root@node0719 sentinels]# redis-sentinel ./s3/sentinel.conf

    核心日志输出:

测试

  1. 先关闭6380节点。发现,确实重新指定了一个主节点

  2. 再次上线6380节点。发现,6380节点成为了新的主节点的从节点

结论:哨兵模式能够解决主节点宕机时,从节点变为主节点继续工作。

注意事项

如果哨兵和redis节点不在同一台服务器上,注意IP绑定的问题。

  1. 主从模型,所有的节点,使用ip绑定

  2. 所有的哨兵,也都使用ip去绑定主机

  3. 所有的哨兵,都是通过主节点的ip,去监控主从模型

redis-cluster集群

哨兵模式的缺点:当并发量大的时候,一个master节点也不能够缓解写的压力。

此时,将一个写的压力分配到多台服务器上,这样不就缓解了一台服务器的压力了吗

redis-cluster集群概念

(1)由多个Redis服务器组成的分布式网络服务集群;

(2)集群之中有多个Master主节点,每一个主节点都可读可写;

(3)节点之间会互相通信,两两相连;

(4)Redis集群无中心节点。

集群节点复制

在Redis-Cluster集群中,可以给每一个主节点添加从节点,主节点和从节点直接遵循主从模型的特性。

当用户需要处理更多读请求的时候,添加从节点可以扩展系统的读性能。

故障转移

Redis集群的主节点内置了类似Redis Sentinel的节点故障检测和自动故障转移功能,当集群中的某个主节点下线时,集群中的其他在线主节点会注意到这一点,并对已下线的主节点进行故障转移。

集群进行故障转移的方法和Redis Sentinel进行故障转移的方法基本一样,不同的是,在集群里面,故障转移是由集群中其他在线的主节点负责进行的,所以集群不必另外使用Redis Sentinel。

集群分片策略(了解)

Redis-cluster分片策略,是用来解决key存储位置的。

集群将整个数据库分为16384个槽位slot,所有key-value数据都存储在这些slot中的某一个上。一个slot槽位可以存放多个数据,key的槽位计算公式为:slot_number=crc16(key)%16384,其中crc16为16位的循环冗余校验和函数。

集群中的每个主节点都可以处理0个至16383个槽,当16384个槽都有某个节点在负责处理时,集群进入上线状态,并开始处理客户端发送的数据命令请求。

集群搭建

准备工作

  1. 安装ruby环境

    redis集群管理工具redis-trib.rb依赖ruby环境,首先需要安装ruby环境:

    yum -y install ruby
    yum -y install rubygems
  2. 安装ruby和redis的接口程序

    拷贝redis-3.0.0.gem至/usr/local下,执行安装:

    gem install /usr/local/redis-3.0.0.gem

集群规划

  1. Redis集群最少需要6个节点,可以分布在一台或者多台主机上。

    本教案在一台主机上创建伪分布式集群,不同的端口表示不同的redis节点,如下:

    主节点:192.168.56.3:7001 192.168.56.3:7002 192.168.56.3:7003

    从节点:192.168.56.3:7004 192.168.56.3:7005 192.168.56.3:7006

  2. 在/usr/local/redis下创建redis-cluster目录,其下创建7001、7002。。7006目录,如下:

  3. 将redis解压路径下的配置文件redis.conf,依次拷贝到每个700X目录内,并修改每个700X目录下的redis.conf配置文件:

    必选配置:
    port 700X
    bind 192.168.23.3
    cluster-enabled yes
    建议配置:
    daemonized yes
    logfile /usr/local/redis/redis-cluster/700X/node.log

启动每个结点redis服务

依次以700X下的redis.conf,启动redis节点。(必须指定redis.conf文件)

redis-server /usr/local/redis/redis-cluster/700X/redis.conf

注意:直接从外界进去启动可能失败,需要进入具体的某一个 700x下面去启动节点

执行创建集群命令

进入到redis源码存放目录redis/redis-4.0.14/src下,执行r****edis-trib.rb,此脚本是ruby脚本,它依赖ruby环境。

./redis-trib.rb create --replicas 1 192.168.224.120:7001 192.168.224.120:7002 192.168.224.120:7003 192.168.224.120:7004 192.168.224.120:7005 192.168.224.120:7006
./redis-trib.rb create --replicas 1 1: 一个主节点几个从节点(1个)

创建过程如下:

查询集群信息

集群创建成功登陆任意redis结点查询集群中的节点情况。

redis-cli -c -h 192.168.56.3 -p 7001

说明:redis-cli -c -h 192.168.56.3 -p 7001 ,其中:

-c表示以集群方式连接redis,
-h指定ip地址,
-p指定端口号
cluster nodes 查询集群结点信息;
cluster info 查询集群状态信。

集群管理

添加主节点

节点规划

集群创建成功后可以向集群中添加节点,下面是添加一个master主节点

添加7007节点,参考集群结点规划章节添加一个“7007”目录作为新节点。

添加节点,执行下边命令:

./redis-trib.rb add-node 192.168.224.120:7007 192.168.224.120:7002

查看集群结点发现7007已添加到集群中:

hash槽重新分配

添加完新的主节点后,需要对主节点进行hash槽分配,这样该主节才可以存储数据。

redis集群有16384个槽,被所有的主节点共同分配,通过查看集群结点可以看到槽占用情况。

给刚添加的7007结点分配槽:

第一步:连接上集群

./redis-trib.rb reshard 192.168.23.3:7001(连接集群中任意一个可用节点都行)

第二步:输入要分配的槽数量

输入 500表示要分配500个槽

第三步:输入接收槽的结点id

这里准备给7007分配槽,通过cluster nodes查看7007结点id为79bbb30bba66b4997b9360dd09849c67d2d02bb9

输入:79bbb30bba66b4997b9360dd09849c67d2d02bb9

第四步:输入源结点id

添加从节

集群创建成功后可以向集群中添加节点,下面是添加一个slave从节点。

添加7008从结点,将7008作为7007的从结点。

新增从节点命令格式:

./redis-trib.rb add-node --slave --master-id masterID newNodIP:port MasterIP:port
masterID 主节点id,从cluster nodes信息中查看
newNodIP:port 新增节点的ip:端口
MasterIP:port 主节点的ip:端口

执行如下命令:

./redis-trib.rb add-node --slave --master-id 909c349f5f2d4db015101fb7c4e3c227a74ad382 192.168.4.253:7008 192.168.4.253:7007

79bbb30bba66b4997b9360dd09849c67d2d02bb9 是7007结点的id,可通过cluster nodes查看。

注意:

如果原来该结点在集群中的配置信息已经生成cluster-config-file指定的配置文件中(如果cluster-config-file没有指定则默认为nodes.conf),这时可能会报错:

[ERR] Node XXXXXX is not empty. Either the node already knows other nodes (check with CLUSTER NODES) or contains some key in database 0

解决方法:

删除生成的配置文件nodes.conf,删除后再执行./redis-trib.rb add-node指令。

查看集群中的结点,刚添加的7008为7007的从节点:

删除节点

删除节点命令格式:

./redis-trib.rb del-node nodeIP:port nodeID
nodeIP:port 待删除节点的ip:端口
nodeID 待删除节点的id,从cluster node中查看

注意,删除已经占有hash槽的结点会失败,报错如下:

[ERR] Node 127.0.0.1:7005 is not empty! Reshard data away and try again.

需要将该结点占用的hash槽分配出去(参考hash槽重新分配章节)。

注意事项:设置完源节点后,需要done

java程序连接redis集群

第一步:创建项目,导入jar包

第二步:创建redis集群的客户端

package cn.zj.test;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.junit.Test;
import redis.clients.jedis.HostAndPort;
import redis.clients.jedis.JedisCluster;
public class JedisTest {
static String prefix = "luffi:lbl";
static String KEY_SPLIT = ":"; //用于隔开缓存前缀与缓存键值
@SuppressWarnings("resource")
@Test
public void testJedis() {
Set<HostAndPort> jedisClusterNode = new HashSet<>();
jedisClusterNode.add(new HostAndPort("192.168.6.100", 7002));
jedisClusterNode.add(new HostAndPort("192.168.6.100", 7003));
jedisClusterNode.add(new HostAndPort("192.168.6.100", 7005));
jedisClusterNode.add(new HostAndPort("192.168.6.100", 7006));
jedisClusterNode.add(new HostAndPort("192.168.6.100", 7007));
jedisClusterNode.add(new HostAndPort("192.168.6.100", 7008));
//创建集群连接
JedisCluster jedis = new JedisCluster(jedisClusterNode);
// ==============String类型=================
jedis.set("name", "lihua2");
String name = jedis.get("name");
System.out.println("name : " + name);// 返回OK
// jedis.incr("dz");
// System.out.println(jedis.get("dz"));
jedis.incrBy("dz", 10);
jedis.decr("dz");
// System.out.println(jedis.get("dz"));
// =================List类型=========================
jedis.lpush("boys", "孙悟空", "猪八戒", "唐僧", "白龙马", "沙僧");
List<String> boys = jedis.lrange("boys", 0, -1);
System.out.println(boys.getClass().getName());
System.out.println(boys);
// ==================hash类型========================
jedis.hset("users", "id", "1");
jedis.hset("users", "name", "悟空");
jedis.hset("users", "age", "1000");
String id = jedis.hget("users", "id");
System.out.println(id);
Map<String, String> users = jedis.hgetAll("users");
System.out.println(users.getClass().getName());
System.out.println(users);
// =====================set类型=================================
jedis.sadd("{girls}1", "lili", "lucy", "lihua", "xiaohong");
jedis.sadd("{girls}2", "zhanjun", "lucy", "baibai", "xiaohong");
System.out.println(jedis.smembers("{girls}1"));
Set<String> sinter = jedis.sinter("{girls}1", "{girls}2");
System.out.println(sinter.getClass().getName());
System.out.println(sinter);
Set<String> sunion = jedis.sunion("{girls}1","{girls}2");
// System.out.println(sunion);
// ========================sortedSet类型===============
jedis.zadd("fuirt", 1.0, "西瓜");
jedis.zadd("fuirt", 2.0, "蜜桃");
jedis.zadd("fuirt", 2.5, "香蕉");
jedis.zadd("fuirt", 3.0, "哈密瓜");
jedis.zadd("fuirt", 10.0, "娃娃菜");
Set<String> fuirt = jedis.zrangeByScore("fuirt", 1.0, 2.0);
System.out.println(fuirt.getClass().getName());
System.out.println(fuirt);
}
}

注意事项:

连接Redis集群时,需要修改防火墙,开方每一个redis节点的端口

说明:如果要开发一个范围的端口,可以使用冒号来分割,即: 7001:7008,表示开发7001-7008之间所有的端口

测试:

出现如下则成功。

posted @   站着说话不腰疼  阅读(191)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通
点击右上角即可分享
微信分享提示