Redis分布式事务与锁全解析

第一章:分布式事务基础理论

1.1 ACID 与 CAP 定理回顾

ACID 特性

  • 原子性(Atomicity):事务要么全部成功提交,要么全部失败回滚,中间不可见半成品。
  • 一致性(Consistency):事务执行前后,系统都必须处于合法状态(满足所有约束)。
  • 隔离性(Isolation):并发执行的事务之间不会相互干扰,隔离级别定义了“可见性”边界。
  • 持久性(Durability):事务一旦提交,其结果对系统是永久性的,即使发生故障也不丢失。

CAP 定理

在分布式系统中,不可能同时满足以下三点,只能选择两项:

  • Consistency(一致性):所有节点在同一时间看到相同的数据视图。
  • Availability(可用性):每次请求都能获得非错误响应。
  • Partition tolerance(分区容忍性):系统能容忍网络分区,保证继续提供服务。
对比:传统单体数据库追求 ACID;分布式系统根据业务侧重点,在 CAP 中做平衡。

1.2 分布式事务常见解决方案对比

模型原理优缺点
2PC(二阶段提交)协调者先询问所有参与者能否提交(Prepare),然后决定提交或回滚(Commit/Rollback)✅ 简单
❌ 易阻塞、单点协调者故障影响全局、性能开销大
3PC(三阶段提交)在 2PC 基础上增加“预提交”阶段(Prepare→PreCommit→Commit)✅ 减少阻塞风险
❌ 实现复杂,仍无法解决网络分区下的安全性
Saga(补偿事务)将大事务拆为若干本地事务,失败时依次执行补偿操作逆转✅ 无全局锁、无阻塞
❌ 补偿逻辑复杂、状态管理难、牵涉多方业务解耦
XSAGA基于 Saga 的扩展,结合消息队列与状态机管理分布式事务✅ 异步解耦、高可用
❌ 开发成本高,需要异步可靠消息与状态机组件
flowchart LR
  subgraph 2PC
    A[协调者: Prepare?] --> B[参与者1: OK/NO]
    A --> C[参与者2: OK/NO]
    B & C --> D[协调者: Commit/Rollback]
  end

1.3 Redis 在分布式事务体系中的定位

  1. 原子性命令

    • Redis 单条命令天然原子(如 INCR, HSET),无需额外加锁即可保证局部一致。
  2. MULTI/EXEC 事务

    • 将多条命令打包,在执行时保证中途不被其他命令插入,但不支持回滚;失败时会跳过出错命令继续。
  3. WATCH 乐观锁

    • 监控一个或多个 key,若在事务执行前有修改,整个事务会被中止
局限:Redis 自身不支持分布式事务协调,需配合应用侧逻辑或外部协调组件才能实现跨多个服务或数据源的一致性。

1.4 事务与锁:基础概念与关系

  • 事务(Transaction):逻辑上将一组操作视为一个整体,要么全部成功,要么全部回滚。
  • 锁(Lock):用于在并发场景下对某个资源或数据行加排它或共享控制,防止并发冲突。
特性事务
关注点处理多步操作的一致性控制并发对单个资源或对象的访问
实现方式协调者 + 协议(如 2PC、Saga)或数据库自带事务支持悲观锁(排它锁)、乐观锁(版本/ CAS)、分布式锁(Redis、Zookeeper)
回滚机制支持(数据库或应用需实现补偿)不支持回滚;锁只是并发控制,解锁后资源状态根据业务决定
使用场景跨服务、跨库的强一致性场景并发写场景、资源争用高的局部协调

第二章:Redis 原子操作与事务命令

2.1 MULTI/EXEC/DISCARD 四大命令详解

Redis 提供了原生的事务支持,通过以下命令组合完成:

  1. MULTI
    开始一个事务,将后续命令入队,不立即执行。
  2. EXEC
    执行事务队列中的所有命令,作为一个批次原子提交。
  3. DISCARD
    放弃事务队列中所有命令,退出事务模式。
  4. UNWATCH(或隐含于 EXEC/DISCARD 后)
    解除对所有 key 的 WATCH 监控。
Client> MULTI
OK
Client> SET user:1:name "Alice"
QUEUED
Client> INCR user:1:counter
QUEUED
Client> EXEC
1) OK
2) (integer) 1
  • 在 MULTI 与 EXEC 之间,所有写命令均返回 QUEUED 而不执行。
  • 若执行过程中某条命令出错(如语法错误),该命令会在 EXEC 时被跳过,其它命令依然执行。
  • EXEC 之后,事务队列自动清空,返回结果列表。

2.2 事务队列实现原理

内部流程简化示意:

flowchart LR
    subgraph Server
      A[Client MULTI] --> B[enter MULTI state]
      B --> C[queue commands]
      C --> D[Client EXEC]
      D --> E[execute queued commands one by one]
      E --> F[exit MULTI state]
    end
  • Redis 仅在内存中维护一个简单的命令数组,不做持久化。
  • 由于单线程模型,EXEC 阶段不会被其他客户端命令插入,保证了“原子”提交的效果。
  • 事务并不支持回滚:一旦 EXEC 开始,出错命令跳过也不影响其它操作。

2.3 WATCH 的乐观锁机制

WATCH 命令用来在事务前做乐观并发控制:

  1. 客户端 WATCH key1 key2 …,服务器会在内存中记录被监控的 key。
  2. 执行 MULTI 入队命令。
  3. 若执行 EXEC 前其他客户端对任一 watched key 执行了写操作,当前事务会失败,返回 nil
Client1> WATCH user:1:counter
OK
Client1> MULTI
OK
Client1> INCR user:1:counter
QUEUED
# 在此期间,Client2 执行 INCR user:1:counter
Client1> EXEC
(nil)             # 事务因 watched key 被修改而放弃
  • UNWATCH:可在多 key 监控后决定放弃事务前,手动取消监控。
  • WATCH+MULTI+EXEC 模式被视作“乐观锁”:假设冲突少,事务提交前无需加锁,冲突时再回退重试。

2.4 事务冲突场景与重试策略示例

在高并发场景下,WATCH 模式下冲突不可避免。常见重试模式:

import redis
r = redis.Redis()

def incr_user_counter(uid):
    key = f"user:{uid}:counter"
    while True:
        try:
            r.watch(key)
            count = int(r.get(key) or 0)
            pipe = r.pipeline()
            pipe.multi()
            pipe.set(key, count + 1)
            pipe.execute()
            break
        except redis.WatchError:
            # 发生冲突,重试
            continue
        finally:
            r.unwatch()
  • 流程

    1. WATCH 监控 key
    2. 读取当前值
    3. MULTI -> 修改 -> EXEC
    4. 若冲突(WatchError),则重试整个流程
  • 图解
sequenceDiagram
    participant C as Client
    participant S as Server
    C->>S: WATCH key
    S-->>C: OK
    C->>S: GET key
    S-->>C: value
    C->>S: MULTI
    S-->>C: OK
    C->>S: SET key newValue
    S-->>C: QUEUED
    C->>C: (some other client modifies key)
    C->>S: EXEC
    alt key changed
      S-->>C: nil (transaction aborted)
    else
      S-->>C: [OK]
    end
  • 重试注意:应设定最大重试次数或退避策略,避免活锁。

第三章:悲观锁与乐观锁在 Redis 中的实现

3.1 悲观锁概念与使用场景

悲观锁(Pessimistic Locking) 假定并发冲突随时会发生,因此,访问共享资源前先获取互斥锁,直到操作完成才释放锁,期间其他线程被阻塞或直接拒绝访问。

  • 适用场景

    • 写多读少、冲突概率高的关键资源(如库存、账户余额)。
    • 对一致性要求极高,无法容忍重试失败或脏读的业务。

3.2 Redis 分布式悲观锁(SETNX + TTL)

通过 SETNX(SET if Not eXists)命令实现基本分布式锁:

SET lock:key uuid NX PX 5000
  • NX:仅当 key 不存在时设置,保证互斥。
  • PX 5000:设置过期时间,避免死锁。

Java 示例(Jedis)

public boolean tryLock(Jedis jedis, String lockKey, String requestId, int expireTime) {
    String result = jedis.set(lockKey, requestId, SetParams.setParams().nx().px(expireTime));
    return "OK".equals(result);
}

public boolean releaseLock(Jedis jedis, String lockKey, String requestId) {
    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, Collections.singletonList(lockKey),
                               Collections.singletonList(requestId));
    return Long.valueOf(1).equals(result);
}
  • 优点:实现简单,基于 Redis 原子命令。
  • 缺点:不支持自动续期、SETNX 与 DEL 之间存在时机窗口。

3.3 乐观锁实现:基于版本号与事务重试

乐观锁(Optimistic Locking) 假定冲突少,通过版本号或时间戳检查,只有在操作前后资源未被修改才提交。

  • 实现方式:使用 Redis 的 WATCH + MULTI/EXEC
import redis
r = redis.Redis()

def update_balance(uid, delta):
    key = f"user:{uid}:balance"
    while True:
        try:
            r.watch(key)
            balance = float(r.get(key) or 0)
            new_balance = balance + delta
            pipe = r.pipeline()
            pipe.multi()
            pipe.set(key, new_balance)
            pipe.execute()
            break
        except redis.WatchError:
            # 冲突,重试
            continue
        finally:
            r.unwatch()
  • 优点:无锁等待成本,适合读多写少场景。
  • 缺点:在高并发写场景下可能频繁重试,性能下降。

3.4 代码示例:悲观锁与乐观锁对比实战

下面示例展示库存扣减场景的两种锁策略对比。

// 悲观锁方案:SETNX
public boolean decrementStockPessimistic(Jedis jedis, String productId) {
    String lockKey = "lock:stock:" + productId;
    String requestId = UUID.randomUUID().toString();
    if (!tryLock(jedis, lockKey, requestId, 3000)) {
        return false; // 获取锁失败
    }
    try {
        int stock = Integer.parseInt(jedis.get("stock:" + productId));
        if (stock <= 0) return false;
        jedis.decr("stock:" + productId);
        return true;
    } finally {
        releaseLock(jedis, lockKey, requestId);
    }
}

// 乐观锁方案:WATCH + MULTI
public boolean decrementStockOptimistic(Jedis jedis, String productId) {
    String key = "stock:" + productId;
    while (true) {
        jedis.watch(key);
        int stock = Integer.parseInt(jedis.get(key));
        if (stock <= 0) {
            jedis.unwatch();
            return false;
        }
        Transaction tx = jedis.multi();
        tx.decr(key);
        List<Object> res = tx.exec();
        if (res != null) {
            return true; // 成功
        }
        // 冲突,重试
    }
}
  • 对比

    • 悲观锁在高并发写时因为互斥性可能成为瓶颈;
    • 乐观锁则可能因冲突频繁重试而浪费 CPU 和网络资源。

第四章:RedLock 深度解析

4.1 RedLock 算法背景与设计目标

RedLock 是 Redis 创始人 Antirez 提出的分布式锁算法,旨在通过多个独立 Redis 节点协同工作,解决单节点故障时锁可能失效的问题。

  • 设计目标

    1. 安全性:获取锁后,只有持锁者才能解锁,防止误删他人锁。
    2. 可用性:即使部分 Redis 节点故障,只要大多数节点可用,仍可获取锁。
    3. 性能:锁获取、释放的延迟保持在可接受范围。

4.2 RedLock 详细流程图解

sequenceDiagram
  participant C as Client
  participant N1 as Redis1
  participant N2 as Redis2
  participant N3 as Redis3
  participant N4 as Redis4
  participant N5 as Redis5

  C->>N1: SET lock_key val NX PX ttl
  C->>N2: SET lock_key val NX PX ttl
  C->>N3: SET lock_key val NX PX ttl
  C->>N4: SET lock_key val NX PX ttl
  C->>N5: SET lock_key val NX PX ttl
  Note right of C: 如果超过半数节点成功,且<br/>总耗时 < ttl,则获取锁成功

步骤

  1. 客户端生成唯一随机值 val 作为请求标识。
  2. 按顺序向 N 个 Redis 实例发送 SET key val NX PX ttl,使用短超时保证请求不阻塞。
  3. 计算成功设置锁的节点数量 count,以及从第一台开始到最后一台花费的总时延 elapsed
  4. count >= N/2 + 1elapsed < ttl,则视为获取锁成功;否则视为失败,并向已成功节点发送 DEL key 释放锁。

4.3 实现代码剖析(Java 示例)

public class RedLock {
    private List<JedisPool> pools;
    private long ttl;
    private long acquireTimeout;

    public boolean lock(String key, String value) {
        int successCount = 0;
        long startTime = System.currentTimeMillis();
        for (JedisPool pool : pools) {
            try (Jedis jedis = pool.getResource()) {
                String resp = jedis.set(key, value, SetParams.setParams().nx().px(ttl));
                if ("OK".equals(resp)) successCount++;
            } catch (Exception e) { /* 忽略单节点故障 */ }
        }
        long elapsed = System.currentTimeMillis() - startTime;
        if (successCount >= pools.size() / 2 + 1 && elapsed < ttl) {
            return true;
        } else {
            // 释放已获取的锁
            unlock(key, value);
            return false;
        }
    }

    public void unlock(String key, String value) {
        String lua = 
          "if redis.call('get', KEYS[1]) == ARGV[1] then " +
          "  return redis.call('del', KEYS[1]) " +
          "else return 0 end";
        for (JedisPool pool : pools) {
            try (Jedis jedis = pool.getResource()) {
                jedis.eval(lua, Collections.singletonList(key),
                           Collections.singletonList(value));
            } catch (Exception e) { /* 忽略 */ }
        }
    }
}
  • 关键点

    • 使用相同 value 确保解锁安全。
    • 超时判断:若总耗时超过 ttl,即便设置足够节点,也视为失败,防止锁已过期。
    • 异常处理:忽略部分节点故障,但依赖多数节点可用。

4.4 RedLock 的安全性与争议

  • 安全性分析

    • N/2+1 节点写入成功的前提下,即使部分节点宕机,也能保留锁权。
    • 随机 value 确保只有真正持有者能解锁。
  • 争议点

    • 网络延迟波动 可能导致 elapsed < ttl 判定失效,从而出现锁重入风险。
    • 时钟漂移:RedLock 假设各个 Redis 节点时钟同步,否则 PX 过期可能不一致。
    • 社区质疑:部分专家认为单节点 SETNX + TTL 足以满足大多数分布式锁场景,RedLock 复杂度与收益不匹配。

第五章:基于 Lua 脚本的分布式锁增强

Lua 脚本在 Redis 中以“原子批处理”的方式执行,保证脚本内所有命令在一个上下文中顺序执行,不会被其他客户端命令打断。利用这一特性,可以实现更加安全、灵活的分布式锁。


5.1 Lua 原子性保证与使用场景

  • 原子执行:当你向 Redis 发送 EVAL 脚本时,服务器将整个脚本当作一个命令执行,期间不会切换到其他客户端。
  • 脚本场景

    • 自动续期(Watchdog)
    • 安全解锁(检查 value 后再 DEL)
    • 可重入锁(记录重入次数)
    • 锁队列(实现公平锁)

5.2 典型锁脚本实现(一):安全解锁

-- KEYS[1] = lockKey, ARGV[1] = ownerId
if redis.call("GET", KEYS[1]) == ARGV[1] then
    return redis.call("DEL", KEYS[1])
else
    return 0
end
  • 流程

    1. GET lockKey,与持有者 ID (UUID) 做比对
    2. 匹配时才 DEL lockKey,否则返回 0
  • 效果:保证只有真正持锁者才能解锁,防止误删他人锁。

5.3 典型锁脚本实现(二):自动续期 Watchdog

当锁持有时间可能不足以完成业务逻辑时,需要“自动续期”机制,常见实现——后台定时执行脚本。

-- KEYS[1] = lockKey, ARGV[1] = ownerId, ARGV[2] = additionalTTL
if redis.call("GET", KEYS[1]) == ARGV[1] then
    return redis.call("PEXPIRE", KEYS[1], ARGV[2])
else
    return 0
end
  • 在业务执行过程中,每隔 TTL/3 调用一次该脚本延长锁寿命,确保业务完成前锁不被过期。

5.4 可重入锁脚本示例

可重入锁允许同一个客户端多次加锁,每次加锁仅需增加内部计数,释放时再递减,直至 0 才真正释放。

-- KEYS[1]=lockKey, ARGV[1]=ownerId, ARGV[2]=ttl
local entry = redis.call("HGETALL", KEYS[1])
if next(entry) == nil then
    -- 首次加锁,创建 hash: { owner=ownerId, count=1 }
    redis.call("HMSET", KEYS[1], "owner", ARGV[1], "count", 1)
    redis.call("PEXPIRE", KEYS[1], ARGV[2])
    return 1
else
    local storedOwner = entry[2]
    if storedOwner == ARGV[1] then
        -- 重入:计数+1,并续期
        local cnt = tonumber(entry[4]) + 1
        redis.call("HSET", KEYS[1], "count", cnt)
        redis.call("PEXPIRE", KEYS[1], ARGV[2])
        return cnt
    else
        return 0
    end
end
  • 存储结构:使用 Hash 记录 ownercount
  • 释放脚本

    -- KEYS[1]=lockKey, ARGV[1]=ownerId
    local entry = redis.call("HGETALL", KEYS[1])
    if next(entry) == nil then
        return 0
    end
    if entry[2] == ARGV[1] then
        local cnt = tonumber(entry[4]) - 1
        if cnt > 0 then
            redis.call("HSET", KEYS[1], "count", cnt)
            return cnt
        else
            return redis.call("DEL", KEYS[1])
        end
    end
    return 0

5.5 性能与安全性评估

特性优点缺点
原子脚本执行无需往返多条命令,网络延迟低;执行期间不会被打断Lua 脚本会阻塞主线程
安全解锁避免 SETNX+DEL 的竞态脚本过长可能影响性能
可重入支持业务调用可安全重入、无锁重获失败状态存储更复杂,Hash 占用更多内存
自动续期保障长时业务场景的锁稳定性需要客户端定时心跳,复杂度提升

第六章:分布式事务模式在 Redis 上的实践

在微服务与分布式架构中,跨服务或跨数据库的一致性需求日益突出。传统的全局事务(如 2PC)在性能与可用性方面存在瓶颈。基于 Redis、消息队列以及应用协议的分布式事务模式成为主流选择。本章聚焦两大常见模式:Saga 与 TCC,并探讨 XSAGA 在 Redis 场景下的实现思路。


6.1 Saga 模式基础与 Redis 实现思路

Saga 模式将一个大事务拆分为一系列本地事务(step)与相应的补偿事务(compensation)。各服务按顺序执行本地事务,若中途某步失败,依次调用前面步骤的补偿事务,达到数据最终一致性。

步骤示意

  1. 执行 T1;若成功,推进至 T2,否则执行 C1。
  2. 执行 T2;若成功,推进至 T3,否则依次执行 C2、C1。
sequenceDiagram
    Client->>OrderService: CreateOrder()
    OrderService->>InventoryService: ReserveStock()
    alt ReserveStock OK
        OrderService->>PaymentService: ReservePayment()
        alt ReservePayment OK
            OrderService->>Client: Success
        else ReservePayment FAIL
            OrderService->>InventoryService: CompensateReleaseStock()
            OrderService->>Client: Failure
        end
    else ReserveStock FAIL
        OrderService->>Client: Failure
    end

Redis 实现要点

  • 状态存储:使用 Redis Hash 存储 Saga 状态:

    HSET saga:{sagaId} step T1 status PENDING
  • 可靠调度:结合消息队列(如 RabbitMQ)确保命令至少执行一次。
  • 补偿执行:若下游失败,由协调者发送补偿消息,消费者触发补偿逻辑。
  • 超时处理:利用 Redis TTL 与 keyspace notifications 触发超时回滚。

6.2 TCC(Try-Confirm-Cancel)模式与 Redis

TCC模式将事务分为三步:

  1. Try:预留资源或执行业务预处理。
  2. Confirm:确认事务,正式扣减或提交。
  3. Cancel:取消预留,回滚资源。

典型流程

sequenceDiagram
    Client->>ServiceA: tryA()
    ServiceA->>ServiceB: tryB()
    alt All try OK
        ServiceA->>ServiceB: confirmB()
        ServiceA->>Client: confirmA()
    else Any try FAIL
        ServiceA->>ServiceB: cancelB()
        ServiceA->>Client: cancelA()
    end

Redis 协调示例

  • 在 Try 阶段写入预留 key,并设置 TTL:

    SET reserved:order:{orderId} userId NX PX 60000
  • Confirm 成功后,DEL 该 key;Cancel 失败后,同样 DEL 并执行回滚逻辑。
  • 优点:明确的三段式接口,易于补偿管理。
  • 缺点:需实现 Try、Confirm、Cancel 三套接口,开发成本高。

6.3 XSAGA 模式示例:结合消息队列与 Redis

XSAGA 是 Saga 的扩展,使用状态机 + 可靠消息实现多事务编排,典型平台如 Apache ServiceComb Pack。

核心组件

  • 事务协调者:控制 Saga 执行流程,发布各 Step 消息。
  • 消息中间件:保证消息可靠投递与重试。
  • 参与者:消费消息,执行本地事务并更新状态。
  • Redis 存储:缓存 Saga 全局状态、Step 状态与补偿函数路由。

Redis 存储设计

HSET xsaga:{sagaId}:status globalState "INIT"
HSET xsaga:{sagaId}:steps step1 "PENDING"
HSET xsaga:{sagaId}:steps step2 "PENDING"
  • 消费者在成功后:

    HSET xsaga:{sagaId}:steps step1 "SUCCESS"
  • 失败时:

    HSET xsaga:{sagaId}:steps step1 "FAIL"
    RPUSH xsaga:{sagaId}:compensate compensateStep1
  • 协调者根据状态机读取 Redis 并发布下一个命令或补偿命令。

6.4 实战案例:电商下单跨服务事务

以“创建订单 → 扣减库存 → 扣减余额”场景展示 Saga 模式实战。

  1. 创建订单:OrderService 记录订单信息,并保存状态至 Redis:

    HSET saga:1001 step:createOrder "SUCCESS"
  2. 扣减库存:InventoryService 订阅 ReserveStock 消息,执行并更新 Redis:

    HSET saga:1001 step:reserveStock "SUCCESS"
  3. 扣减余额:PaymentService 订阅 ReservePayment 消息,执行并更新 Redis。
  4. 确认:协商者检查所有 step 状态为 SUCCESS 后,发布 Confirm 消息至各服务。
  5. 补偿:若任一 step FAIL,顺序执行补偿,如库存回滚、余额退回。


第七章:锁失效、超时与防死锁策略

在分布式锁场景中,锁过期、超时、死锁是常见挑战,本章深入分析并提供解决方案。

7.1 锁过期失效场景分析

  • 业务处理超时:持有者业务执行超过锁 TTL,锁自动过期,其他客户端可能抢占,导致并发操作错误。
  • 解决:自动续期或延长 TTL。

7.2 Watchdog 自动续期机制

基于 Redisson 的 Watchdog:若客户端在锁到期前仍在运行,自动为锁续期。

  • 默认超时时间:30 秒。
  • 调用 lock() 后,后台定时线程周期性发送 PEXPIRE 脚本延长 TTL。
RLock lock = redisson.getLock("resource");
lock.lock();  // 自动续期
try {
    // 业务代码
} finally {
    lock.unlock();
}

7.3 防止死锁的最佳实践

  • 合理设置 TTL:结合业务最坏执行时间估算。
  • 使用可重入锁:减少同线程重复加锁引发的死锁。
  • 请求超时机制:客户端设定最大等待时间,尝试失败后放弃或降级。

7.4 代码示例:可靠锁释放与续期

String luaRenew = 
  "if redis.call('get', KEYS[1]) == ARGV[1] then " +
  "  return redis.call('pexpire', KEYS[1], ARGV[2]) " +
  "else return 0 end";

// 定时续期线程
ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);
scheduler.scheduleAtFixedRate(() -> {
    jedis.eval(luaRenew, List.of(lockKey), List.of(requestId, "5000"));
}, 5, 5, TimeUnit.SECONDS);

第八章:高可用与锁的容灾设计

锁机制在 Redis Sentinel 或 Cluster 环境下,需考虑主从切换与分片容灾。

8.1 单实例锁的局限性

  • 节点宕机,锁丢失;
  • 尝试获取锁时可能连接失败。

8.2 Sentinel/Cluster 环境下的锁可靠性

  • Sentinel:自动主从切换,客户端需使用 Sentinel Pool;锁脚本需在所有节点上统一执行。
  • Cluster:锁 key 分布到某个 slot,需确保所有脚本与客户端指向正确节点。

8.3 主从切换与锁恢复

  • 切换窗口期,锁可能在新主上不存在;
  • 解决:使用 RedLock 多节点算法,或在多个实例上冗余存储锁。

8.4 容灾演练:故障切换场景下的锁安全

  • 模拟主节点挂掉,检查 RedLock 是否仍能获取大多数节点锁;
  • Sentinel 切换后,验证脚本与客户端自动连接。

第九章:锁与性能优化

9.1 锁的粒度与并发影响

  • 粗粒度锁:简单但并发性能差;
  • 细粒度锁:提高并发但管理复杂。

9.2 限流、降级与锁结合

  • 使用 Token Bucket 限流先前置;
  • 锁失败时可降级返回缓存或默认值。
if (!rateLimiter.tryAcquire()) return fallback();
if (lock.tryLock()) { ... }

9.3 大并发场景下的锁性能测试

  • 利用 JMeter 或 custom thread pool 对比 SETNX、RedLock、Redisson 性能;
  • 指标:成功率、平均延迟、吞吐量。

9.4 环境搭建与压力测试脚本

# JMeter 测试脚本示例,设置并发 1000
jmeter -n -t lock_test.jmx -Jthreads=1000

第十章:分布式事务监控与故障排查

10.1 监控锁获取、释放与超时指标

  • 收集 lock:acquire:successlock:acquire:faillock:release 计数;
  • Prometheus + Grafana 可视化。

10.2 事务执行链路跟踪

  • 使用 Sleuth 或 Zipkin,链路中记录 Redis 脚本调用。
  • 在链路报文中标注锁 key 与 requestId。

10.3 常见故障案例剖析

  • 锁未释放:可能因脚本错误或网络中断;
  • 续期失败:脚本未执行,TTL 到期。

10.4 报警策略与实践

  • lock:acquire:fail 超阈值报警;
  • 未释放锁告警(探测 key 长时间存在)。

第十一章:生产级锁框架与封装

11.1 Spring-Redis 分布式锁实践

@Component
public class RedisLockService {
    @Autowired private StringRedisTemplate redis;
    public boolean lock(String key, String id, long ttl) { ... }
    public boolean unlock(String key, String id) { ... }
}

11.2 Redisson、Lettuce 等客户端对比

框架特性
Redisson高级特性(可重入、延迟队列)
Lettuce轻量、高性能、响应式
Jedis简单、成熟

11.3 自研锁框架关键模块设计

  • LockManager 管理不同类型锁;
  • RetryPolicy 定制重试逻辑;
  • MetricsCollector 上报监控。

11.4 发布与灰度、回滚方案

  • 分组灰度:逐步打开分布式锁功能;
  • 回滚:配置中心开关、客户端降级到本地锁。

最后修改于:2025年07月03日 16:50

评论已关闭

推荐阅读

AIGC实战——Transformer模型
2024年12月01日
Socket TCP 和 UDP 编程基础(Python)
2024年11月30日
python , tcp , udp
如何使用 ChatGPT 进行学术润色?你需要这些指令
2024年12月01日
AI
最新 Python 调用 OpenAi 详细教程实现问答、图像合成、图像理解、语音合成、语音识别(详细教程)
2024年11月24日
ChatGPT 和 DALL·E 2 配合生成故事绘本
2024年12月01日
omegaconf,一个超强的 Python 库!
2024年11月24日
【视觉AIGC识别】误差特征、人脸伪造检测、其他类型假图检测
2024年12月01日
[超级详细]如何在深度学习训练模型过程中使用 GPU 加速
2024年11月29日
Python 物理引擎pymunk最完整教程
2024年11月27日
MediaPipe 人体姿态与手指关键点检测教程
2024年11月27日
深入了解 Taipy:Python 打造 Web 应用的全面教程
2024年11月26日
基于Transformer的时间序列预测模型
2024年11月25日
Python在金融大数据分析中的AI应用(股价分析、量化交易)实战
2024年11月25日
AIGC Gradio系列学习教程之Components
2024年12月01日
Python3 `asyncio` — 异步 I/O,事件循环和并发工具
2024年11月30日
llama-factory SFT系列教程:大模型在自定义数据集 LoRA 训练与部署
2024年12月01日
Python 多线程和多进程用法
2024年11月24日
Python socket详解,全网最全教程
2024年11月27日
python之plot()和subplot()画图
2024年11月26日
理解 DALL·E 2、Stable Diffusion 和 Midjourney 工作原理
2024年12月01日