Redis(74)Redis分布式锁与ZooKeeper分布式锁有何区别?

Redis和ZooKeeper都是用来实现分布式锁的常用工具,但它们在实现机制、性能、可用性和一致性方面有显著区别。以下是详细的比较和结合代码的解释。

1. 实现机制

Redis分布式锁:

  • 实现机制 :基于简单的字符串操作,通过SET NX PX命令来实现锁,使用Lua脚本确保原子性。Redis是一个内存数据库,操作速度非常快。
  • 一致性:Redis提供的是最终一致性,不能完全保证严格的强一致性。
  • 可用性:Redis是AP系统(高可用性和分区容错性),可能在网络分区时提供较高的可用性,但在一致性方面有所妥协。

ZooKeeper分布式锁:

  • 实现机制:基于ZNode(ZooKeeper节点)来实现锁,通过创建临时顺序节点来竞争锁。ZooKeeper依赖于ZAB协议(ZooKeeper Atomic Broadcast),确保严格的一致性。
  • 一致性:ZooKeeper提供的是强一致性,保证严格的顺序一致性。
  • 可用性:ZooKeeper是CP系统(强一致性和分区容错性),在网络分区时可能会牺牲部分可用性来保证一致性。

2. 性能

  • Redis:由于是内存数据库,操作非常快,适合高性能、高并发的场景。
  • ZooKeeper:性能较Redis稍逊,但在一致性和可靠性方面更强,适合需要严格一致性的场景。

3. 可用性和一致性

  • Redis:在网络分区时可能会牺牲一致性来保证可用性,属于AP系统。
  • ZooKeeper:在网络分区时可能会不可用来保证一致性,属于CP系统。

4. 示例代码

Redis分布式锁代码示例

java 复制代码
import redis.clients.jedis.Jedis;
import redis.clients.jedis.params.SetParams;

public class RedisDistributedLock {
    private Jedis jedis;
    private String lockKey;
    private String lockValue;
    private int expireTime;

    public RedisDistributedLock(Jedis jedis, String lockKey, int expireTime) {
        this.jedis = jedis;
        this.lockKey = lockKey;
        this.expireTime = expireTime;
        this.lockValue = String.valueOf(Thread.currentThread().getId());
    }

    public boolean acquireLock() {
        SetParams params = new SetParams().nx().px(expireTime);
        String result = jedis.set(lockKey, lockValue, params);
        return "OK".equals(result);
    }

    public boolean releaseLock() {
        String luaScript = "if redis.call('get', KEYS[1]) == ARGV[1] then " +
                           "return redis.call('del', KEYS[1]) else return 0 end";
        Object result = jedis.eval(luaScript, 1, lockKey, lockValue);
        return result.equals(1L);
    }

    public void executeWithLock(Runnable task) {
        if (acquireLock()) {
            try {
                task.run();
            } finally {
                boolean released = releaseLock();
                if (!released) {
                    System.out.println("Failed to release lock.");
                }
            }
        } else {
            System.out.println("Failed to acquire lock, try again later.");
        }
    }

    public static void main(String[] args) {
        Jedis jedis = new Jedis("localhost", 6379);
        RedisDistributedLock lock = new RedisDistributedLock(jedis, "resource_lock", 10000);

        lock.executeWithLock(() -> {
            System.out.println("Executing critical section.");
            // Simulate long running task
            try {
                Thread.sleep(8000);
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            }
            System.out.println("Critical section completed.");
        });

        jedis.close();
    }
}

ZooKeeper分布式锁代码示例

java 复制代码
import org.apache.zookeeper.*;
import org.apache.zookeeper.data.Stat;

import java.io.IOException;

public class ZookeeperDistributedLock implements Watcher {
    private ZooKeeper zooKeeper;
    private String lockName;
    private String lockPath;
    private String currentLockNode;

    public ZookeeperDistributedLock(String hostPort, String lockName) throws IOException {
        this.zooKeeper = new ZooKeeper(hostPort, 3000, this);
        this.lockName = lockName;
        this.lockPath = "/locks/" + lockName;
        try {
            Stat stat = zooKeeper.exists("/locks", false);
            if (stat == null) {
                zooKeeper.create("/locks", new byte[0], ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
            }
        } catch (KeeperException | InterruptedException e) {
            e.printStackTrace();
        }
    }

    public boolean acquireLock() {
        try {
            currentLockNode = zooKeeper.create(lockPath + "/lock_", new byte[0],
                    ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL_SEQUENTIAL);
            while (true) {
                Stat predecessorStat = null;
                String predecessorPath = null;
                for (String node : zooKeeper.getChildren(lockPath, false)) {
                    String fullPath = lockPath + "/" + node;
                    if (currentLockNode.compareTo(fullPath) > 0) {
                        if (predecessorStat == null || fullPath.compareTo(predecessorPath) > 0) {
                            predecessorStat = zooKeeper.exists(fullPath, true);
                            predecessorPath = fullPath;
                        }
                    }
                }
                if (predecessorStat == null) {
                    return true;
                }
                synchronized (this) {
                    wait();
                }
            }
        } catch (KeeperException | InterruptedException e) {
            e.printStackTrace();
            return false;
        }
    }

    public boolean releaseLock() {
        try {
            zooKeeper.delete(currentLockNode, -1);
            return true;
        } catch (KeeperException | InterruptedException e) {
            e.printStackTrace();
            return false;
        }
    }

    public void executeWithLock(Runnable task) {
        if (acquireLock()) {
            try {
                task.run();
            } finally {
                boolean released = releaseLock();
                if (!released) {
                    System.out.println("Failed to release lock.");
                }
            }
        } else {
            System.out.println("Failed to acquire lock, try again later.");
        }
    }

    @Override
    public void process(WatchedEvent event) {
        if (event.getType() == Event.EventType.NodeDeleted) {
            synchronized (this) {
                notifyAll();
            }
        }
    }

    public static void main(String[] args) throws IOException {
        ZookeeperDistributedLock lock = new ZookeeperDistributedLock("localhost:2181", "resource_lock");

        lock.executeWithLock(() -> {
            System.out.println("Executing critical section.");
            // Simulate long running task
            try {
                Thread.sleep(8000);
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            }
            System.out.println("Critical section completed.");
        });
    }
}

代码详解

Redis分布式锁代码详解

  1. 锁的获取与释放

    • acquireLock()方法使用NXPX参数设置锁的唯一性和过期时间,确保锁的原子性和自动过期。
    • releaseLock()方法使用Lua脚本确保释放锁的原子性,避免误删其他线程持有的锁。
  2. 执行带锁的任务

    • executeWithLock(Runnable task)方法封装了锁的获取、任务执行和锁的释放逻辑,确保任务在锁的保护下执行。

ZooKeeper分布式锁代码详解

  1. 锁的获取与释放

    • acquireLock()方法通过创建临时顺序节点来竞争锁,并监听前一个节点的删除事件来确保锁的公平性。
    • releaseLock()方法删除当前的临时顺序节点,释放锁。
  2. 执行带锁的任务

    • executeWithLock(Runnable task)方法封装了锁的获取、任务执行和锁的释放逻辑,确保任务在锁的保护下执行。
  3. 事件处理

    • process(WatchedEvent event)方法处理节点删除事件,确保在前一个节点释放锁后,当前节点能及时获取锁。

总结来说,Redis和ZooKeeper都有各自的优势和适用场景。Redis适合高性能、高并发的场景,ZooKeeper适合需要严格一致性的场景。选择哪一种锁机制,主要取决于具体的应用需求和系统架构。

相关推荐
风象南3 小时前
SpringBoot “分身术”:同时监听多个端口
后端
Victor3564 小时前
Redis(75)Redis分布式锁的性能如何优化?
后端
JaguarJack4 小时前
PHP 8.5 新特性 闭包可以作为常量表达式了
后端·php
毕业设计制作和分享7 小时前
springboot150基于springboot的贸易行业crm系统
java·vue.js·spring boot·后端·毕业设计·mybatis
你的人类朋友11 小时前
【Node】认识multer库
前端·javascript·后端
lang2015092812 小时前
Spring Boot 官方文档精解:构建与依赖管理
java·spring boot·后端
why技术13 小时前
从18w到1600w播放量,我的一点思考。
java·前端·后端
间彧13 小时前
Redis Cluster vs Sentinel模式区别
后端
间彧14 小时前
🛡️ 构建高可用缓存架构:Redis集群与Caffeine多级缓存实战
后端