分布式锁

简介: 分布式锁是分布式系统中实现跨节点资源互斥访问的关键机制,常用于解决多进程、多机器环境下的并发控制问题。它依赖外部存储(如Redis、ZooKeeper)协调锁状态,确保全局唯一性和原子性操作。常见实现包括基于Redis的单点锁与RedLock算法、ZooKeeper的临时顺序节点及数据库唯一索引。适用于任务调度、缓存重建和库存管理等场景。设计时需关注可重入性、锁超时、续租及异常处理,并权衡性能与可靠性。

分布式锁是在分布式系统中实现跨节点资源互斥访问的一种机制,用于解决多进程、多机器环境下的并发控制问题。与单机锁(如synchronized)不同,分布式锁需要依赖外部共享存储(如Redis、ZooKeeper)来协调不同节点间的锁状态。

核心原理

  1. 全局唯一性
    通过共享存储(如Redis的SETNX命令)确保同一时间只有一个客户端能获取锁。

  2. 原子性操作
    锁的获取和释放必须是原子操作,避免竞态条件。例如:

    # Redis的SET命令同时设置值和过期时间(原子操作)
    SET lock_key "value" NX PX 30000  # 30秒过期
    
  3. 锁超时机制
    防止锁持有者崩溃后锁无法释放,通过设置过期时间自动失效。

分布式锁的实现方式

1. 基于Redis的实现

  • 单点Redis

    import redis
    import time
    
    class RedisLock:
        def __init__(self, redis_client, lock_key, expire_time=30):
            self.redis = redis_client
            self.lock_key = lock_key
            self.expire_time = expire_time
            self.uuid = str(uuid.uuid4())  # 唯一标识锁持有者
    
        def acquire(self):
            return self.redis.set(self.lock_key, self.uuid, nx=True, ex=self.expire_time)
    
        def release(self):
            script = """
            if redis.call("get", KEYS[1]) == ARGV[1] then
                return redis.call("del", KEYS[1])
            else
                return 0
            end
            """
            return self.redis.eval(script, 1, self.lock_key, self.uuid)
    
  • RedLock算法(多节点Redis):
    向多个独立的Redis节点同时获取锁,超过半数成功则认为获取成功,提高可靠性。

2. 基于ZooKeeper的实现

  • 创建临时顺序节点,最小节点持有者获得锁,其他节点监听前一个节点的删除事件。

    public class ZkLock implements AutoCloseable {
         
        private final CuratorFramework client;
        private final String lockPath;
        private String currentPath;
        private String previousPath;
    
        public ZkLock(CuratorFramework client, String lockPath) {
         
            this.client = client;
            this.lockPath = lockPath;
        }
    
        public void acquire() throws Exception {
         
            if (currentPath == null) {
         
                currentPath = client.create()
                    .withMode(CreateMode.EPHEMERAL_SEQUENTIAL)
                    .forPath(lockPath + "/lock-");
            }
    
            List<String> children = client.getChildren().forPath(lockPath);
            Collections.sort(children);
    
            if (currentPath.endsWith(children.get(0))) {
         
                return; // 当前节点是最小节点,获取锁成功
            }
    
            // 监听前一个节点
            previousPath = lockPath + "/" + children.get(children.indexOf(currentPath.substring(lockPath.length() + 1)) - 1);
            CountDownLatch latch = new CountDownLatch(1);
            NodeCache cache = new NodeCache(client, previousPath);
            cache.getListenable().addListener(() -> {
         
                if (cache.getCurrentData() == null) {
         
                    latch.countDown();
                }
            });
            cache.start();
    
            // 检查前一个节点是否已删除
            if (client.checkExists().forPath(previousPath) == null) {
         
                return;
            }
    
            latch.await(); // 等待前一个节点释放锁
        }
    
        @Override
        public void close() throws Exception {
         
            client.delete().forPath(currentPath);
        }
    }
    

3. 基于数据库的实现

  • 通过唯一索引或INSERT ... ON DUPLICATE KEY UPDATE实现:

    -- 创建锁表
    CREATE TABLE distributed_lock (
        lock_key VARCHAR(64) PRIMARY KEY,
        expire_time DATETIME NOT NULL
    );
    
    -- 获取锁
    INSERT INTO distributed_lock (lock_key, expire_time)
    VALUES ('resource_key', NOW() + INTERVAL 30 SECOND)
    ON DUPLICATE KEY UPDATE expire_time = NOW() + INTERVAL 30 SECOND;
    

分布式锁的典型应用场景

  1. 分布式任务调度
    防止多个节点同时执行同一任务。

  2. 缓存失效重建
    避免缓存击穿时大量请求同时重建缓存。

  3. 库存扣减
    保证分布式系统中库存操作的原子性。

分布式锁的设计要点

  1. 可重入性
    同一客户端可多次获取同一把锁,需记录获取次数。

  2. 锁的续租
    通过定时任务延长锁的过期时间,防止任务执行时间过长导致锁提前释放。

  3. 异常处理

    • 锁持有者崩溃时,通过过期时间自动释放锁。
    • 释放锁时验证锁的所有者,防止误释放。

与单机锁的对比

特性 单机锁 分布式锁
作用范围 同一进程内 跨进程、跨机器
实现方式 JVM内置(如synchronized) 依赖外部存储(Redis、ZooKeeper)
可靠性 取决于外部存储的可靠性
性能 高(无网络开销) 低(网络调用开销)
复杂度 高(需处理网络分区等)

注意事项

  1. 网络分区问题
    当发生网络分区时,可能导致多个节点同时认为自己持有锁(如Redis的脑裂问题)。

  2. 锁的粒度
    避免使用全局锁,尽量细化锁的粒度以提高并发度。

  3. 性能权衡
    分布式锁引入网络开销,需根据业务场景选择合适的实现方案。

总结

分布式锁是分布式系统中实现资源互斥的关键机制,常用Redis、ZooKeeper或数据库实现。设计时需考虑原子性、可重入性、锁超时和异常处理等问题。在选择实现方案时,需根据业务场景权衡性能、可靠性和复杂度。合理使用分布式锁能有效避免多节点并发带来的数据一致性问题。

目录
相关文章
|
4月前
|
Java Spring 容器
SpringBoot自动配置的原理是什么?
Spring Boot自动配置核心在于@EnableAutoConfiguration注解,它通过@Import导入配置选择器,加载META-INF/spring.factories中定义的自动配置类。这些类根据@Conditional系列注解判断是否生效。但Spring Boot 3.0后已弃用spring.factories,改用新格式的.imports文件进行配置。
810 0
|
4月前
|
消息中间件 canal 存储
如何解决并发环境下双写不一致的问题?
在并发环境下,“双写不一致”指数据库与缓存因操作顺序或执行时机差异导致数据不匹配。解决核心是保证操作的原子性、顺序性或最终一致性。常见方案包括延迟双删、加锁机制、binlog同步、版本号机制和读写锁分离,分别适用于不同一致性要求和并发场景,需根据业务需求综合选择。
316 0
|
机器学习/深度学习 自动驾驶 机器人
【论文速递】BEVFormer: 通过时空变换器从多相机图像中学习BEV表示
【论文速递】BEVFormer: 通过时空变换器从多相机图像中学习BEV表示
|
机器学习/深度学习 编解码 BI
RegNet架构复现--CVPR2020
在这项工作中,我们**提出了一种新的网络设计范式**。我们的目标是帮助促进对网络设计的理解,并发现跨环境通用的设计原则。我们不是专注于设计单个网络实例,而是设计参数化网络群体的网络设计空间。整个过程类似于经典的网络手动设计,但提升到了设计空间级别。使用我们的方法,我们探索了网络设计的结构方面,并**得出了一个由简单、规则的网络组成的低维设计空间,我们称之为** ==RegNet==。
2247 0
RegNet架构复现--CVPR2020
|
4月前
|
JSON Java 数据格式
Spring Boot返回Json数据及数据封装
在Spring Boot中,接口间及前后端的数据传输通常使用JSON格式。通过@RestController注解,可轻松实现Controller返回JSON数据。该注解是Spring Boot新增的组合注解,结合了@Controller和@ResponseBody的功能,默认将返回值转换为JSON格式。Spring Boot底层默认采用Jackson作为JSON解析框架,并通过spring-boot-starter-json依赖集成了相关库,包括jackson-databind、jackson-datatype-jdk8等常用模块,简化了开发者对依赖的手动管理。
471 3
|
9月前
|
缓存 安全 网络安全
代理协议解析:如何根据需求选择HTTP、HTTPS或SOCKS5?
本文详细介绍了HTTP、HTTPS和SOCKS5三种代理协议的特点、优缺点以及适用场景。通过对比和分析,可以根据具体需求选择最合适的代理协议。希望本文能帮助您更好地理解和应用代理协议,提高网络应用的安全性和性能。
498 17
美团面试:Redis锁如何续期?Redis锁超时,任务没完怎么办?
在40岁老架构师尼恩的读者交流群中,近期有小伙伴在面试一线互联网企业时遇到了关于Redis分布式锁过期及自动续期的问题。尼恩对此进行了系统化的梳理,介绍了两种核心解决方案:一是通过增加版本号实现乐观锁,二是利用watch dog自动续期机制。后者通过后台线程定期检查锁的状态并在必要时延长锁的过期时间,确保锁不会因超时而意外释放。尼恩还分享了详细的代码实现和原理分析,帮助读者深入理解并掌握这些技术点,以便在面试中自信应对相关问题。更多技术细节和面试准备资料可在尼恩的技术文章和《尼恩Java面试宝典》中获取。
美团面试:Redis锁如何续期?Redis锁超时,任务没完怎么办?
|
消息中间件 JSON Java
Spring Boot、Spring Cloud与Spring Cloud Alibaba版本对应关系
Spring Boot、Spring Cloud与Spring Cloud Alibaba版本对应关系
26329 0
|
存储 安全 前端开发
SAAS解决方案深度剖析:适用场景、挑战与成本评估指南
SAAS解决方案深度剖析:适用场景、挑战与成本评估指南
578 0