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适合需要严格一致性的场景。选择哪一种锁机制,主要取决于具体的应用需求和系统架构。

相关推荐
Moonbit9 小时前
MGPIC 初赛提交倒计时 4 天!
后端·算法·编程语言
程序定小飞9 小时前
基于springboot的作业管理系统设计与实现
java·开发语言·spring boot·后端·spring
程序员小假9 小时前
我们来说一下 Mybatis 的缓存机制
java·后端
沙虫一号10 小时前
线上python问题排查思路
后端·python
Hacker_Future10 小时前
Python FastAPI 数据库集成(SQLAlchemy)+ 接口权限校验
后端
Hacker_Future10 小时前
Python FastAPI 参数传递与响应校验
后端
NiShiKiFuNa10 小时前
AutoHotkey 功能配置与使用指南
后端
黎燃10 小时前
基于生产负载回放的数据库迁移验证实践:从模拟测试到真实预演【金仓数据库】
后端
文心快码BaiduComate10 小时前
双十一将至,用Rules玩转电商场景提效
前端·人工智能·后端
该用户已不存在11 小时前
免费的 Vibe Coding 助手?你想要的Gemini CLI 都有
人工智能·后端·ai编程