当前位置: 首页 > news >正文

分布式锁实现方式深度详解

目录

  1. 分布式锁核心概念
  2. 分布式锁实现方式对比
  3. Redis 分布式锁实现
  4. ZooKeeper 分布式锁实现
  5. 数据库分布式锁实现
  6. Redisson 分布式锁实现
  7. 常见问题与解决方案
  8. 最佳实践
  9. 常见疑问与解答

一、分布式锁核心概念

1.1 什么是分布式锁?

分布式锁是在分布式系统中,用于控制多个进程/线程对共享资源进行互斥访问的机制。

单机锁 vs 分布式锁:┌────────────────────────────────────────────────────┐
│                   单机锁(JVM内)                   │
├────────────────────────────────────────────────────┤
│  Thread-1 ──┐                                      │
│             ├──> [synchronized / ReentrantLock]    │
│  Thread-2 ──┘                                      │
│                                                    │
│  特点: 只在一个JVM内有效                             │
└────────────────────────────────────────────────────┘┌────────────────────────────────────────────────────┐
│                 分布式锁(跨JVM)                     │
├────────────────────────────────────────────────────┤
│  JVM-1 ──┐                                         │
│          ├──> [分布式锁服务] ──┐                    │
│  JVM-2 ──┤                    ├──> 共享资源         │
│          │                    │                    │
│  JVM-3 ──┘                    ┘                    │
│                                                    │
│  特点: 跨多个JVM/服务器有效                          │
└────────────────────────────────────────────────────┘

1.2 分布式锁的核心特性

分布式锁必须满足的特性:1. 互斥性(Mutual Exclusion)✅ 同一时刻只有一个进程/线程能持有锁2. 可重入性(Reentrant)✅ 同一个进程/线程可以多次获取同一把锁3. 锁超时(Timeout)✅ 防止死锁,锁必须有过期时间4. 高可用性(High Availability)✅ 锁服务本身必须高可用,不能单点故障5. 非阻塞性(Non-blocking)✅ 获取锁失败时,应该快速失败,而不是无限等待6. 公平性(Fairness)✅ (可选)按照请求顺序获取锁

1.3 分布式锁的应用场景

典型应用场景:1. 防止重复操作- 用户重复提交订单- 定时任务重复执行- 消息重复消费2. 资源访问控制- 库存扣减- 账户余额修改- 文件上传处理3. 分布式任务调度- 定时任务只在一个节点执行- 分布式计算任务分配4. 缓存更新控制- 防止缓存击穿(缓存重建)- 防止缓存雪崩

二、分布式锁实现方式对比

2.1 实现方式概览

分布式锁实现方式对比表:

实现方式性能可靠性复杂度推荐度
Redis⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐
Redisson⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐
ZooKeeper⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐
数据库⭐⭐⭐⭐⭐⭐⭐
etcd⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐

2.2 详细对比

特性RedisRedissonZooKeeper数据库etcd
性能极高(10万+ QPS)极高(10万+ QPS)中等(1万+ QPS)低(1000 QPS)高(5万+ QPS)
可靠性中等(主从复制)高(RedLock)极高(ZAB协议)高(ACID)极高(Raft)
实现复杂度简单简单中等简单中等
锁超时支持支持支持支持支持
可重入需实现原生支持需实现需实现需实现
公平锁需实现支持支持不支持需实现
Watch机制需实现支持原生支持不支持原生支持
适用场景高并发、低一致性要求高并发、中等一致性要求高一致性要求低并发、高一致性高一致性要求

三、Redis 分布式锁实现

3.1 基础实现(SET NX EX)

3.1.1 简单实现
/*** Redis 分布式锁(基础版)* 问题: 存在死锁风险(进程崩溃无法释放锁)*/
@Service
@Slf4j
public class SimpleRedisLock {@Autowiredprivate StringRedisTemplate redisTemplate;/*** 获取锁* @param key 锁的key* @param value 锁的值(用于释放锁时验证)* @param expireTime 过期时间(秒)* @return 是否获取成功*/public boolean tryLock(String key, String value, long expireTime) {Boolean result = redisTemplate.opsForValue().setIfAbsent(key, value, expireTime, TimeUnit.SECONDS);return Boolean.TRUE.equals(result);}/*** 释放锁* @param key 锁的key* @param value 锁的值(必须匹配才能释放)*/public void unlock(String key, String value) {String script = "if redis.call('get', KEYS[1]) == ARGV[1] then " +"    return redis.call('del', KEYS[1]) " +"else " +"    return 0 " +"end";redisTemplate.execute(new DefaultRedisScript<>(script, Long.class),Collections.singletonList(key),value);}
}
3.1.2 问题分析
基础实现的问题:
1. ❌ 死锁风险场景: 进程获取锁后崩溃,锁无法释放解决: 必须设置过期时间
2. ❌ 误释放锁场景: 进程A获取锁(30s),执行50s,进程B获取锁,进程A释放了B的锁解决: 释放锁时验证value是否匹配
3. ❌ 锁续期问题场景: 业务执行时间超过锁过期时间解决: 使用看门狗机制自动续期

3.2 改进实现(带看门狗)

/*** Redis 分布式锁(改进版 - 带看门狗)*/
@Service
@Slf4j
public class ImprovedRedisLock {@Autowiredprivate StringRedisTemplate redisTemplate;// 锁续期线程池private final ScheduledExecutorService watchDogExecutor = Executors.newScheduledThreadPool(10);// 存储锁的续期任务private final Map<String, ScheduledFuture<?>> watchDogTasks = new ConcurrentHashMap<>();/*** 获取锁(带看门狗)* @param key 锁的key* @param expireTime 过期时间(秒)* @param watchDogInterval 看门狗续期间隔(秒)* @return 锁的value(用于释放锁)*/public String tryLock(String key, long expireTime, long watchDogInterval) {String value = UUID.randomUUID().toString();// 1. 尝试获取锁Boolean result = redisTemplate.opsForValue().setIfAbsent(key, value, expireTime, TimeUnit.SECONDS);if (Boolean.TRUE.equals(result)) {// 2. 启动看门狗(自动续期)startWatchDog(key, value, expireTime, watchDogInterval);return value;}return null;}/*** 启动看门狗(自动续期)*/private void startWatchDog(String key, String value, long expireTime, long interval) {ScheduledFuture<?> future = watchDogExecutor.scheduleAtFixedRate(() -> {try {// 检查锁是否还存在且value匹配String script = "if redis.call('get', KEYS[1]) == ARGV[1] then " +"    return redis.call('expire', KEYS[1], ARGV[2]) " +"else " +"    return 0 " +"end";Long result = redisTemplate.execute(new DefaultRedisScript<>(script, Long.class),Collections.singletonList(key),value,String.valueOf(expireTime));if (result == null || result == 0) {// 锁已不存在,停止续期stopWatchDog(key);}} catch (Exception e) {log.error("Watch dog error for key: {}", key, e);stopWatchDog(key);}}, interval, interval, TimeUnit.SECONDS);  watchDogTasks.put(key, future);}/*** 停止看门狗*/private void stopWatchDog(String key) {ScheduledFuture<?> future = watchDogTasks.remove(key);if (future != null) {future.cancel(false);}}/*** 释放锁*/public void unlock(String key, String value) {// 1. 停止看门狗stopWatchDog(key);// 2. 释放锁(Lua脚本保证原子性)String script = "if redis.call('get', KEYS[1]) == ARGV[1] then " +"    return redis.call('del', KEYS[1]) " +"else " +"    return 0 " +"end";redisTemplate.execute(new DefaultRedisScript<>(script, Long.class),Collections.singletonList(key),value);}
}

3.3 可重入锁实现

/*** Redis 可重入分布式锁*/
@Service
@Slf4j
public class ReentrantRedisLock {@Autowiredprivate StringRedisTemplate redisTemplate;// ThreadLocal存储当前线程的锁信息private final ThreadLocal<Map<String, Integer>> lockCount = ThreadLocal.withInitial(HashMap::new);/*** 获取可重入锁*/public boolean tryLock(String key, String value, long expireTime) {Map<String, Integer> counts = lockCount.get();Integer count = counts.get(key);if (count != null && count > 0) {// 重入:增加计数counts.put(key, count + 1);return true;}// 首次获取锁Boolean result = redisTemplate.opsForValue().setIfAbsent(key, value, expireTime, TimeUnit.SECONDS);if (Boolean.TRUE.equals(result)) {counts.put(key, 1);return true;}return false;}/*** 释放可重入锁*/public void unlock(String key, String value) {Map<String, Integer> counts = lockCount.get();Integer count = counts.get(key);if (count == null || count <= 0) {throw new IllegalStateException("Lock not held by current thread");}if (count == 1) {// 最后一次释放,删除锁String script = "if redis.call('get', KEYS[1]) == ARGV[1] then " +"    return redis.call('del', KEYS[1]) " +"else " +"    return 0 " +"end";redisTemplate.execute(new DefaultRedisScript<>(script, Long.class),Collections.singletonList(key),value);counts.remove(key);} else {// 减少计数counts.put(key, count - 1);}}
}

3.4 公平锁实现(基于List + Set)

/*** Redis 公平分布式锁(FIFO)* * 实现原理:*  1. 使用 List 维护等待队列(FIFO)*  2. 使用 Set 存储已获取锁的线程*  3. 使用 String 存储当前持有锁的线程*/
@Service
@Slf4j
public class FairRedisLock {@Autowiredprivate StringRedisTemplate redisTemplate;private static final String LOCK_KEY_PREFIX = "fair:lock:";private static final String QUEUE_KEY_PREFIX = "fair:queue:";private static final String SET_KEY_PREFIX = "fair:set:";/*** 获取公平锁*/public boolean tryLock(String key, String value, long expireTime, long waitTime) {String lockKey = LOCK_KEY_PREFIX + key;String queueKey = QUEUE_KEY_PREFIX + key;String setKey = SET_KEY_PREFIX + key;long startTime = System.currentTimeMillis();while (true) {// 1. 尝试获取锁Boolean result = redisTemplate.opsForValue().setIfAbsent(lockKey, value, expireTime, TimeUnit.SECONDS);if (Boolean.TRUE.equals(result)) {// 2. 从等待队列中移除redisTemplate.opsForList().remove(queueKey, 1, value);redisTemplate.opsForSet().remove(setKey, value);return true;}// 3. 检查是否超时if (System.currentTimeMillis() - startTime > waitTime) {// 超时,从等待队列中移除redisTemplate.opsForList().remove(queueKey, 1, value);redisTemplate.opsForSet().remove(setKey, value);return false;}// 4. 加入等待队列(如果不在队列中)Boolean inQueue = redisTemplate.opsForSet().isMember(setKey, value);if (Boolean.FALSE.equals(inQueue)) {redisTemplate.opsForList().rightPush(queueKey, value);redisTemplate.opsForSet().add(setKey, value);}// 5. 检查是否轮到当前线程String first = redisTemplate.opsForList().index(queueKey, 0);if (value.equals(first)) {// 轮到我了,再次尝试获取锁continue;}// 6. 等待一小段时间后重试try {Thread.sleep(10);} catch (InterruptedException e) {Thread.currentThread().interrupt();return false;}}}/*** 释放公平锁*/public void unlock(String key, String value) {String lockKey = LOCK_KEY_PREFIX + key;String queueKey = QUEUE_KEY_PREFIX + key;String setKey = SET_KEY_PREFIX + key;String script = "if redis.call('get', KEYS[1]) == ARGV[1] then " +"    redis.call('del', KEYS[1]) " +"    return 1 " +"else " +"    return 0 " +"end";redisTemplate.execute(new DefaultRedisScript<>(script, Long.class),Arrays.asList(lockKey),value);}
}

四、ZooKeeper 分布式锁实现

4.1 ZooKeeper 锁原理

ZooKeeper 分布式锁原理:1. 临时顺序节点(Ephemeral Sequential Node)/lock/resource_0000000001/lock/resource_0000000002/lock/resource_00000000032. 最小序号获取锁- 节点序号最小的获得锁- 其他节点监听前一个节点的删除事件3. 锁释放- 节点删除(临时节点,会话断开自动删除)- 下一个节点被通知,尝试获取锁

4.2 基础实现

/*** ZooKeeper 分布式锁*/
@Service
@Slf4j
public class ZooKeeperLock {private final CuratorFramework client;private static final String LOCK_PATH = "/locks";public ZooKeeperLock(CuratorFramework client) {this.client = client;}/*** 获取锁*/public InterProcessMutex tryLock(String resource) throws Exception {String lockPath = LOCK_PATH + "/" + resource;InterProcessMutex lock = new InterProcessMutex(client, lockPath);// 尝试获取锁(最多等待10秒)boolean acquired = lock.acquire(10, TimeUnit.SECONDS);if (acquired) {return lock;}return null;}/*** 释放锁*/public void unlock(InterProcessMutex lock) {try {if (lock != null && lock.isAcquiredInThisProcess()) {lock.release();}} catch (Exception e) {log.error("Failed to release lock", e);}}
}

4.3 可重入锁实现

/*** ZooKeeper 可重入分布式锁*/
@Service
@Slf4j
public class ReentrantZooKeeperLock {private final CuratorFramework client;private static final String LOCK_PATH = "/locks";// ThreadLocal存储当前线程的锁信息private final ThreadLocal<Map<String, InterProcessMutex>> locks = ThreadLocal.withInitial(HashMap::new);public ReentrantZooKeeperLock(CuratorFramework client) {this.client = client;}/*** 获取可重入锁*/public boolean tryLock(String resource) throws Exception {Map<String, InterProcessMutex> lockMap = locks.get();InterProcessMutex lock = lockMap.get(resource);if (lock != null) {// 重入:直接返回true(Curator的InterProcessMutex本身支持可重入)return true;}// 首次获取锁String lockPath = LOCK_PATH + "/" + resource;lock = new InterProcessMutex(client, lockPath);boolean acquired = lock.acquire(10, TimeUnit.SECONDS);if (acquired) {lockMap.put(resource, lock);return true;}return false;}/*** 释放可重入锁*/public void unlock(String resource) {Map<String, InterProcessMutex> lockMap = locks.get();InterProcessMutex lock = lockMap.get(resource);if (lock != null) {try {lock.release();lockMap.remove(resource);} catch (Exception e) {log.error("Failed to release lock: {}", resource, e);}}}
}

4.4 ZooKeeper 配置

/*** ZooKeeper 配置*/
@Configuration
public class ZooKeeperConfig {@Value("${zookeeper.connect-string:localhost:2181}")private String connectString;@Value("${zookeeper.session-timeout:30000}")private int sessionTimeout;@Beanpublic CuratorFramework curatorFramework() {RetryPolicy retryPolicy = new ExponentialBackoffRetry(1000, 3);CuratorFramework client = CuratorFrameworkFactory.builder().connectString(connectString).sessionTimeoutMs(sessionTimeout).retryPolicy(retryPolicy).build();client.start();return client;}
}

五、数据库分布式锁实现

5.1 基于唯一索引实现

/*** 数据库分布式锁(基于唯一索引)*/
@Service
@Slf4j
public class DatabaseLock {@Autowiredprivate JdbcTemplate jdbcTemplate;/*** 获取锁*/public boolean tryLock(String lockName, String owner, long expireTime) {String sql = "INSERT INTO distributed_lock (lock_name, owner, expire_time, create_time) " +"VALUES (?, ?, ?, NOW()) " +"ON DUPLICATE KEY UPDATE " +"  owner = IF(expire_time < NOW(), VALUES(owner), owner), " +"  expire_time = IF(expire_time < NOW(), VALUES(expire_time), expire_time)";try {int rows = jdbcTemplate.update(sql, lockName, owner, new Timestamp(System.currentTimeMillis() + expireTime));return rows > 0;} catch (DuplicateKeyException e) {// 锁已被其他进程持有return false;}}/*** 释放锁*/public void unlock(String lockName, String owner) {String sql = "DELETE FROM distributed_lock WHERE lock_name = ? AND owner = ?";jdbcTemplate.update(sql, lockName, owner);}/*** 锁续期*/public boolean renewLock(String lockName, String owner, long expireTime) {String sql = "UPDATE distributed_lock " +"SET expire_time = DATE_ADD(NOW(), INTERVAL ? SECOND) " +"WHERE lock_name = ? AND owner = ? AND expire_time > NOW()";int rows = jdbcTemplate.update(sql, expireTime / 1000, lockName, owner);return rows > 0;}
}

5.2 数据库表结构

CREATE TABLE distributed_lock (id BIGINT PRIMARY KEY AUTO_INCREMENT,lock_name VARCHAR(128) NOT NULL UNIQUE COMMENT '锁名称',owner VARCHAR(128) NOT NULL COMMENT '锁持有者',expire_time TIMESTAMP NOT NULL COMMENT '过期时间',create_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',INDEX idx_expire_time (expire_time)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='分布式锁表';

5.3 定时清理过期锁

/*** 定时清理过期锁*/
@Component
@Slf4j
public class LockCleanupTask {@Autowiredprivate JdbcTemplate jdbcTemplate;@Scheduled(fixedRate = 60000) // 每分钟执行一次public void cleanupExpiredLocks() {String sql = "DELETE FROM distributed_lock WHERE expire_time < NOW()";int deleted = jdbcTemplate.update(sql);if (deleted > 0) {log.info("Cleaned up {} expired locks", deleted);}}
}

六、Redisson 分布式锁实现

6.1 Redisson 简介

Redisson 是 Redis 的 Java 客户端,提供了丰富的分布式对象和服务,包括分布式锁。

Redisson 分布式锁特性:✅ 可重入锁(ReentrantLock)
✅ 公平锁(FairLock)
✅ 读写锁(ReadWriteLock)
✅ 信号量(Semaphore)
✅ 闭锁(CountDownLatch)
✅ 看门狗机制(自动续期)
✅ 异步支持

6.2 Redisson 配置

/*** Redisson 配置*/
@Configuration
public class RedissonConfig {@Value("${redis.host:localhost}")private String host;@Value("${redis.port:6379}")private int port;@Value("${redis.password:}")private String password;@Beanpublic RedissonClient redissonClient() {Config config = new Config();// 单节点模式config.useSingleServer().setAddress("redis://" + host + ":" + port).setPassword(password).setConnectionPoolSize(10).setConnectionMinimumIdleSize(5);// 集群模式(可选)// config.useClusterServers()//     .addNodeAddress("redis://127.0.0.1:7000", "redis://127.0.0.1:7001");return Redisson.create(config);}
}

6.3 可重入锁使用

/*** Redisson 可重入锁使用示例*/
@Service
@Slf4j
public class RedissonLockService {@Autowiredprivate RedissonClient redissonClient;/*** 可重入锁示例*/public void doWithLock(String resource) {RLock lock = redissonClient.getLock("lock:" + resource);try {// 尝试获取锁,最多等待10秒,锁定后30秒自动解锁boolean acquired = lock.tryLock(10, 30, TimeUnit.SECONDS);if (acquired) {// 业务逻辑doBusinessLogic(resource);} else {log.warn("Failed to acquire lock: {}", resource);}} catch (InterruptedException e) {Thread.currentThread().interrupt();log.error("Interrupted while acquiring lock", e);} finally {// 释放锁if (lock.isHeldByCurrentThread()) {lock.unlock();}}}/*** 公平锁示例*/public void doWithFairLock(String resource) {RLock lock = redissonClient.getFairLock("fair:lock:" + resource);try {boolean acquired = lock.tryLock(10, 30, TimeUnit.SECONDS);if (acquired) {doBusinessLogic(resource);}} catch (InterruptedException e) {Thread.currentThread().interrupt();} finally {if (lock.isHeldByCurrentThread()) {lock.unlock();}}}/*** 读写锁示例*/public void doWithReadWriteLock(String resource) {RReadWriteLock readWriteLock = redissonClient.getReadWriteLock("rw:lock:" + resource);RLock readLock = readWriteLock.readLock();RLock writeLock = readWriteLock.writeLock();// 读锁(共享)readLock.lock();try {// 读操作readData(resource);} finally {readLock.unlock();}// 写锁(独占)writeLock.lock();try {// 写操作writeData(resource);} finally {writeLock.unlock();}}/*** 信号量示例(限流)*/public void doWithSemaphore(String resource, int permits) {RSemaphore semaphore = redissonClient.getSemaphore("semaphore:" + resource);try {// 尝试获取许可boolean acquired = semaphore.tryAcquire(permits, 10, TimeUnit.SECONDS);if (acquired) {// 执行业务逻辑doBusinessLogic(resource);}} catch (InterruptedException e) {Thread.currentThread().interrupt();} finally {// 释放许可semaphore.release(permits);}}private void doBusinessLogic(String resource) {// 业务逻辑}private void readData(String resource) {// 读操作}private void writeData(String resource) {// 写操作}
}

6.4 RedLock 实现(多Redis实例)

/*** RedLock 实现(多Redis实例,提高可靠性)*/
@Service
@Slf4j
public class RedLockService {@Autowiredprivate RedissonClient redissonClient1;@Autowiredprivate RedissonClient redissonClient2;@Autowiredprivate RedissonClient redissonClient3;/*** RedLock 获取锁(需要大多数节点成功)*/public boolean tryRedLock(String resource, long waitTime, long leaseTime) {RLock lock1 = redissonClient1.getLock("lock:" + resource);RLock lock2 = redissonClient2.getLock("lock:" + resource);RLock lock3 = redissonClient3.getLock("lock:" + resource);RedissonRedLock redLock = new RedissonRedLock(lock1, lock2, lock3);try {boolean acquired = redLock.tryLock(waitTime, leaseTime, TimeUnit.SECONDS);if (acquired) {// 业务逻辑doBusinessLogic(resource);return true;}return false;} catch (InterruptedException e) {Thread.currentThread().interrupt();return false;} finally {if (redLock.isHeldByCurrentThread()) {redLock.unlock();}}}private void doBusinessLogic(String resource) {// 业务逻辑}
}

七、常见问题与解决方案

7.1 死锁问题

问题: 进程获取锁后崩溃,锁无法释放解决方案:1. 设置锁过期时间(TTL)2. 使用看门狗机制自动续期3. 使用临时节点(ZooKeeper)

7.2 锁误释放问题

问题: 进程A的锁过期,进程B获取锁,进程A释放了B的锁解决方案:1. 释放锁时验证value是否匹配(Redis)2. 使用唯一标识(UUID)作为锁的值3. 使用Lua脚本保证原子性

7.3 锁续期问题

问题: 业务执行时间超过锁过期时间解决方案:1. 使用看门狗机制(Redisson)2. 合理设置锁过期时间3. 异步续期任务

7.4 时钟漂移问题

问题: 不同服务器时钟不同步,导致锁过期时间不准确解决方案:1. 使用NTP同步时钟2. 使用相对时间而非绝对时间3. 增加锁过期时间的缓冲时间

7.5 网络分区问题

问题: 网络分区导致锁状态不一致解决方案:1. 使用RedLock(多Redis实例)2. 使用ZooKeeper(ZAB协议保证一致性)3. 使用etcd(Raft协议保证一致性)

八、最佳实践

8.1 锁命名规范

/*** 锁命名规范*/
public class LockNaming {/*** 格式: {业务模块}:{资源类型}:{资源ID}* * 示例:*   order:create:user123*   inventory:deduct:product456*   payment:process:order789*/public static String generateLockKey(String module, String resourceType, String resourceId) {return String.format("%s:%s:%s", module, resourceType, resourceId);}
}

8.2 锁超时时间设置

/*** 锁超时时间设置建议*/
public class LockTimeout {/*** 根据业务场景设置合理的超时时间* * 快速操作: 5-10秒* 一般操作: 30-60秒* 慢速操作: 5-10分钟*/public static long getTimeout(String operationType) {switch (operationType) {case "QUICK": return 10;      // 10秒case "NORMAL": return 60;     // 60秒case "SLOW": return 600;      // 10分钟default: return 30;           // 默认30秒}}
}

8.3 锁使用模板

/*** 分布式锁使用模板*/
@Component
@Slf4j
public class LockTemplate {@Autowiredprivate RedissonClient redissonClient;/*** 执行带锁的业务逻辑*/public <T> T executeWithLock(String lockKey, long waitTime, long leaseTime, Supplier<T> supplier) {RLock lock = redissonClient.getLock(lockKey);try {boolean acquired = lock.tryLock(waitTime, leaseTime, TimeUnit.SECONDS);if (!acquired) {throw new LockAcquisitionException("Failed to acquire lock: " + lockKey);}return supplier.get();} catch (InterruptedException e) {Thread.currentThread().interrupt();throw new LockAcquisitionException("Interrupted while acquiring lock", e);} finally {if (lock.isHeldByCurrentThread()) {lock.unlock();}}}/*** 执行带锁的业务逻辑(无返回值)*/public void executeWithLock(String lockKey, long waitTime, long leaseTime, Runnable runnable) {executeWithLock(lockKey, waitTime, leaseTime, () -> {runnable.run();return null;});}
}

8.4 使用示例

/*** 分布式锁使用示例*/
@Service
@Slf4j
public class OrderService {@Autowiredprivate LockTemplate lockTemplate;/*** 创建订单(防止重复提交)*/public Order createOrder(CreateOrderRequest request) {String lockKey = "order:create:" + request.getUserId();return lockTemplate.executeWithLock(lockKey,5,      // 等待5秒30,     // 锁定30秒() -> {// 检查是否重复提交Order existingOrder = orderMapper.selectByRequestId(request.getRequestId());if (existingOrder != null) {return existingOrder;}// 创建订单Order order = new Order();// ... 设置订单信息orderMapper.insert(order);return order;});}/*** 扣减库存(防止超卖)*/public void deductInventory(String productId, int quantity) {String lockKey = "inventory:deduct:" + productId;lockTemplate.executeWithLock(lockKey,10,     // 等待10秒60,     // 锁定60秒() -> {// 查询库存Inventory inventory = inventoryMapper.selectByProductId(productId);// 检查库存是否充足if (inventory.getStock() < quantity) {throw new InsufficientStockException("库存不足");}// 扣减库存inventory.setStock(inventory.getStock() - quantity);inventoryMapper.updateById(inventory);});}
}

8.5 监控和告警

/*** 分布式锁监控*/
@Component
@Slf4j
public class LockMonitor {@Autowiredprivate MeterRegistry meterRegistry;private final Counter lockAcquireCounter;private final Counter lockReleaseCounter;private final Counter lockTimeoutCounter;private final Timer lockHoldTimer;public LockMonitor(MeterRegistry meterRegistry) {this.meterRegistry = meterRegistry;this.lockAcquireCounter = Counter.builder("distributed.lock.acquire.total").description("分布式锁获取总数").register(meterRegistry);this.lockReleaseCounter = Counter.builder("distributed.lock.release.total").description("分布式锁释放总数").register(meterRegistry);this.lockTimeoutCounter = Counter.builder("distributed.lock.timeout.total").description("分布式锁超时总数").register(meterRegistry);this.lockHoldTimer = Timer.builder("distributed.lock.hold.duration").description("分布式锁持有时间").register(meterRegistry);}/*** 记录锁获取*/public void recordLockAcquire(String lockKey, boolean success) {lockAcquireCounter.increment(Tags.of("lock", lockKey, "success", String.valueOf(success)));if (!success) {lockTimeoutCounter.increment(Tags.of("lock", lockKey));}}/*** 记录锁释放*/public void recordLockRelease(String lockKey) {lockReleaseCounter.increment(Tags.of("lock", lockKey));}/*** 记录锁持有时间*/public void recordLockHoldDuration(String lockKey, long durationMs) {lockHoldTimer.record(durationMs, TimeUnit.MILLISECONDS, Tags.of("lock", lockKey));}
}

九、常见疑问与解答

9.1 基础概念疑问

Q1: 为什么单机锁(synchronized/ReentrantLock)在分布式环境下不够用?

疑问: 既然单机锁已经能保证线程安全,为什么还需要分布式锁?

解答:

单机锁的局限性:场景示例:┌──────────────┐      ┌─────────────┐      ┌─────────────┐│  服务器A     │      │  服务器B     │      │  服务器C     ││  JVM-1       │      │  JVM-2      │      │  JVM-3      ││              │      │              │      │              ││ synchronized │      │ synchronized │      │ synchronized ││  锁A         │      │  锁B         │      │  锁C         │└──────────────┘      └─────────────┘      └─────────────┘│                     │                     │└─────────────────────┴─────────────────────┘│┌─────────▼─────────┐│   共享数据库       ││   库存: 100        │└──────────────────┘问题:1. 每个JVM的锁是独立的,无法感知其他JVM的锁2. 三个服务器同时扣减库存,可能导致超卖3. 需要跨JVM的协调机制解决方案:使用分布式锁,所有服务器共享同一个锁状态

代码示例:

// ❌ 错误:单机锁无法防止分布式环境下的并发问题
public class InventoryService {private int stock = 100;private final Object lock = new Object();public void deductStock(int quantity) {synchronized (lock) {  // 只在当前JVM有效if (stock >= quantity) {stock -= quantity;}}}
}// ✅ 正确:使用分布式锁
public class InventoryService {@Autowiredprivate RedissonClient redissonClient;public void deductStock(int quantity) {RLock lock = redissonClient.getLock("inventory:product:123");try {lock.lock();// 从数据库查询库存Inventory inventory = inventoryMapper.selectById(123);if (inventory.getStock() >= quantity) {inventory.setStock(inventory.getStock() - quantity);inventoryMapper.updateById(inventory);}} finally {lock.unlock();}}
}

Q2: 分布式锁和数据库事务有什么区别?什么时候用哪个?

疑问: 数据库事务已经能保证ACID,为什么还需要分布式锁?

解答:

分布式锁 vs 数据库事务:┌─────────────────────────────────────────────────────────┐
│                   数据库事务                            │
├─────────────────────────────────────────────────────────┤
│  作用范围: 单个数据库连接内                              │
│  保证: ACID(原子性、一致性、隔离性、持久性)            │
│  粒度: 数据库操作级别                                    │
│  场景: 单个请求内的多个数据库操作                        │
└─────────────────────────────────────────────────────────┘┌─────────────────────────────────────────────────────────┐
│                   分布式锁                              │
├─────────────────────────────────────────────────────────┤
│  作用范围: 跨进程、跨服务器                              │
│  保证: 互斥访问                                          │
│  粒度: 业务逻辑级别                                      │
│  场景: 跨请求的业务逻辑互斥                              │
└─────────────────────────────────────────────────────────┘

使用场景对比:

// 场景1: 数据库事务适用(单个请求内的操作)
@Transactional
public void transferMoney(Long fromAccount, Long toAccount, BigDecimal amount) {// 这些操作在同一个事务中,要么全部成功,要么全部回滚accountMapper.deduct(fromAccount, amount);accountMapper.add(toAccount, amount);
}// 场景2: 分布式锁适用(跨请求的业务逻辑)
public void createOrder(CreateOrderRequest request) {String lockKey = "order:create:" + request.getUserId();RLock lock = redissonClient.getLock(lockKey);try {lock.lock();// 防止用户重复提交订单(跨请求)Order existingOrder = orderMapper.selectByRequestId(request.getRequestId());if (existingOrder != null) {return existingOrder;}// 创建订单(内部可能包含事务)return createOrderInternal(request);} finally {lock.unlock();}
}

选择原则:

  • 数据库事务: 单个请求内的多个数据库操作需要原子性
  • 分布式锁: 跨请求的业务逻辑需要互斥访问

9.2 实现细节疑问

Q3: Redis分布式锁为什么要用Lua脚本?直接用Redis命令不行吗?

疑问: 为什么释放锁要用Lua脚本,直接用DEL命令不行吗?

解答:

问题场景:时间线:T1: 进程A获取锁(过期时间30秒)T2: 进程A执行业务逻辑(耗时40秒)T3: 锁过期,进程B获取锁T4: 进程A执行完成,释放锁(误释放了B的锁)T5: 进程C获取锁(此时B和C都认为持有锁)❌ 错误实现:
public void unlock(String key) {redisTemplate.delete(key);  // 可能删除其他进程的锁
}✅ 正确实现(Lua脚本保证原子性):
public void unlock(String key, String value) {String script = "if redis.call('get', KEYS[1]) == ARGV[1] then " +"    return redis.call('del', KEYS[1]) " +"else " +"    return 0 " +"end";redisTemplate.execute(new DefaultRedisScript<>(script, Long.class),Collections.singletonList(key),value);
}

为什么需要Lua脚本:

非原子操作的问题:步骤1: GET key → 返回 "value-A"
步骤2: 判断 value == "value-A" → true
步骤3: DEL key → 删除锁问题:在步骤2和步骤3之间,如果锁被其他进程修改,步骤3可能删除错误的锁Lua脚本的优势:✅ 原子性:整个脚本作为一个原子操作执行✅ 一致性:执行过程中不会被其他命令打断✅ 性能:减少网络往返次数

Q4: 看门狗机制是怎么工作的?为什么需要它?

疑问: 既然设置了锁过期时间,为什么还需要看门狗自动续期?

解答:

看门狗机制的必要性:场景1: 锁过期时间设置过长❌ 问题: 如果进程崩溃,锁要等很久才能自动释放✅ 解决: 设置较短的过期时间(如30秒)场景2: 锁过期时间设置过短❌ 问题: 业务逻辑执行时间超过锁过期时间,锁被提前释放✅ 解决: 使用看门狗自动续期看门狗工作原理:时间线:T0: 获取锁,设置过期时间30秒,启动看门狗(每10秒续期一次)T10: 看门狗检查锁是否还存在,如果存在则续期30秒T20: 看门狗再次续期30秒T30: 业务逻辑执行完成,释放锁,停止看门狗

代码示例:

/*** 看门狗机制实现*/
public class WatchDogLock {private ScheduledExecutorService executor = Executors.newScheduledThreadPool(1);private ScheduledFuture<?> watchDogTask;public void lock(String key, String value, long expireTime) {// 1. 获取锁redisTemplate.opsForValue().setIfAbsent(key, value, expireTime, TimeUnit.SECONDS);// 2. 启动看门狗(在过期时间的1/3时续期)long renewInterval = expireTime / 3;watchDogTask = executor.scheduleAtFixedRate(() -> {String script = "if redis.call('get', KEYS[1]) == ARGV[1] then " +"    return redis.call('expire', KEYS[1], ARGV[2]) " +"else " +"    return 0 " +"end";redisTemplate.execute(new DefaultRedisScript<>(script, Long.class),Collections.singletonList(key),value,String.valueOf(expireTime));}, renewInterval, renewInterval, TimeUnit.SECONDS);}public void unlock(String key, String value) {// 停止看门狗if (watchDogTask != null) {watchDogTask.cancel(false);}// 释放锁// ...}
}

看门狗的优势:

  • ✅ 自动续期,避免锁提前过期
  • ✅ 进程崩溃时,看门狗停止,锁自动过期
  • ✅ 无需手动估算业务执行时间

Q5: RedLock算法真的能解决所有问题吗?有什么局限性?

疑问: RedLock号称能解决Redis单点故障问题,它真的完美吗?

解答:

RedLock算法原理:1. 客户端向N个Redis实例发送加锁请求
2. 如果大多数(N/2+1)实例加锁成功,则认为获取锁成功
3. 释放锁时,向所有实例发送释放请求示例(5个Redis实例):Redis-1: ✅ 加锁成功Redis-2: ✅ 加锁成功Redis-3: ✅ 加锁成功Redis-4: ❌ 加锁失败Redis-5: ❌ 加锁失败结果: 3/5 > 50%,获取锁成功

RedLock的局限性:

1. 时钟同步问题问题: 如果不同Redis服务器时钟不同步,可能导致锁提前过期场景:- Redis-1时钟快2秒,锁在T+28秒过期- Redis-2时钟正常,锁在T+30秒过期- 客户端认为锁还有效,但Redis-1的锁已过期解决: 使用NTP同步时钟,增加锁过期时间的缓冲2. 网络延迟问题问题: 网络延迟可能导致锁状态不一致场景:- T1: 客户端向5个Redis发送加锁请求- T2: 3个Redis响应成功(网络快)- T3: 客户端认为获取锁成功- T4: 另外2个Redis响应成功(网络慢)- 结果: 实际上5个Redis都加锁成功,但客户端只收到3个响应解决: 设置合理的超时时间,等待所有响应3. 性能开销问题: 需要向多个Redis实例发送请求,性能开销大场景: 5个Redis实例,每次加锁需要5次网络请求解决: 权衡可靠性和性能,选择合适的实例数量(通常3-5个)4. 脑裂问题问题: 网络分区可能导致多个客户端同时持有锁场景:- 网络分区1: Redis-1, Redis-2, Redis-3, 客户端A- 网络分区2: Redis-4, Redis-5, 客户端B- 客户端A在分区1中获取锁(3/5)- 客户端B在分区2中获取锁(2/5,但分区2认为自己是多数)解决: 使用ZooKeeper等强一致性系统

Martin Kleppmann的质疑:

Martin Kleppmann(《数据密集型应用系统设计》作者)对RedLock的质疑:1. RedLock假设所有Redis实例时钟同步→ 实际环境中很难保证2. RedLock假设网络延迟可预测→ 实际网络延迟波动很大3. RedLock无法保证锁的安全性→ 在某些故障场景下,可能多个客户端同时持有锁建议:- 如果只需要效率(efficiency),使用单Redis实例即可- 如果需要正确性(correctness),使用ZooKeeper等强一致性系统

9.3 性能与可靠性疑问

Q6: Redis分布式锁在高并发场景下性能如何?会不会成为瓶颈?

疑问: 如果所有请求都要获取锁,Redis会不会成为性能瓶颈?

解答:

Redis性能分析:单Redis实例性能:- SET命令: ~100,000 QPS- GET命令: ~100,000 QPS- Lua脚本: ~80,000 QPS分布式锁操作:1. SET NX EX(获取锁): ~100,000 QPS2. Lua脚本(释放锁): ~80,000 QPS实际场景分析:场景: 电商秒杀活动- 并发用户: 10,000- 每个用户请求: 1次锁操作(获取锁 + 释放锁)- 总QPS: 10,000 × 2 = 20,000 QPS结论: Redis单实例可以轻松应对(100,000 QPS >> 20,000 QPS)场景: 高频交易系统- 并发请求: 100,000- 每个请求: 1次锁操作- 总QPS: 100,000 × 2 = 200,000 QPS结论: 需要Redis集群或优化锁粒度

性能优化策略:

/*** 性能优化示例*/
public class OptimizedLockService {@Autowiredprivate RedissonClient redissonClient;/*** 优化1: 减少锁粒度*/public void deductInventory(String productId, int quantity) {// ❌ 粗粒度锁(锁整个库存表)// RLock lock = redissonClient.getLock("inventory:all");// ✅ 细粒度锁(只锁单个商品)RLock lock = redissonClient.getLock("inventory:product:" + productId);try {lock.lock();// 业务逻辑} finally {lock.unlock();}}/*** 优化2: 使用读写锁(读多写少场景)*/public Inventory getInventory(String productId) {RReadWriteLock readWriteLock = redissonClient.getReadWriteLock("inventory:product:" + productId);RLock readLock = readWriteLock.readLock();try {readLock.lock();// 读操作(多个读可以并发)return inventoryMapper.selectById(productId);} finally {readLock.unlock();}}/*** 优化3: 锁超时快速失败*/public void quickFailLock(String key) {RLock lock = redissonClient.getLock(key);try {// 快速失败,不等待boolean acquired = lock.tryLock(0, 30, TimeUnit.SECONDS);if (!acquired) {throw new LockAcquisitionException("Lock busy");}// 业务逻辑} finally {if (lock.isHeldByCurrentThread()) {lock.unlock();}}}
}

性能对比:

锁粒度对性能的影响:粗粒度锁(锁整个表):- 并发度: 1(所有请求串行)- QPS: ~1,000细粒度锁(锁单条记录):- 并发度: N(N条记录可以并发)- QPS: ~10,000(假设100条记录)读写锁(读多写少):- 读并发度: 无限(多个读可以并发)- 写并发度: 1(写操作串行)- QPS: ~50,000(假设读:写 = 10:1)

Q7: ZooKeeper分布式锁为什么比Redis更可靠?代价是什么?

疑问: 为什么说ZooKeeper分布式锁更可靠?它有什么代价?

解答:

可靠性对比:┌────────────────────────────────────────────────────┐
│                   Redis分布式锁                     │
├────────────────────────────────────────────────────┤
│  可靠性机制: 主从复制(异步)                        │
│  一致性保证: 最终一致性                              │
│  故障场景:                                          │
│    - 主节点崩溃 → 从节点提升为主(可能丢失部分数据)   │
│    - 网络分区 → 可能出现脑裂                         │
│  适用场景: 高并发、可容忍短暂不一致                   │
└────────────────────────────────────────────────────┘┌────────────────────────────────────────────────────┐
│                ZooKeeper分布式锁                    │
├────────────────────────────────────────────────────┤
│  可靠性机制: ZAB协议(强一致性)                     │
│  一致性保证: 强一致性(线性一致性)                   │
│  故障场景:                                          │
│    - 节点崩溃 → 自动故障转移(不丢失数据)            │
│    - 网络分区 → 少数派节点停止服务(保证一致性)       │
│  适用场景: 高一致性要求、可容忍较低性能               │
└────────────────────────────────────────────────────┘

ZooKeeper的代价:

1. 性能代价Redis: ~100,000 QPSZooKeeper: ~10,000 QPS(性能低10倍)2. 复杂度代价Redis: 简单(SET NX EX)ZooKeeper: 复杂(临时顺序节点、Watch机制)3. 资源代价Redis: 内存占用小ZooKeeper: 需要更多内存和磁盘空间4. 运维代价Redis: 运维简单ZooKeeper: 需要维护集群,配置复杂

选择建议:

选择Redis分布式锁:✅ 高并发场景(QPS > 10,000)✅ 可容忍短暂不一致✅ 性能优先✅ 简单易用选择ZooKeeper分布式锁:✅ 高一致性要求✅ 可容忍较低性能✅ 需要强一致性保证✅ 已有ZooKeeper基础设施

9.4 实际应用疑问

Q8: 分布式锁会不会导致性能下降?如何评估是否需要使用?

疑问: 使用分布式锁会不会让系统变慢?什么时候该用,什么时候不该用?

解答:

性能影响分析:无锁场景:- 操作耗时: 10ms- QPS: 100使用分布式锁场景:- 获取锁: 2ms- 业务操作: 10ms- 释放锁: 1ms- 总耗时: 13ms- QPS: 77(下降23%)结论: 分布式锁确实会带来性能开销,但通常是可接受的

是否需要分布式锁的判断标准:

/*** 判断是否需要分布式锁的决策树*/
public class LockDecisionTree {/*** 场景1: 单机应用 → 不需要分布式锁*/public void singleMachineScenario() {// 使用synchronized或ReentrantLock即可synchronized (this) {// 业务逻辑}}/*** 场景2: 分布式应用 + 无共享资源竞争 → 不需要分布式锁*/public void noResourceContention() {// 每个请求处理不同的资源,无竞争// 例如: 用户A修改自己的信息,用户B修改自己的信息// → 不需要锁}/*** 场景3: 分布式应用 + 有共享资源竞争 → 需要分布式锁*/public void hasResourceContention() {// 多个请求竞争同一资源// 例如: 多个用户同时购买同一商品(库存竞争)// → 需要分布式锁}/*** 场景4: 数据库唯一约束可以解决 → 不需要分布式锁*/public void databaseConstraint() {// 例如: 防止重复订单// 方案1: 使用分布式锁// 方案2: 使用数据库唯一索引(更简单)// ✅ 推荐: 使用数据库唯一索引try {orderMapper.insert(order);  // 唯一索引防止重复} catch (DuplicateKeyException e) {// 重复订单,返回已有订单return orderMapper.selectByRequestId(request.getRequestId());}}
}

性能优化建议:

/*** 减少分布式锁的使用*/
public class LockOptimization {/*** 优化1: 使用数据库唯一约束代替分布式锁*/public void useDatabaseConstraint() {// ❌ 使用分布式锁防止重复订单// RLock lock = redissonClient.getLock("order:create:" + userId);// ✅ 使用数据库唯一索引// ALTER TABLE orders ADD UNIQUE KEY uk_request_id (request_id);try {orderMapper.insert(order);} catch (DuplicateKeyException e) {// 处理重复订单}}/*** 优化2: 使用乐观锁代替分布式锁*/public void useOptimisticLock() {// ❌ 使用分布式锁// RLock lock = redissonClient.getLock("inventory:product:" + productId);// ✅ 使用数据库乐观锁(版本号)Inventory inventory = inventoryMapper.selectById(productId);int oldVersion = inventory.getVersion();inventory.setStock(inventory.getStock() - quantity);int updated = inventoryMapper.updateByIdAndVersion(inventory, oldVersion);if (updated == 0) {// 版本冲突,重试throw new OptimisticLockException();}}/*** 优化3: 减少锁持有时间*/public void minimizeLockTime() {RLock lock = redissonClient.getLock("resource:123");try {lock.lock();// ❌ 在锁内执行耗时操作// heavyComputation();// networkCall();// ✅ 只锁关键操作Resource resource = resourceMapper.selectById(123);resource.setStatus("PROCESSING");resourceMapper.updateById(resource);} finally {lock.unlock();}// 在锁外执行耗时操作heavyComputation();networkCall();}
}

Q9: 分布式锁在微服务架构中如何设计?有哪些最佳实践?

疑问: 在微服务架构中,分布式锁应该如何设计和使用?

解答:

微服务架构中的分布式锁设计:┌─────────────────────────────────────────────────────────┐
│                   微服务架构示例                          │
├─────────────────────────────────────────────────────────┤
│                                                          │
│  服务A ──┐                                               │
│         │                                               │
│  服务B ──┼──> [分布式锁服务] ──┐                          │
│         │                    │                          │
│  服务C ──┘                    ├──> 共享资源(数据库)     │
│                                │                          │
│  服务D ───────────────────────┘                          │
│                                                          │
└─────────────────────────────────────────────────────────┘

最佳实践:

/*** 微服务架构中的分布式锁最佳实践*/
@Service
@Slf4j
public class MicroserviceLockService {@Autowiredprivate RedissonClient redissonClient;/*** 实践1: 锁命名规范(服务名:资源类型:资源ID)*/public void lockNamingConvention(String serviceName, String resourceType, String resourceId) {String lockKey = String.format("%s:%s:%s", serviceName, resourceType, resourceId);// 示例: "order-service:create:user123"// 示例: "inventory-service:deduct:product456"}/*** 实践2: 锁超时时间根据业务场景设置*/public void lockTimeoutByScenario(String operationType) {long timeout;switch (operationType) {case "QUICK":   timeout = 5; break;   // 5秒(快速操作)case "NORMAL":  timeout = 30; break;  // 30秒(一般操作)case "SLOW":    timeout = 300; break; // 5分钟(慢速操作)default:        timeout = 30; break;}RLock lock = redissonClient.getLock("lock:key");try {lock.tryLock(timeout, TimeUnit.SECONDS);// 业务逻辑} finally {if (lock.isHeldByCurrentThread()) {lock.unlock();}}}/*** 实践3: 使用锁模板减少重复代码*/public <T> T executeWithLock(String lockKey, long waitTime, long leaseTime, Supplier<T> supplier) {RLock lock = redissonClient.getLock(lockKey);try {boolean acquired = lock.tryLock(waitTime, leaseTime, TimeUnit.SECONDS);if (!acquired) {throw new LockAcquisitionException("Failed to acquire lock: " + lockKey);}return supplier.get();} catch (InterruptedException e) {Thread.currentThread().interrupt();throw new LockAcquisitionException("Interrupted", e);} finally {if (lock.isHeldByCurrentThread()) {lock.unlock();}}}/*** 实践4: 分布式锁与事务结合使用*/@Transactionalpublic void lockWithTransaction(String resourceId) {String lockKey = "resource:" + resourceId;// 在事务外获取锁RLock lock = redissonClient.getLock(lockKey);try {lock.lock();// 在事务内执行数据库操作Resource resource = resourceMapper.selectById(resourceId);resource.setStatus("PROCESSING");resourceMapper.updateById(resource);// 事务提交后释放锁(在finally中)} finally {lock.unlock();}}/*** 实践5: 监控和告警*/public void lockWithMonitoring(String lockKey) {long startTime = System.currentTimeMillis();RLock lock = redissonClient.getLock(lockKey);try {boolean acquired = lock.tryLock(5, 30, TimeUnit.SECONDS);if (!acquired) {// 记录锁获取失败log.warn("Failed to acquire lock: {}", lockKey);meterRegistry.counter("lock.acquire.failed", "lock", lockKey).increment();return;}// 记录锁获取成功meterRegistry.counter("lock.acquire.success", "lock", lockKey).increment();// 业务逻辑doBusinessLogic();} catch (InterruptedException e) {Thread.currentThread().interrupt();} finally {if (lock.isHeldByCurrentThread()) {long holdTime = System.currentTimeMillis() - startTime;meterRegistry.timer("lock.hold.duration", "lock", lockKey).record(holdTime, TimeUnit.MILLISECONDS);lock.unlock();}}}
}

微服务架构中的注意事项:

1. 锁的粒度要合理✅ 细粒度: 锁单个资源(如单个商品库存)❌ 粗粒度: 锁整个服务(影响性能)2. 避免跨服务锁✅ 锁在服务内部使用❌ 跨多个服务使用同一把锁(增加耦合)3. 锁超时时间要合理✅ 根据业务执行时间设置❌ 设置过长(进程崩溃后锁很久才释放)4. 监控锁的使用情况✅ 记录锁获取成功率、持有时间❌ 不监控(无法发现性能问题)5. 异常处理要完善✅ 确保锁在finally中释放❌ 异常时锁未释放(导致死锁)

9.5 深入技术疑问

Q10: 分布式锁的CAP理论权衡是什么?如何选择?

疑问: 分布式锁在CAP理论中如何权衡一致性、可用性和分区容错性?

解答:

CAP理论在分布式锁中的应用:┌─────────────────────────────────────────────────────────┐
│                   CAP理论                                │
├─────────────────────────────────────────────────────────┤
│  C (Consistency): 一致性                                 │
│    所有节点在同一时刻看到相同的数据                       │
│                                                          │
│  A (Availability): 可用性                                │
│    系统持续可用,每个请求都能得到响应                     │
│                                                          │
│  P (Partition Tolerance): 分区容错性                     │
│    系统在网络分区时仍能继续工作                         │
└─────────────────────────────────────────────────────────┘分布式锁的CAP权衡:Redis分布式锁:- 选择: AP(可用性 + 分区容错性)- 牺牲: C(一致性)- 特点: 高可用、高性能,但可能出现不一致ZooKeeper分布式锁:- 选择: CP(一致性 + 分区容错性)- 牺牲: A(可用性)- 特点: 强一致性,但网络分区时可能不可用

实际场景选择:

/*** CAP理论在分布式锁选择中的应用*/
public class CAPTradeOff {/*** 场景1: 电商库存扣减(选择AP)* * 需求:*  - 高并发(10万+ QPS)*  - 可容忍短暂不一致(最终一致性)*  - 高可用(不能因为锁服务故障导致整个系统不可用)* * 选择: Redis分布式锁(AP)*/public void ecommerceInventory() {// 使用Redis分布式锁// 即使出现短暂不一致,也可以通过最终一致性保证正确性}/*** 场景2: 金融交易(选择CP)* * 需求:*  - 强一致性(不能出现数据不一致)*  - 可容忍短暂不可用(一致性优先)*  - 并发量相对较低(1万+ QPS)* * 选择: ZooKeeper分布式锁(CP)*/public void financialTransaction() {// 使用ZooKeeper分布式锁// 即使网络分区导致不可用,也要保证一致性}/*** 场景3: 配置管理(选择CP)* * 需求:*  - 配置必须一致(所有节点看到相同配置)*  - 可容忍短暂不可用* * 选择: ZooKeeper分布式锁(CP)*/public void configurationManagement() {// 使用ZooKeeper分布式锁}/*** 场景4: 缓存更新(选择AP)* * 需求:*  - 高可用(缓存服务不能影响主业务)*  - 可容忍短暂不一致(缓存可以重建)* * 选择: Redis分布式锁(AP)*/public void cacheUpdate() {// 使用Redis分布式锁}
}

Q11: 分布式锁和消息队列的区别是什么?什么时候用哪个?

疑问: 分布式锁和消息队列都能保证顺序执行,它们有什么区别?

解答:

分布式锁 vs 消息队列:┌─────────────────────────────────────────────────────────┐
│                   分布式锁                               │
├─────────────────────────────────────────────────────────┤
│  用途: 互斥访问(同一时刻只有一个进程能执行)            │
│  模式: 同步阻塞                                          │
│  场景: 防止重复操作、资源竞争                            │
│  示例: 防止重复订单、库存扣减                            │
└─────────────────────────────────────────────────────────┘┌─────────────────────────────────────────────────────────┐
│                   消息队列                               │
├─────────────────────────────────────────────────────────┤
│  用途: 异步处理、解耦、削峰填谷                          │
│  模式: 异步非阻塞                                        │
│  场景: 任务队列、事件驱动、流量控制                      │
│  示例: 订单处理、邮件发送、日志收集                      │
└─────────────────────────────────────────────────────────┘

使用场景对比:

/*** 分布式锁 vs 消息队列使用场景*/
public class LockVsQueue {/*** 场景1: 防止重复订单(使用分布式锁)* * 需求: 同一用户不能同时创建多个订单* 特点: 需要立即返回结果,同步处理*/public Order createOrder(CreateOrderRequest request) {String lockKey = "order:create:" + request.getUserId();RLock lock = redissonClient.getLock(lockKey);try {lock.lock();// 检查是否已存在订单Order existingOrder = orderMapper.selectByRequestId(request.getRequestId());if (existingOrder != null) {return existingOrder;}// 创建订单return createOrderInternal(request);} finally {lock.unlock();}}/*** 场景2: 订单处理(使用消息队列)* * 需求: 订单创建后异步处理(发送邮件、更新库存等)* 特点: 不需要立即返回结果,异步处理*/public void processOrder(Order order) {// 发送到消息队列kafkaTemplate.send("order-created", order);// 立即返回,不等待处理完成}/*** 场景3: 库存扣减(使用分布式锁)* * 需求: 防止超卖,需要立即返回结果*/public void deductInventory(String productId, int quantity) {String lockKey = "inventory:product:" + productId;RLock lock = redissonClient.getLock(lockKey);try {lock.lock();// 扣减库存,立即返回结果Inventory inventory = inventoryMapper.selectById(productId);if (inventory.getStock() >= quantity) {inventory.setStock(inventory.getStock() - quantity);inventoryMapper.updateById(inventory);}} finally {lock.unlock();}}/*** 场景4: 批量处理订单(使用消息队列)* * 需求: 批量处理订单,削峰填谷*/public void batchProcessOrders() {// 订单发送到消息队列List<Order> orders = getPendingOrders();for (Order order : orders) {kafkaTemplate.send("order-processing", order);}// 消费者批量处理// @KafkaListener(topics = "order-processing")// public void processOrder(Order order) { ... }}
}

选择原则:

使用分布式锁:✅ 需要互斥访问(同一时刻只有一个进程能执行)✅ 需要立即返回结果✅ 同步处理✅ 防止重复操作使用消息队列:✅ 需要异步处理✅ 需要解耦✅ 需要削峰填谷✅ 不需要立即返回结果✅ 批量处理

总结

常见疑问快速参考

Q1: 为什么需要分布式锁?
A1: 单机锁无法跨JVM,分布式环境下需要跨进程协调Q2: 分布式锁和数据库事务的区别?
A2: 事务保证单个请求内的原子性,分布式锁保证跨请求的互斥Q3: 为什么用Lua脚本?
A3: 保证原子性,防止误释放其他进程的锁Q4: 为什么需要看门狗?
A4: 自动续期,避免业务执行时间超过锁过期时间Q5: RedLock真的完美吗?
A5: 不完美,存在时钟同步、网络延迟等问题Q6: Redis锁会成为瓶颈吗?
A6: 通常不会,但需要合理设计锁粒度Q7: ZooKeeper为什么更可靠?
A7: 强一致性保证,但性能较低Q8: 什么时候该用分布式锁?
A8: 有共享资源竞争时,但要评估性能影响Q9: 微服务中如何设计?
A9: 合理命名、设置超时、监控告警、异常处理Q10: CAP理论如何权衡?
A10: Redis选择AP,ZooKeeper选择CPQ11: 锁和消息队列的区别?
A11: 锁用于互斥访问,队列用于异步处理

希望这些疑问和解答能帮助你更深入地理解分布式锁!

分布式锁选择建议

选择建议:1. 高并发、低一致性要求 → Redis / Redisson
2. 高一致性要求 → ZooKeeper / etcd
3. 简单场景、低并发 → 数据库锁
4. 需要可重入、公平锁 → Redisson
5. 需要读写锁 → Redisson
6. 需要高可用 → RedLock / ZooKeeper集群

核心要点

✅ 必须设置锁过期时间(防止死锁)
✅ 释放锁时验证value(防止误释放)
✅ 使用Lua脚本保证原子性
✅ 合理设置锁超时时间
✅ 监控锁的使用情况
✅ 异常情况下确保锁释放

参考资源:

  • Redis 官方文档
  • Redisson 官方文档
  • ZooKeeper 官方文档
  • Martin Kleppmann - How to do distributed locking
http://www.dtcms.com/a/602613.html

相关文章:

  • 网站建设评审会禁忌网站
  • 网站后台策划微信如何开小程序
  • 【AI学习-comfyUI学习-抠图+实时图像裁剪-各个部分学习-第九节1】
  • SwiftUI 新特性:Animatable 宏的使用与原理解析
  • 网站制作优质公司html网站后台模板
  • 电子商务网站开发系统江西seo
  • 如何改进网站服务建设和管理安装wordpress到服务器
  • 用帝国做网站怎么样做网站必须知道的问题
  • 一场协议转换的“破壁”之旅:CC-Link IE转PROFINET网关实战手记
  • 【05】方向梯度直方图(HOG)详解:从原理到实现
  • 在淘宝做印刷网站怎么办网站主要栏目
  • 监督对齐DPO算法实例讲解
  • 建设网站应该注意些什么个人域名备案需要多久
  • C语言条件编译的应用与优化技巧 | 探讨条件编译在项目中的实际运用与优化方法
  • 网页数据抓取:融合BeautifulSoup和Scrapy的高级爬虫技术
  • LeetCode 2654. 使数组所有元素变成 1 的最少操作次数 - GCD 思维题详解
  • 站长统计芭乐官方网站下载wordpress移动主题开发教程
  • 网站开发大学是什么专业营销型企业网站系统模板下载
  • JavaScript 35个数组方法完整参数返回值表
  • PPP协议异界冒险:连接神殿的试炼
  • 网站建设的基本条件网站制作协议
  • kotlin build.gradle.kts下修改APK的输出名称
  • 帝国cms地方门户网站模板室内设计效果图素材网站
  • 淘客网站怎么建设小程序商城货源怎么找
  • 在线编译C语言:提升跨平台开发效率
  • 诊断数据库 --- ODX和PDX关系核区别
  • 阿里云申请域名后网站转运网站建设
  • 第13章 函数式语言特性
  • C语言防止反编译工具 | 提高程序安全性并保护源代码
  • 【实战】动态 SQL + 统一 Result + 登录校验:图书管理系统(下)