Java-分布式锁

1. 分布式锁概述

1.1 什么是分布式锁?

分布式锁是一种在分布式系统中实现资源互斥访问的机制,确保多个进程或线程在访问共享资源时不会发生冲突。其核心特性包括:

  • 互斥性:同一时刻只有一个客户端能持有锁。

  • 无死锁:即使持有锁的客户端崩溃,锁最终也会被释放。

  • 容错性:部分节点故障不影响锁的正常使用。

1.2 为什么需要分布式锁?

在分布式系统中,传统的单机锁(如Java的synchronizedReentrantLock)无法跨JVM生效。例如:

  • 电商库存扣减:防止超卖。

  • 订单处理:避免重复下单。

  • 定时任务调度:确保任务仅执行一次。

2. 分布式锁的实现方案

2.1 基于数据库的实现

2.1.1 乐观锁(CAS)
UPDATE inventory SET stock = stock - 1 
WHERE product_id = 100 AND stock >= 1;

缺点:高并发下大量失败重试。

2.1.2 悲观锁(SELECT FOR UPDATE)
@Transactional
public void deductStock(Long productId) {
    Inventory inventory = em.createQuery(
        "SELECT i FROM Inventory i WHERE i.productId = :pid FOR UPDATE", 
        Inventory.class)
        .setParameter("pid", productId)
        .getSingleResult();
    inventory.setStock(inventory.getStock() - 1);
}

缺点:数据库性能瓶颈。

2.2 基于Redis的实现

2.2.1 基础版(SETNX + EXPIRE)
public boolean tryLock(Jedis jedis, String lockKey, String requestId, int expireTime) {
    String result = jedis.set(lockKey, requestId, "NX", "PX", expireTime);
    return "OK".equals(result);
}

问题:非原子性操作可能导致死锁。

2.2.2 增强版(Lua脚本)
if redis.call('setnx', KEYS[1], ARGV[1]) == 1 then
    return redis.call('pexpire', KEYS[1], ARGV[2])
else
    return 0
end

优点:保证原子性。

2.3 基于ZooKeeper的实现

public class ZkDistributedLock {
    private final InterProcessMutex mutex;
    
    public boolean tryLock(long time, TimeUnit unit) {
        return mutex.acquire(time, unit);
    }
}

特点:基于临时顺序节点,强一致但性能较低。


3. Redisson高级特性解析

3.1 看门狗机制

// 自动续期(默认30秒,每10秒续一次)
RLock lock = redisson.getLock("myLock");
lock.lock(); // 无需指定超时时间

源码逻辑

  1. 启动守护线程定期检查锁状态。

  2. 若客户端仍活跃,重置锁过期时间。

3.2 红锁(RedLock)算法

RLock lock1 = redisson.getLock("lock1");
RLock lock2 = redisson.getLock("lock2");
RLock lock3 = redisson.getLock("lock3");
RedissonRedLock redLock = new RedissonRedLock(lock1, lock2, lock3);
redLock.lock();

流程

  1. 向N个独立Redis节点申请锁。

  2. 当多数节点(N/2+1)获取成功时,认为加锁成功。

4. 性能优化方案

4.1 锁分段技术

// 将库存ID哈希到16个分段
public RLock getSegmentLock(Long productId) {
    int segment = productId.hashCode() & 0xF;
    return redisson.getLock("stock_lock_" + segment);
}

效果:并发能力提升16倍。

4.2 热点数据优化

4.2.1 本地缓存+版本号
@Cacheable(value = "inventory", key = "#productId")
public Inventory getInventory(Long productId) {
    return inventoryRepo.findById(productId);
}
4.2.2 熔断降级
@HystrixCommand(fallbackMethod = "fallbackDeduct")
public void deductStock(Long productId) {
    // 尝试获取分布式锁
}

5. 生产环境监控

5.1 关键指标

指标名称计算方式报警阈值
锁等待时间获取锁结束时间-开始时间P99 > 500ms
锁持有时间释放锁时间-获取锁时间平均值 > 1s
锁竞争失败率失败次数/总尝试次数> 20%

5.2 Prometheus配置

- job_name: 'distributed_lock'
  metrics_path: '/actuator/prometheus'
  static_configs:
    - targets: ['lock-service:8080']

6. 选型建议

6.1 方案对比

方案一致性性能实现复杂度适用场景
Redis SETNX最终允许短暂不一致
Redisson中高金融交易
ZooKeeper配置管理
数据库行锁最低低并发事务

6.2 决策树

 

7. 未来演进

7.1 协程支持

suspend fun acquireLock() {
    while (!tryLock()) {
        delay(10) // 非阻塞挂起
    }
}

7.2 自动弹性扩缩

# 根据QPS动态调整锁分段数
def adjust_segments(current_qps):
    return max(16, math.ceil(current_qps / 1000))

附录:扩展阅读

### Java分布式锁的实现方式 在Java中,分布式锁是一种用于解决分布式系统中并发控制问题的机制。它可以确保在多个节点上运行的应用程序能够安全地访问共享资源。以下是几种常见的Java分布式锁实现方式: #### 1. 基于Redis的分布式锁 Redis因其高性能和简单的数据结构而成为实现分布式锁的常用选择。可以通过以下两种方式实现基于Redis的分布式锁- **使用SETNX命令** SETNX(Set if Not Exists)是一个原子操作,只有当键不存在时才会设置值。可以利用这一特性来实现分布式锁[^1]。例如: ```java public boolean tryLock(String key, String value, int expireTime) { Jedis jedis = new Jedis("localhost", 6379); String result = jedis.set(key, value, "NX", "PX", expireTime); return "OK".equals(result); } ``` - **使用Redisson库** Redisson是一个用于实现分布式锁的高级库,它提供了更丰富的功能和更高的可靠性。以下是一个基于Redisson的分布式锁示例代码[^2]: ```java @Resource private RedissonClient redissonClient; public void updateUser(String userId) { String lockKey = "config" + userId; RLock lock = redissonClient.getLock(lockKey); try { lock.lock(10, TimeUnit.SECONDS); // 加锁,指定锁定时间 // 处理业务逻辑 } finally { lock.unlock(); // 确保释放锁 } } ``` #### 2. 基于ZooKeeper的分布式锁 ZooKeeper是一个分布式协调服务,支持分布式锁的实现。其临时顺序节点的特性非常适合用于实现分布式锁。以下是一个简单的实现示例: ```java InterProcessMutex lock = new InterProcessMutex(client, "/lockpath"); try { lock.acquire(); // 获取锁 // 执行业务逻辑 } finally { lock.release(); // 释放锁 } ``` 此方法通过创建一个唯一的临时顺序节点来实现锁的获取与释放[^3]。 #### 3. 基于数据库的分布式锁 虽然不推荐,但在某些场景下也可以通过数据库实现分布式锁。通常使用`UPDATE`语句结合唯一约束来实现。例如: ```sql UPDATE lock_table SET locked = 1 WHERE name = 'resource' AND locked = 0; ``` 如果更新成功,则表示锁已被获取[^4]。 #### 注意事项 - **锁的超时机制**:为了避免死锁,所有分布式锁实现都应包含超时机制。 - **锁的释放**:无论是否发生异常,锁都应在`finally`块中释放。 - **性能与可靠性**:选择实现方式时需权衡性能与可靠性,例如Redis适合高并发场景,而ZooKeeper更适合需要强一致性的场景。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值