Is the ultimate solution for distributed locks RedLock? Why?

RedLock is an implementation solution of Redis distributed lock, proposed by Salvatore Sanfilippo, the author of Redis.

The RedLock algorithm is designed to solve the single point of failure problem that may occur when a single Redis instance serves as a distributed lock, and improves the availability and security of the lock service by acquiring locks simultaneously on multiple independently running Redis instances.

1. Implement ideas

RedLock locks each node in the cluster. If the majority of nodes (N/2+1) are successfully locked, the lock will be considered successful. In this way, even if a node in the cluster dies, the distributed lock can still be used because most of the cluster nodes have been successfully locked.

2. Implement the code

In Java development, you can use the Redisson framework to easily implement RedLock. The specific operation code is as follows:

import org.redisson.Redisson;
import org.redisson.api.RedisClient;
import org.redisson.api.RedissonClient;
import org.redisson.config.Config;
import org.redisson.redisson.RedissonRedLock;

public class RedLockDemo {

    public static void main(String[] args) {
        // 创建 Redisson 客户端配置
        Config config = new Config();
        config.useClusterServers()
        .addNodeAddress("redis://127.0.0.1:6379",
                        "redis://127.0.0.1:6380",
                        "redis://127.0.0.1:6381"); // 假设有三个 Redis 节点
        // 创建 Redisson 客户端实例
        RedissonClient redissonClient = Redisson.create(config);
        // 创建 RedLock 对象
        RedissonRedLock redLock = redissonClient.getRedLock("resource");
        try {
            // 尝试获取分布式锁,最多尝试 5 秒获取锁,并且锁的有效期为 5000 毫秒
            boolean lockAcquired = redLock.tryLock(5, 5000, TimeUnit.MILLISECONDS); 
            if (lockAcquired) {
                // 加锁成功,执行业务代码...
            } else {
                System.out.println("Failed to acquire the lock!");
            }
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
            System.err.println("Interrupted while acquiring the lock");
        } finally {
            // 无论是否成功获取到锁,在业务逻辑结束后都要释放锁
            if (redLock.isLocked()) {
                redLock.unlock();
            }
            // 关闭 Redisson 客户端连接
            redissonClient.shutdown();
        }
    }
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • twenty one.
  • twenty two.
  • twenty three.
  • twenty four.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.
  • 33.
  • 34.
  • 35.
  • 36.
  • 37.
  • 38.
  • 39.
  • 40.

3. Implementation principle

RedLock in Redisson is implemented based on RedissonMultiLock (interlocking).

RedissonMultiLock is a distributed lock type provided by Redisson. It can operate multiple locks at the same time to achieve unified management of multiple locks. The interlocking operation is atomic, that is, either all locks or all unlocks. This ensures the consistency of multiple locks.

Examples of RedissonMultiLock usage are as follows:

import org.redisson.Redisson;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.redisson.config.Config;
import org.redisson.multi.MultiLock;

public class RedissonMultiLockDemo {

    public static void main(String[] args) throws InterruptedException {
        // 创建 Redisson 客户端
        Config config = new Config();
        config.useSingleServer().setAddress("redis://127.0.0.1:6379");
        RedissonClient redisson = Redisson.create(config);

        // 创建多个分布式锁实例
        RLock lock1 = redisson.getLock("lock1");
        RLock lock2 = redisson.getLock("lock2");
        RLock lock3 = redisson.getLock("lock3");

        // 创建 RedissonMultiLock 对象
        MultiLock multiLock = new MultiLock(lock1, lock2, lock3);

        // 加锁
        multiLock.lock();
        try {
            // 执行任务
            System.out.println("Lock acquired. Task started.");
            Thread.sleep(3000);
            System.out.println("Task finished. Releasing the lock.");
        } finally {
            // 释放锁
            multiLock.unlock();
        }
        // 关闭客户端连接
        redisson.shutdown();
    }
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • twenty one.
  • twenty two.
  • twenty three.
  • twenty four.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.
  • 33.
  • 34.
  • 35.
  • 36.
  • 37.

In the example, we first create a Redisson client and connect to the Redis server. Then, we created multiple distributed lock instances using the redisson.getLock method. Next, we created the RedissonMultiLock object by passing in these lock instances.

Back to the topic, RedissonRedLock is implemented based on RedissonMultiLock, which can be seen from the inheritance relationship.

RedissonRedLock inherits from RedissonMultiLock. The core implementation source code is as follows:

public class RedissonRedLock extends RedissonMultiLock {
    public RedissonRedLock(RLock... locks) {
        super(locks);
    }

    /**
     * 锁可以失败的次数,锁的数量-锁成功客户端最小的数量
     */
    @Override
    protected int failedLocksLimit() {
        return locks.size() - minLocksAmount(locks);
    }

    /**
     * 锁的数量 / 2 + 1,例如有3个客户端加锁,那么最少需要2个客户端加锁成功
     */
    protected int minLocksAmount(final List<RLock> locks) {
        return locks.size()/2 + 1;
    }

    /** 
     * 计算多个客户端一起加锁的超时时间,每个客户端的等待时间
     */
    @Override
    protected long calcLockWaitTime(long remainTime) {
        return Math.max(remainTime / locks.size(), 1);
    }

    @Override
    public void unlock() {
        unlockInner(locks);
    }
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • twenty one.
  • twenty two.
  • twenty three.
  • twenty four.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.
  • 33.

As can be seen from the above source code, RedLock in Redisson is implemented based on RedissonMultiLock (interlocking). When RedLock locks each node in the cluster, if most nodes, that is, N/2+1 nodes, are locked If successful, it is considered that the RedLock lock is successful.

4. There are problems

RedLock mainly has the following two problems:

  • Performance issues : RedLock must wait for the majority of nodes to return before locking can be successful. During this process, network problems or node timeouts may affect locking performance.
  • Concurrency security issues : When the client locks, if it encounters GC, the lock may fail, but after GC, it is mistakenly believed that the lock was successful, such as the following process:
  1. Client A requests 3 nodes for locking.
  2. Before node reply processing, client A enters the GC phase (STW exists, global pause).
  3. Later, due to the locking time, the lock has expired.
  4. Client B requested a lock (the same lock as client A), and the lock was successful.
  5. Client A GC is completed and continues to process the messages of the previous node, mistakenly thinking that the lock is successful.
  6. At this time, client B and client A are successfully locked at the same time, causing concurrency security issues.

5. RedLock has been abandoned

Because the problems of RedLock are quite controversial and there is no perfect solution, RedLock has been abandoned in Redisson. This can be found in the official Redisson documentation, as shown in the figure below:

6. Solution after abandoning RedLock

Although RedLock has been abandoned in Redisson, you can directly use ordinary locks in Redisson, because its ordinary locks are based on the wait mechanism, waiting for the lock to synchronize information to the slave node, thereby ensuring data consistency. Although Data consistency problems cannot be completely avoided, but data consistency can be guaranteed to the greatest extent.

Thoughts after class

Since ordinary distributed locks have a single point of problem? And RedLock is not the most perfect solution, so in the field of distributed locks, who is the final solution? Please leave your solution and corresponding reasons in the comment area?