分布式锁实现详解
一、知识概述
在分布式系统中,多个服务实例可能同时访问共享资源,如数据库、缓存、文件等。为了保证数据一致性,需要一种跨进程、跨机器的互斥机制,这就是分布式锁。分布式锁是实现分布式系统数据一致性的重要手段。
本文将深入讲解分布式锁的实现原理,包括数据库锁、Redis锁、Zookeeper锁等主流方案,分析各种方案的优缺点和适用场景,并提供生产级的实现代码。
二、分布式锁需求分析
2.1 核心特性
/**
* 分布式锁核心特性
*/
public class DistributedLockRequirements {
/**
* 1. 互斥性
* - 任意时刻,只有一个客户端能持有锁
* - 这是最基本的要求
*/
/**
* 2. 防死锁
* - 锁必须有超时机制
* - 客户端崩溃后锁能自动释放
* - 不能出现永久死锁
*/
/**
* 3. 可重入性
* - 同一线程可多次获取同一把锁
* - 避免死锁
*/
/**
* 4. 高可用
* - 锁服务不能成为系统瓶颈
* - 需要容错机制
*/
/**
* 5. 高性能
* - 加锁/解锁延迟低
* - 支持高并发
*/
/**
* 6. 公平性(可选)
* - 按请求顺序获取锁
* - 避免饥饿
*/
}
2.2 方案对比
/**
* 分布式锁方案对比
*/
public class LockSchemesComparison {
/*
┌─────────────────────────────────────────────────────────────────────────────────┐
│ 分布式锁方案对比 │
├──────────────┬──────────┬──────────┬──────────┬──────────┬─────────────────────┤
│ 方案 │ 性能 │ 可靠性 │ 复杂度 │ 可重入 │ 适用场景 │
├──────────────┼──────────┼──────────┼──────────┼──────────┼─────────────────────┤
│ 数据库锁 │ 低 │ 高 │ 低 │ 支持 │ 低并发、强一致 │
│ Redis SETNX │ 高 │ 中 │ 低 │ 不支持 │ 高并发、可容忍丢失 │
│ Redis RedLock│ 中 │ 高 │ 中 │ 不支持 │ 高可用要求 │
│ Zookeeper │ 中 │ 高 │ 高 │ 支持 │ 强一致、低延迟要求低│
│ Etcd │ 中 │ 高 │ 中 │ 支持 │ 云原生环境 │
└──────────────┴──────────┴──────────┴──────────┴──────────┴─────────────────────┘
*/
}
三、数据库实现
3.1 基于唯一索引
/**
* 数据库唯一索引实现分布式锁
*
* 原理:利用数据库唯一索引的互斥性
*/
@Service
public class DatabaseLock {
/**
* 锁表设计
*/
/*
CREATE TABLE `distributed_lock` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`lock_key` varchar(64) NOT NULL COMMENT '锁标识',
`lock_value` varchar(64) NOT NULL COMMENT '锁持有者标识',
`expire_time` datetime NOT NULL COMMENT '过期时间',
`create_time` datetime NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `uk_lock_key` (`lock_key`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
*/
@Autowired
private JdbcTemplate jdbcTemplate;
/**
* 加锁
*/
public boolean tryLock(String lockKey, String lockValue,
long expireSeconds) {
try {
int rows = jdbcTemplate.update(
"INSERT INTO distributed_lock " +
"(lock_key, lock_value, expire_time, create_time) " +
"VALUES (?, ?, DATE_ADD(NOW(), INTERVAL ? SECOND), NOW())",
lockKey, lockValue, expireSeconds);
return rows > 0;
} catch (DuplicateKeyException e) {
// 锁已被占用
return false;
}
}
/**
* 解锁
*/
public boolean unlock(String lockKey, String lockValue) {
int rows = jdbcTemplate.update(
"DELETE FROM distributed_lock " +
"WHERE lock_key = ? AND lock_value = ?",
lockKey, lockValue);
return rows > 0;
}
/**
* 强制解锁(过期清理)
*/
public void forceUnlock(String lockKey) {
jdbcTemplate.update(
"DELETE FROM distributed_lock " +
"WHERE lock_key = ? OR expire_time < NOW()",
lockKey);
}
/**
* 使用示例
*/
public void executeWithLock(String lockKey, Runnable task) {
String lockValue = UUID.randomUUID().toString();
try {
// 尝试加锁
if (tryLock(lockKey, lockValue, 30)) {
try {
// 执行业务逻辑
task.run();
} finally {
// 释放锁
unlock(lockKey, lockValue);
}
} else {
throw new LockException("获取锁失败: " + lockKey);
}
} catch (Exception e) {
// 处理异常
throw new LockException("锁操作异常", e);
}
}
}
3.2 基于乐观锁
/**
* 数据库乐观锁实现
*/
@Service
public class OptimisticLock {
/**
* 带版本号的表
*/
/*
CREATE TABLE `resource_lock` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`resource_key` varchar(64) NOT NULL COMMENT '资源标识',
`version` int(11) NOT NULL DEFAULT 0 COMMENT '版本号',
`locked` tinyint(1) NOT NULL DEFAULT 0 COMMENT '是否锁定',
`lock_holder` varchar(64) DEFAULT NULL COMMENT '锁持有者',
`update_time` datetime NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `uk_resource_key` (`resource_key`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
*/
@Autowired
private JdbcTemplate jdbcTemplate;
/**
* 加锁(CAS操作)
*/
public boolean tryLock(String resourceKey, String lockHolder) {
// 先查询当前状态
Map<String, Object> current = jdbcTemplate.queryForMap(
"SELECT * FROM resource_lock WHERE resource_key = ?",
resourceKey);
int version = (Integer) current.get("version");
// CAS更新
int rows = jdbcTemplate.update(
"UPDATE resource_lock SET locked = 1, lock_holder = ?, " +
"version = version + 1, update_time = NOW() " +
"WHERE resource_key = ? AND version = ? AND locked = 0",
lockHolder, resourceKey, version);
return rows > 0;
}
/**
* 解锁
*/
public boolean unlock(String resourceKey, String lockHolder) {
int rows = jdbcTemplate.update(
"UPDATE resource_lock SET locked = 0, lock_holder = NULL, " +
"version = version + 1, update_time = NOW() " +
"WHERE resource_key = ? AND lock_holder = ?",
resourceKey, lockHolder);
return rows > 0;
}
}
四、Redis 实现
4.1 SETNX 实现
/**
* Redis SETNX 实现分布式锁
*/
@Service
public class RedisDistributedLock {
@Autowired
private RedisTemplate<String, String> redisTemplate;
private static final String LOCK_PREFIX = "lock:";
/**
* 简单加锁
* 问题:SETNX + EXPIRE 不是原子操作
*/
@Deprecated
public boolean tryLockSimple(String lockKey, String lockValue,
long expireTime, TimeUnit unit) {
// 问题:如果 SETNX 成功但 EXPIRE 失败,会导致死锁
Boolean success = redisTemplate.opsForValue()
.setIfAbsent(LOCK_PREFIX + lockKey, lockValue);
if (Boolean.TRUE.equals(success)) {
redisTemplate.expire(LOCK_PREFIX + lockKey, expireTime, unit);
return true;
}
return false;
}
/**
* 加锁(原子操作)
* 使用 SET key value NX EX seconds
*/
public boolean tryLock(String lockKey, String lockValue,
long expireTime, TimeUnit unit) {
String key = LOCK_PREFIX + lockKey;
// 使用 SET NX EX 原子操作
Boolean success = redisTemplate.opsForValue()
.setIfAbsent(key, lockValue, expireTime, unit);
return Boolean.TRUE.equals(success);
}
/**
* 加锁(带重试)
*/
public boolean tryLockWithRetry(String lockKey, String lockValue,
long expireTime, TimeUnit unit,
long waitTime, TimeUnit waitUnit) {
long startTime = System.currentTimeMillis();
long waitMillis = waitUnit.toMillis(waitTime);
while (true) {
// 尝试加锁
if (tryLock(lockKey, lockValue, expireTime, unit)) {
return true;
}
// 检查是否超时
if (System.currentTimeMillis() - startTime > waitMillis) {
return false;
}
// 短暂等待后重试
try {
Thread.sleep(50);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
return false;
}
}
}
/**
* 解锁(Lua脚本保证原子性)
* 只有锁的持有者才能解锁
*/
public boolean unlock(String lockKey, String lockValue) {
String key = LOCK_PREFIX + lockKey;
// Lua脚本:检查并删除
String script =
"if redis.call('get', KEYS[1]) == ARGV[1] then " +
" return redis.call('del', KEYS[1]) " +
"else " +
" return 0 " +
"end";
RedisScript<Long> redisScript = RedisScript.of(script, Long.class);
Long result = redisTemplate.execute(
redisScript, Collections.singletonList(key), lockValue);
return result != null && result > 0;
}
/**
* 看门狗(自动续期)
*/
public class LockWatchdog {
private final ScheduledExecutorService scheduler =
Executors.newScheduledThreadPool(1);
private final Map<String, ScheduledFuture<?>> tasks =
new ConcurrentHashMap<>();
/**
* 加锁并启动看门狗
*/
public boolean tryLockWithWatchdog(String lockKey, String lockValue,
long expireTime, TimeUnit unit) {
if (!tryLock(lockKey, lockValue, expireTime, unit)) {
return false;
}
// 启动续期任务
long expireMillis = unit.toMillis(expireTime);
long renewInterval = expireMillis / 3;
ScheduledFuture<?> future = scheduler.scheduleAtFixedRate(() -> {
renewLock(lockKey, lockValue, expireTime, unit);
}, renewInterval, renewInterval, TimeUnit.MILLISECONDS);
tasks.put(lockKey, future);
return true;
}
/**
* 解锁并停止看门狗
*/
public boolean unlockWithWatchdog(String lockKey, String lockValue) {
// 停止续期任务
ScheduledFuture<?> future = tasks.remove(lockKey);
if (future != null) {
future.cancel(true);
}
// 解锁
return unlock(lockKey, lockValue);
}
/**
* 续期
*/
private void renewLock(String lockKey, String lockValue,
long expireTime, TimeUnit unit) {
String key = LOCK_PREFIX + lockKey;
// Lua脚本:检查并续期
String script =
"if redis.call('get', KEYS[1]) == ARGV[1] then " +
" return redis.call('expire', KEYS[1], ARGV[2]) " +
"else " +
" return 0 " +
"end";
RedisScript<Long> redisScript = RedisScript.of(script, Long.class);
redisTemplate.execute(redisScript,
Collections.singletonList(key),
lockValue,
String.valueOf(unit.toSeconds(expireTime)));
}
}
}
4.2 Redisson 实现
/**
* Redisson 分布式锁实现
*
* 特点:
* - 可重入锁
* - 看门狗自动续期
* - 公平锁
* - 读写锁
*/
@Service
public class RedissonLockService {
@Autowired
private RedissonClient redissonClient;
/**
* 可重入锁
*/
public void reentrantLock(String lockKey, Runnable task) {
RLock lock = redissonClient.getLock(lockKey);
try {
// 尝试加锁,最多等待100秒,锁自动过期时间10秒
boolean acquired = lock.tryLock(100, 10, TimeUnit.SECONDS);
if (acquired) {
try {
task.run();
} finally {
lock.unlock();
}
} else {
throw new LockException("获取锁超时");
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
throw new LockException("获取锁被中断", e);
}
}
/**
* 公平锁
*/
public void fairLock(String lockKey, Runnable task) {
RLock lock = redissonClient.getFairLock(lockKey);
try {
if (lock.tryLock(10, TimeUnit.SECONDS)) {
try {
task.run();
} finally {
lock.unlock();
}
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
/**
* 读写锁
*/
public class ReadWriteLockExample {
private final RReadWriteLock rwLock;
public ReadWriteLockExample(String lockKey) {
this.rwLock = redissonClient.getReadWriteLock(lockKey);
}
/**
* 读锁
*/
public void readLock(Runnable task) {
RLock lock = rwLock.readLock();
try {
lock.lock();
task.run();
} finally {
lock.unlock();
}
}
/**
* 写锁
*/
public void writeLock(Runnable task) {
RLock lock = rwLock.writeLock();
try {
lock.lock();
task.run();
} finally {
lock.unlock();
}
}
}
/**
* 联锁(MultiLock)
* 同时锁定多个资源
*/
public void multiLock(List<String> lockKeys, Runnable task) {
RLock[] locks = lockKeys.stream()
.map(redissonClient::getLock)
.toArray(RLock[]::new);
RedissonMultiLock multiLock = new RedissonMultiLock(locks);
try {
if (multiLock.tryLock(10, 30, TimeUnit.SECONDS)) {
try {
task.run();
} finally {
multiLock.unlock();
}
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
/**
* 红锁(RedLock)
* 在多个 Redis 实例上同时加锁
*/
public void redLock(List<String> lockKeys, Runnable task) {
RLock[] locks = lockKeys.stream()
.map(redissonClient::getLock)
.toArray(RLock[]::new);
RedissonRedLock redLock = new RedissonRedLock(locks);
try {
if (redLock.tryLock(10, 30, TimeUnit.SECONDS)) {
try {
task.run();
} finally {
redLock.unlock();
}
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
/**
* 信号量
*/
public void semaphore(String semaphoreKey, int permits, Runnable task) {
RSemaphore semaphore = redissonClient.getSemaphore(semaphoreKey);
try {
// 初始化信号量
semaphore.trySetPermits(permits);
// 获取许可
semaphore.acquire();
try {
task.run();
} finally {
semaphore.release();
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
}
4.3 RedLock 算法
/**
* RedLock 算法实现
*
* 由 Redis 作者 Antirez 提出
* 解决 Redis 主从切换导致的锁丢失问题
*/
@Service
public class RedLockService {
private final List<RedisTemplate<String, String>> redisTemplates;
public RedLockService(List<RedisTemplate<String, String>> redisTemplates) {
this.redisTemplates = redisTemplates;
}
/**
* RedLock 加锁流程
*
* 1. 获取当前时间戳
* 2. 依次向所有 Redis 实例尝试加锁
* 3. 计算加锁成功数和耗时
* 4. 成功数超过半数且耗时小于锁过期时间,则加锁成功
* 5. 否则解锁所有实例
*/
public boolean tryLock(String lockKey, String lockValue,
long expireTime, TimeUnit unit) {
long startTime = System.currentTimeMillis();
long expireMillis = unit.toMillis(expireTime);
int successCount = 0;
// 依次向所有 Redis 实例加锁
for (RedisTemplate<String, String> redisTemplate : redisTemplates) {
try {
Boolean success = redisTemplate.opsForValue()
.setIfAbsent(lockKey, lockValue, expireTime, unit);
if (Boolean.TRUE.equals(success)) {
successCount++;
}
} catch (Exception e) {
// 忽略单个实例的错误
}
}
// 计算耗时
long elapsed = System.currentTimeMillis() - startTime;
// 判断是否成功
// 1. 成功数超过半数
// 2. 耗时小于锁过期时间
if (successCount > redisTemplates.size() / 2 && elapsed < expireMillis) {
return true;
}
// 加锁失败,解锁所有实例
unlock(lockKey, lockValue);
return false;
}
/**
* 解锁所有实例
*/
public void unlock(String lockKey, String lockValue) {
for (RedisTemplate<String, String> redisTemplate : redisTemplates) {
try {
String script =
"if redis.call('get', KEYS[1]) == ARGV[1] then " +
" return redis.call('del', KEYS[1]) " +
"else " +
" return 0 " +
"end";
RedisScript<Long> redisScript = RedisScript.of(script, Long.class);
redisTemplate.execute(redisScript,
Collections.singletonList(lockKey), lockValue);
} catch (Exception e) {
// 忽略解锁失败
}
}
}
}
五、Zookeeper 实现
5.1 临时节点实现
/**
* Zookeeper 临时节点实现分布式锁
*
* 原理:
* - 创建临时节点,成功则获取锁
* - 删除节点则释放锁
* - 会话断开自动删除临时节点
*/
@Service
public class ZkDistributedLock {
private final CuratorFramework zkClient;
private final String lockPath;
public ZkDistributedLock(CuratorFramework zkClient, String lockPath) {
this.zkClient = zkClient;
this.lockPath = lockPath;
}
/**
* 简单加锁
*/
public boolean tryLock(String lockKey) {
try {
zkClient.create()
.creatingParentsIfNeeded()
.withMode(CreateMode.EPHEMERAL)
.forPath(lockPath + "/" + lockKey);
return true;
} catch (Exception e) {
return false;
}
}
/**
* 解锁
*/
public void unlock(String lockKey) {
try {
zkClient.delete().forPath(lockPath + "/" + lockKey);
} catch (Exception e) {
// 忽略
}
}
}
5.2 临时顺序节点实现
/**
* Zookeeper 临时顺序节点实现公平锁
*
* 原理:
* 1. 在锁节点下创建临时顺序节点
* 2. 获取所有子节点,判断自己是否是最小序号
* 3. 如果是,获取锁成功
* 4. 如果不是,监听前一个节点
* 5. 前一个节点删除时,被唤醒
*/
@Service
public class ZkFairLock {
private final CuratorFramework zkClient;
private final String lockPath;
private final ThreadLocal<String> currentPath = new ThreadLocal<>();
public ZkFairLock(CuratorFramework zkClient, String lockPath) {
this.zkClient = zkClient;
this.lockPath = lockPath;
}
/**
* 加锁
*/
public void lock() throws Exception {
// 1. 创建临时顺序节点
String path = zkClient.create()
.creatingParentsIfNeeded()
.withMode(CreateMode.EPHEMERAL_SEQUENTIAL)
.forPath(lockPath + "/lock-");
currentPath.set(path);
while (true) {
// 2. 获取所有子节点
List<String> children = zkClient.getChildren().forPath(lockPath);
Collections.sort(children);
// 3. 获取当前节点的序号
String currentNode = path.substring(path.lastIndexOf('/') + 1);
int currentIndex = children.indexOf(currentNode);
// 4. 如果是最小序号,获取锁成功
if (currentIndex == 0) {
return;
}
// 5. 否则监听前一个节点
String prevNode = children.get(currentIndex - 1);
String prevPath = lockPath + "/" + prevNode;
// 使用 CountDownLatch 等待
CountDownLatch latch = new CountDownLatch(1);
// 监听前一个节点
Watcher watcher = event -> {
if (event.getType() == Watcher.Event.EventType.NodeDeleted) {
latch.countDown();
}
};
// 检查前一个节点是否存在
Stat stat = zkClient.checkExists()
.usingWatcher(watcher)
.forPath(prevPath);
if (stat == null) {
// 前一个节点已不存在,重试
continue;
}
// 等待前一个节点删除
latch.await();
}
}
/**
* 解锁
*/
public void unlock() {
try {
String path = currentPath.get();
if (path != null) {
zkClient.delete().forPath(path);
currentPath.remove();
}
} catch (Exception e) {
// 忽略
}
}
}
5.3 Curator InterProcessMutex
/**
* Curator 框架提供的分布式锁实现
*/
@Service
public class CuratorLockService {
private final CuratorFramework zkClient;
public CuratorLockService(CuratorFramework zkClient) {
this.zkClient = zkClient;
}
/**
* 可重入锁
*/
public void reentrantLock(String lockPath, Runnable task) {
InterProcessMutex lock = new InterProcessMutex(zkClient, lockPath);
try {
// 尝试加锁,最多等待10秒
if (lock.acquire(10, TimeUnit.SECONDS)) {
try {
task.run();
} finally {
lock.release();
}
} else {
throw new LockException("获取锁超时");
}
} catch (Exception e) {
throw new LockException("锁操作异常", e);
}
}
/**
* 不可重入锁
*/
public void semaphoreLock(String lockPath, Runnable task) {
InterProcessSemaphoreMutex lock =
new InterProcessSemaphoreMutex(zkClient, lockPath);
try {
if (lock.acquire(10, TimeUnit.SECONDS)) {
try {
task.run();
} finally {
lock.release();
}
}
} catch (Exception e) {
throw new LockException("锁操作异常", e);
}
}
/**
* 读写锁
*/
public class ZkReadWriteLock {
private final InterProcessReadWriteLock rwLock;
public ZkReadWriteLock(String lockPath) {
this.rwLock = new InterProcessReadWriteLock(zkClient, lockPath);
}
public void readLock(Runnable task) {
InterProcessMutex lock = rwLock.readLock();
try {
lock.acquire();
try {
task.run();
} finally {
lock.release();
}
} catch (Exception e) {
throw new LockException("读锁操作异常", e);
}
}
public void writeLock(Runnable task) {
InterProcessMutex lock = rwLock.writeLock();
try {
lock.acquire();
try {
task.run();
} finally {
lock.release();
}
} catch (Exception e) {
throw new LockException("写锁操作异常", e);
}
}
}
/**
* 联锁(多锁)
*/
public void multiLock(List<String> lockPaths, Runnable task) {
List<InterProcessMutex> locks = lockPaths.stream()
.map(path -> new InterProcessMutex(zkClient, path))
.collect(Collectors.toList());
InterProcessMultiLock multiLock = new InterProcessMultiLock(locks);
try {
if (multiLock.acquire(10, TimeUnit.SECONDS)) {
try {
task.run();
} finally {
multiLock.release();
}
}
} catch (Exception e) {
throw new LockException("联锁操作异常", e);
}
}
}
六、最佳实践
6.1 锁使用模板
/**
* 分布式锁使用最佳实践
*/
@Service
public class LockBestPractices {
@Autowired
private RedissonLockService redissonLockService;
/**
* 标准锁使用模式
*/
public <T> T executeWithLock(String lockKey, long waitTime,
long leaseTime, TimeUnit unit,
Supplier<T> supplier) {
RLock lock = redissonClient.getLock(lockKey);
try {
// 1. 尝试获取锁
if (!lock.tryLock(waitTime, leaseTime, unit)) {
throw new LockException("获取锁失败: " + lockKey);
}
try {
// 2. 执行业务逻辑
return supplier.get();
} finally {
// 3. 确保释放锁
if (lock.isHeldByCurrentThread()) {
lock.unlock();
}
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
throw new LockException("获取锁被中断", e);
}
}
/**
* 带降级的锁使用
*/
public <T> T executeWithLockFallback(String lockKey,
Supplier<T> supplier,
Supplier<T> fallback) {
RLock lock = redissonClient.getLock(lockKey);
try {
if (lock.tryLock(3, 10, TimeUnit.SECONDS)) {
try {
return supplier.get();
} finally {
lock.unlock();
}
} else {
// 获取锁失败,执行降级逻辑
return fallback.get();
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
return fallback.get();
}
}
/**
* 防止锁失效
*/
public void safeLockUsage(String lockKey, Runnable task) {
RLock lock = redissonClient.getLock(lockKey);
try {
// 使用看门狗自动续期
lock.lock(30, TimeUnit.SECONDS);
try {
task.run();
} finally {
// 检查锁是否仍由当前线程持有
if (lock.isHeldByCurrentThread()) {
lock.unlock();
}
}
} catch (Exception e) {
// 异常处理
log.error("锁操作异常", e);
}
}
}
6.2 注意事项
/**
* 分布式锁注意事项
*/
public class LockPrecautions {
/**
* 1. 锁粒度
*/
// 锁粒度越细越好
// 锁范围越小越好
// 好的做法:锁具体资源
String lockKey = "order:" + orderId;
// 不好的做法:锁范围太大
String lockKey = "order";
/**
* 2. 锁超时
*/
// 必须设置合理的超时时间
// 过短:业务未执行完锁就释放
// 过长:异常时锁长时间不释放
/**
* 3. 异常处理
*/
// 加锁失败的处理
// 锁续期失败的处理
// 解锁失败的处理
/**
* 4. 死锁预防
*/
// 设置超时时间
// 使用可重入锁
// 按固定顺序加锁
/**
* 5. 性能优化
*/
// 减少锁持有时间
// 使用分段锁
// 考虑读写分离
/**
* 6. 监控告警
*/
// 锁等待时间监控
// 锁持有时间监控
// 锁获取失败率监控
}
六、思考与练习
思考题
-
基础题:Redis的SETNX + EXPIRE为什么不是原子操作?使用SET key value NX EX命令解决了什么问题?
-
进阶题:RedLock算法的核心思想是什么?为什么需要在多个Redis实例上同时加锁?它有什么争议?
-
实战题:在Redis主从架构下,主节点加锁成功但未同步到从节点就宕机,会发生什么?如何避免这种情况?
编程练习
练习:使用Redisson实现一个分布式锁工具类,要求:(1) 支持可重入锁;(2) 实现看门狗自动续期;(3) 提供加锁失败时的降级策略;(4) 完善的异常处理和日志记录。
章节关联
- 前置章节:分布式ID生成详解
- 后续章节:分布式事务详解
- 扩展阅读:Redis RedLock文档、Martin Kleppmann对RedLock的批评文章
📝 下一章预告
分布式锁解决了资源互斥访问问题,但分布式系统中的事务一致性更为复杂。下一章将深入讲解分布式事务的理论与实现,包括2PC/3PC、TCC、Saga等主流方案。
本章完
参考资料:
- Redis SET 命令:redis.io/commands/se…
- RedLock 算法:redis.io/topics/dist…
- Curator Recipes:curator.apache.org/curator-rec…
- Redisson 文档:github.com/redisson/re…