服务器之家:专注于VPS、云服务器配置技术及软件下载分享
分类导航

Mysql|Sql Server|Oracle|Redis|MongoDB|PostgreSQL|Sqlite|DB2|mariadb|Access|数据库技术|

服务器之家 - 数据库 - Redis - Redisson 主从一致性问题详解

Redisson 主从一致性问题详解

2022-08-26 15:14ruochen Redis

这篇文章主要为大家介绍了Redisson 主从一致性问题详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪

Redisson 主从一致性

  • 我们先来说一下 Redis 的主从模式,Redis Master(主节点)中处理所有发向 Redis 的写操作(增删改),Redis Slave (从节点)只负责处理读操作,主节点会不断将自己的数据同步给从节点,确保主从之间的数据一致性,但是数据同步会存在一定的延时,主从一致性问题就是因为延时而导致的
  • 比如我们通过 set lock thread1 nx ex 10 来获取锁,主节点就会保存这个锁的标识 thread1,然后主节点会向从节点进行同步,但在同步尚未完成时时主节点发生故障,Redis 哨兵发现主节点宕机后,客户端连接会断开,然后从从节点中选出一个作为新的主节点,但是由于之前主从同步未完成,即 thread1 这个锁已经丢失,所以此时 Java 应用再来访问新的主节点时就会发现锁失效了,此时其他线程来获取锁时也能获取成功,这时就可能出现并发安全问题,以上就是主从一致性导致的锁失效问题
  • 那么 Redisson 是如何解决上述问题的呢?既然导致主从一致性问题发生的主要原因是主从同步延时问题,Redisson 干脆直接舍弃了主从节点,所有 Redis 节点都是独立的节点,相互之间无任何关系,都可以做读写操作。此时,我们想获取锁就必须依次向多个 Redis 都去获取锁(之前直接向 Master 节点获取就可以),多个 Redis 节点都保存锁的标识,才算获取成功
  • 这样一来,由于所有节点都是独立的,所以避免了主从一致性问题;又由于所有的节点都保存了锁标识,即使由一个节点宕机,其他的节点也保存有锁的标识,保证了高可用,并且可用性会随着节点的增多而增高
  • 此外,我们还以为给这些独立的节点再加上从节点 Slave,即使一个独立节点宕机了导致其对应的从节点变成新的主节点,且节点上锁标识丢失了也没有关系,因为我们只有在每一个节点都拿到锁才算成功, 尽管可以在这个空虚的节点上获取到锁,但在其他节点上是获取不到的,最终仍然是失败,因此只要有任意一个节点存货,其他线程就不可能拿到锁,就不会出现锁失效问题。这样,既保留了主从同步机制,又确保了 Redis 集群的高可用特性,同时还避免了主从一致所引发的锁失效问题,这个方案就叫做 mutilLock

Java 实现 mutilLock

RedissonConfig.java

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
package com.hmdp.config;
import org.redisson.Redisson;
import org.redisson.api.RedissonClient;
import org.redisson.config.Config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class RedissonConfig {
    @Bean
    public RedissonClient redissonClient() {
        // 配置
        Config config = new Config();
        // 地址 & 密码
        config.useSingleServer().setAddress("redis://ip:端口").setPassword("pwd");
        // 创建 RedissonClient 对象
        return Redisson.create(config);
    }
    @Bean
    public RedissonClient redissonClient2() {
        // 配置
        Config config = new Config();
        // 地址 & 密码
        config.useSingleServer().setAddress("redis://ip:端口").setPassword("pwd");
        // 创建 RedissonClient 对象
        return Redisson.create(config);
    }
    @Bean
    public RedissonClient redissonClient3() {
        // 配置
        Config config = new Config();
        // 地址 & 密码
        config.useSingleServer().setAddress("redis://ip:端口").setPassword("pwd");
        // 创建 RedissonClient 对象
        return Redisson.create(config);
    }
}

TestRedisson.java

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
package com.hmdp;
import lombok.extern.slf4j.Slf4j;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.boot.test.context.SpringBootTest;
import javax.annotation.Resource;
import java.util.concurrent.TimeUnit;
@Slf4j
@SpringBootTest
public class TestRedisson {
    @Resource
    private RedissonClient redissonClient;
    @Resource
    private RedissonClient redissonClient2;
    @Resource
    private RedissonClient redissonClient3;
    private RLock lock;
    @BeforeEach
    void setUp() {
        RLock lock1 = redissonClient.getLock("order");
        RLock lock2 = redissonClient2.getLock("order");
        RLock lock3 = redissonClient3.getLock("order");
        // 创建连锁 multiLock
        lock = redissonClient.getMultiLock(lock1, lock2, lock3);
    }
    @Test
    void method1() throws InterruptedException {
        // 尝试获取锁
        boolean isLock = lock.tryLock(1L, TimeUnit.SECONDS);
        if (!isLock) {
            log.error("获取锁失败 .... 1");
            return;
        }
        try {
            log.info("获取锁成功 .... 1");
            method2();
            log.info("开始执行业务 .... 1");
        } finally {
            log.warn("准备释放锁 .... 1");
            lock.unlock();
        }
    }
    void method2() {
        // 尝试获取锁
        boolean isLock = lock.tryLock();
        if (!isLock) {
            log.error("获取锁失败 .... 2");
            return;
        }
        try {
            log.info("获取锁成功 .... 2");
            log.info("开始执行业务 .... 2");
        } finally {
            log.warn("准备释放锁 .... 2");
            lock.unlock();
        }
    }
}

跟踪源码,我们发现只有所有的锁都获取成功了才会返回 true

Redisson 主从一致性问题详解

以上就是Redisson 主从一致性问题详解的详细内容,更多关于Redisson 主从一致的资料请关注服务器之家其它相关文章!

原文链接:https://juejin.cn/post/7135613701951848461

延伸 · 阅读

精彩推荐
  • Redisredis中如何使用lua脚本让你的灵活性提高5个逼格详解

    redis中如何使用lua脚本让你的灵活性提高5个逼格详解

    这篇文章主要给大家介绍了关于redis中如何使用lua脚本让你的灵活性提高5个逼格的相关资料,文中通过示例代码介绍的非常详细,对大家的学习或者工作具...

    一线码农5262019-11-18
  • Redis面试常问,工作常用的Redis持久化机制

    面试常问,工作常用的Redis持久化机制

    redis是一个内存数据库,数据保存在内存中,但是我们都知道内存的数据变化是很快的,也容易发生丢失。幸好Redis还为我们提供了持久化的机制,分别是...

    愚公要移山11862021-03-16
  • RedisRedis字符串原理的深入理解

    Redis字符串原理的深入理解

    这篇文章主要给大家介绍了关于Redis字符串原理的相关资料,文中通过示例代码介绍的非常详细,对大家学习或者使用Redis具有一定的参考学习价值,需要的...

    YangSir3052019-11-24
  • Redis如何利用Redis分布式锁实现控制并发操作

    如何利用Redis分布式锁实现控制并发操作

    这篇文章主要介绍了如何利用Redis分布式锁实现控制并发操作,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧...

    拉里·佩奇1812020-12-20
  • Redis关于SpringBoot 使用 Redis 分布式锁解决并发问题

    关于SpringBoot 使用 Redis 分布式锁解决并发问题

    针对上面问题,一般的解决方案是使用分布式锁来解决,本文通过场景分析给大家介绍关于SpringBoot 使用 Redis 分布式锁解决并发问题,感兴趣的朋友一起看...

    qhh02058202021-11-24
  • RedisRedis 不使用 keys 命令获取键值信息的方法

    Redis 不使用 keys 命令获取键值信息的方法

    这篇文章主要介绍了Redis 不使用 keys 命令获取键值信息的相关知识,非常不错,具有一定的参考借鉴价值,需要的朋友参考下吧 ...

    JenningLang3822019-11-15
  • Redisredis 替代php文件存储session的实例

    redis 替代php文件存储session的实例

    这篇文章主要介绍了redis 替代php文件存储session的实例的相关资料,希望通过本文能帮助到大家,让大家掌握这样的方法,需要的朋友可以参考下 ...

    项象多5502019-11-10
  • RedisRedis数据库的安装配置方法

    Redis数据库的安装配置方法

    redis 是一个高性能的key-value数据库。 redis的出现,很大程度补偿了memcached这类keyvalue存储的不足,在部 分场合可以对关系数据库起到很好的补充作用。它提...

    mdxy-dxy3312019-10-20