• 分布式锁故障转移问题


    背景:基于redis实现分布式锁。客户端:lettuce-core-5.1.7.RELEASE。连接池:commons-pool2-2.6.2

    单点与故障转移问题

    单点问题

    很简单,单机的服务,如果服务器宕机,那么服务处于不可用状态,如何解决?增加备份,master- slave模式,master宕机后,slave升级master进行服务

    故障转移问题

    单点问题解决后也引入了故障转移问题。如果master宕机时,数据还没有同步至slave怎么办?

    分布式锁也同样需要解决此类问题,如果master宕机slave未完成master数据的同步,会导致两台机器同时获取到同一把分布式锁

    解决方案

    主从同步模式

    简单讲就是写入主库的同时,等待主库数据同步至从库后再响应客户端,但是牺牲的是可用性。互联网时代,如果一主一从,那么代价就是任何一台机器有问题都会导致服务不可用,如果增加slave机器,则可用性也会降低

    优势

    1. 客户端代码逻辑简单,服务端层面保障双写一致性

    劣势

    1. rt翻倍,每增加一个深度(水平扩展不影响)链路节点就代表请求的rt要增加一个节点请求rt

    redlock

    redis不再采用master-slave模式,而是集群模式,假设有3台机器,同时向三台机器申请分布式锁。如果有两台机器响应成功则代表请求成功,类似于选举算法的过半原则。既保障了高可用性,也解决了故障转移问题

    redlock其实就是“双写”方案的变形,等同于:多写+过半机制

    优势

    1. 不存在主从同步问题

    劣势

    1. 成本高,3台机器只能允许一台机器宕机

    代码实现

    主从同步模式

    环境背景:集群分片+master-slave模式
    底层实现使用的是redis的wait命令
    wait命令的作用:
    此命令将阻塞当前客户端,直到当前Session连接(master节点上)所有的写命令都被传送到指定数据量的slave节点。
    如果到达超时(以毫秒为单位),则即使尚未完全传送到达指定数量的salve节点,该命令也会返回成功传送到的节点的个数。

    private boolean doClusterLock(String lock, String resource, String sign, long maxWaitTime, long maxLockDuration) {
        boolean masterSuccess = false;
        boolean rollback = false;
        long getPoolStartTime = System.currentTimeMillis();
        try (StatefulConnection<String, String> conn = genericObjectPool.borrowObject()) {
            long getPoolEndTime = System.currentTimeMillis();
            logger.debug("the lock {} get connection time is : {}", lock, (getPoolEndTime - getPoolStartTime));
            StatefulRedisClusterConnection<String, String> clusterConn = (StatefulRedisClusterConnection<String, String>) conn;
            long beginTime = System.currentTimeMillis();
            
            // 1. 获取redis key所在集群分片的master节点id
            int keySlot = SlotHash.getSlot(resource);
            String masterId = redisClusterClient.getPartitions().getPartitionBySlot(keySlot).getNodeId();
            // 2. 操作redis master节点,获取分布式锁
            RedisCommands<String, String> masterCmd = clusterConn.getConnection(masterId).sync();
            String masterResult = masterCmd.set(resource, sign, SetArgs.Builder.nx().px(maxLockDuration));
            masterSuccess = "OK".equals(masterResult);
            if (!masterSuccess) {
                return false;
            }
            
            // 3. 等待master节点同步数据至slave节点
            Map<RedisClusterNode, RedisAsyncCommands<String, String>> slaveNodes =
                clusterConn.async()
                .slaves(node -> masterId.equals(node.getSlaveOf()))
                .asMap();
            int slaveCount = slaveNodes.size();
            if (slaveCount > 0) {
                int minReplicaCount = Math.min(slaveCount, MIN_REPLICA_COUNT);
                masterCmd.waitForReplication(minReplicaCount, maxWaitTime);
            }
            ...
    }
    
    • 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
  • 相关阅读:
    java8中LocalDateTime解析日期的示例分享
    【面试必刷TOP101】二分查找-I & 二维数组中的查找
    SpringMVC基础:AJAX发送请求
    越小越好: Q8-Chat,在英特尔至强 CPU 上体验高效的生成式 AI
    推荐一款语音识别软件
    JAVA大学生活动中心场地管理系统计算机毕业设计Mybatis+系统+数据库+调试部署
    Linux运维基础知识大全
    MongoDb-01——Mac上安装MongoDb以及相关的简单命令
    动力节点索引优化解决方案学习笔记——索引介绍
    LeetCode·20.有效的括号·栈模拟
  • 原文地址:https://blog.csdn.net/u010597819/article/details/126200963