SpringCloud Redis与分布式

张开发
2026/5/21 11:58:24 15 分钟阅读
SpringCloud Redis与分布式
Redis与分布式Redis是一个基于内存的高性能数据库主从复制主从复制将一台Redis服务器的数据复制到其他Redis服务器前者是主节点Master后者是从节点Slave,数据的复制是单向的只能从主节点到从节点。Master以写为主Slave以读为主。这样的好处有实现读写分离提高性能在写少读多的情况下可以安排多个从节点这样能大幅度分担压力就算挂掉一个其他的也能用具体的实施步骤如下先修改master的redis.windows.conf文件把端口为6001slave的端口为6002# Accept connections on the specified port, default is 6379 (IANA #815344). # If port 0 is specified Redis will not listen on a TCP socket. port 6001启动redis命令如下redis-server.exe redis.windows.conf使用info replication 命令可以查看主从状态。启动客户端后可以使用命令查看将6002设置为从节点先使用客户端连上6002 再输入命令 replicaof 127.0.0.1 6001 命令后就会将6001服务器作为主节点当前节点就是从节点可以看到6002的角色是slave。从新连接6001再查看状态此时6001和6002已经形成了主从关系这里有一个偏移量反应的是从节点的同步情况主节点和从节点都会维护一个复制偏移量主节点每次向从节点传递N个字节的时候会将自己的复制偏移量N.从节点收到N个字节的数据后就会将自己的复制偏移量N.通过2个偏移量的对比可以知道主从节点的数据是否一致如果不一致就需要进行增量同步主节点可以读写。从节点只能读当有新的从节点加入时会马上同步主节点的数据当节点关闭后从节点也可以读取数据毕竟从节点没关闭整个同步流程如下从节点执行replicaof 【ip】 [port] 命令后从节点会保存主节点的地址信息从节点通过每秒允许的定时任务发现新的主节点后会尝试与该节点建立网络连接专门接收主节点发送的复制命令连接成功后第一次会将主节点的数据进行全量复制之后采用增量复制持续将新来的写命令同步给从节点怎么解除主从模式客户端连接6002 输入指令replicaof no one 就可以了也可以直接在配置文件中配置主从模式在从节点的redis.windows.conf 文件中加入replicaof 127.0.0.1 6001 内容就可以了除了作为主节点的从节点外也可以作为从节点的从节点比如增加一个6003 作为6002 的从节点操作方式和上面一样这两种主从方式的区别如下但是第二种方式一旦传播链路中出现问题就会导致后面的从节点无法及时同步数据哨兵模式上面的主从复制模式一旦主节点故障就会导致整个系统崩溃要是能够监控到主节点的允许状况就能够采取补救措施这时就要用到哨兵模式。哨兵会对所有的节点进行监控如果发现主节点故障会立即让从节点进行投票选举一个新的主节点出来这样就不会由于主节点故障而导致系统崩溃要实现这样的功能最少必须时一主一从模式再小就没有意义了。如何启动一个哨兵修改一下配置文件先删除全部内容然后添加sentinel monitor ali 127.0.0.1 6001 1第一个和第二个时固定第三个时监控对象名称可以随意命名后面就是主节点的信息ip和端口最后的1 后面说明然后启动服务器redis-server.exe redis.windows.conf --sentinel哨兵模式启动后会字段监控主节点还会显示哪些节点是从节点。此时关闭主节点后过一段时间哨兵会将一个从节点选举为主节点而挂掉的主节点会设置为从节点当挂掉的节点启动后就当成从节点使用了。那么从节点见的选举规则是什么呢首先会根据优先级进行选择可以在配置文件中设置添加replica-priority配置项默认100值越小优先级越高如果优先级一样选择偏移量大的要是还选不出来那就选择runid启动时随机生成的最小的。那要是哨兵挂了怎么办可以多设置几个哨兵。只需要将哨兵的配置复制一下然后修改端口号。就可以同时启动多个哨兵只不过要把最后一个值改为2比如sentinel monitor ali 127.0.0.1 6001 2 port 2002这个值代表的额是当有几个哨兵认为主节点挂掉时就判断主节点真的挂掉了。那再哨兵重新选举新的主节点后Java中的redis客户端怎么感知到呢先引入依赖dependency groupIdredis.clients/groupId artifactIdjedis/artifactId version4.2.1/version /dependencypublic class Main { public static void main(String[] args) { // 直接使用JedisSentinelPool获取Master节点 // 填入三个哨兵地址注意如果连不上就把哨兵的配置文件中的protected-mode属性改为no JedisSentinelPool pool new JedisSentinelPool(ali, new HashSet(Arrays.asList( 127.0.0.1:2001, 127.0.0.1:2002, 127.0.0.1:2003 ))); // 询问并得到Jedis对象就是master节点的连接 Jedis jedis pool.getResource(); // 向master节点写入数据 jedis.set(test, 114514); // 再次获取Jedis对象得到的也是master节点的连接 Jedis jedis2 pool.getResource(); // 读取数据验证是否成功 System.out.println(jedis2.get(test)); } }集群搭建如果服务器的内存不够用但是redis需要继续存储内容这时候就需要使用集群来实现扩容。此时面对一个写入请求数据该写到哪个节点上呢我们需要先明白集群的机制一个redis集群包含16384个插槽集群中的每个热覅是维护一部分插槽以及插槽送映射的键值对数据那这个插槽到底是什么呢插槽就是Hash计算后的一个结果这里采用CRC16循环冗余校验得到16个bit位的数据就是说算出来的结果是0-65535之间再进行取模得到最终结果Redis key 的路由计算公式slot CRC(Key) % 16384结果是多少就存放在对应的redis下比如redis节点1负责0-25565的插槽这时客户端插入了一个数据a10.a在hash计算后结果为666.那么a就应该存到节点1。本质上就是通过哈希算法将如数分摊到各个节点搭建redis集群步骤如下这里创建6个配置先在配置文件中开启集群模式注意修改端口号cluster-enabled yes然后输入redis-cli.exe --cluster create --cluster-replicas 1 127.0.0.1:6001 127.0.0.1:6002 127.0.0.1:6003 127.0.0.1:7001 127.0.0.1:7002 127.0.0.1:70036001 6002 6003 这三个是三个主节点。7001 7002 7003 分别是三个从节点这里的--cluster-replicas 1 指的是每个节点配一个从节点输入yes执行此时集群搭建成功此时在保存数据时可以使用集群方式连接这样无论在哪个节点插入数据都可以成功只需要添加 -c 表示以集群模式连接即可redis-cli.exe -p 6001 -cset a 888就可以保存成功。此时可以输入cluster nodes来查看当前所有节点信息当某一个主节点发生故障后这个主节点的从节点就会升级为新的主节点之后当故障节点恢复后会变为当前主节点的从节点如果某个节点的主从redis都挂掉了呢那当前节点就不可用了无法在此节点插入数据当他们都恢复后就可以正常使用了。如何使用Java连接到集群模式下的redis需要用到JedisCluster对象// 和客户端一样随便连一个就行也可以多写几个 JedisCluster cluster new JedisCluster(new HostAndPort(127.0.0.1,6001)); System.out.println(集群实例数量cluster.getClusterNodes().size()); cluster.set(test, 114514); System.out.println(cluster.get(test));分布式锁redis实现分布式锁可以使用setnx key value 命令。意思是只有当指定的key不存在的时候才能进行插入实际上就是set if not exists比如setnx a 999 ,然后再输入setnx a 100 就会报错因为已经存在key是a 的数据了利用这种特定就可以在不同的服务中实现分布式锁但是当某个服务加了锁但是卡顿或者崩溃了那这个锁岂不是永远无法释放了此时可以加上过期时间set a 666 EX 5 NX这里使用的set命令。最后的NX表示使用setnx模式和上面一样的效果。通过EX设定过期时间这里设为5秒表明超过5秒还没释放就会自动删除锁虽然添加过期时间解决了上面的问题但是也带来了很多麻烦比如下面这种情况因此单纯的加过期时间会把别人加的锁给删除要解决这种问题很简单。现在的目标是保证任务只能删除自己加的锁所以可以把a的值设定为任务专属值比如使用uuid。如果在主动删除锁的时候发现值不是我们当前任务指定的那么说明可能是因为超时其他任务已经加锁了此时就不能删除锁了。此时还会有一个问题如果在超时之前那一刹那进入到释放锁阶段获取的值还是自己的但是在即将执行删除之前由于超时机制导致被删除并且其他任务也枷锁了这时再进行删除仍会导致删除其他任务加的锁本质还是因为锁的超时时间不好衡量如果超时时间能够设定恰当就可以避免这种问题了。这时就需要Redisson框架它是Redis官方推荐的Java版的Redis客户端。Redisson内部提供了一个监控锁的看门狗作用是再Redisson实例被关闭前不断的延长锁的有效期使用步骤如下先引入依赖dependency groupIdorg.redisson/groupId artifactIdredisson/artifactId version3.5.0/version /dependency dependency groupIdio.netty/groupId artifactIdnetty-all/artifactId /dependency编写代码public static void main(String[] args) { Config config new Config(); // 连接单机模式的Redis服务器也可以指定集群 config.useSingleServer().setAddress(redis://127.0.0.1:6379); // 创建RedissonClient实例内部会创建连接池 RedissonClient redisson Redisson.create(config); for (int i 0; i 10; i) { new Thread(()- { Jedis jedis new Jedis(127.0.0.1, 6379); // 指定锁名称拿到锁对象 RLock lock redisson.getLock(testLock); for (int j 0; j 100; j) { lock.lock(); // 加锁 int a Integer.parseInt(jedis.get(a)) 1; jedis.set(a, String.valueOf(a)); lock.unlock(); // 解锁 } }).start(); } }

更多文章