Redis实战:分布式锁、消息队列、缓存设计

一、分布式锁:从SETNX到Redlock 1.1 为什么需要分布式锁? 场景:秒杀系统的超卖问题 // ❌ 错误:单机锁无法解决分布式超卖 @Service public class SeckillService { @Autowired private ProductRepository productRepository; /** * 秒杀下单(单机锁) */ public synchronized Order seckill(Long productId, Long userId) { // 1. 检查库存 Integer stock = productRepository.getStock(productId); if (stock <= 0) { throw new SoldOutException("商品已售罄"); } // 2. 扣减库存 productRepository.decrementStock(productId); // 3. 创建订单 return orderService.createOrder(productId, userId); } } // 问题: // 假设有3台服务器(Server A、B、C) // T1: Server A:检查库存=1(通过) // T2: Server B:检查库存=1(通过) // T3: Server A:扣减库存=0,创建订单1 // T4: Server B:扣减库存=-1(超卖!),创建订单2 // synchronized只能锁住单机JVM内的线程 // 无法锁住分布式环境的多个进程 核心问题:分布式环境下,单机锁无效,需要分布式锁。 ...

2025-11-03 · maneng

Redis实战:分布式锁、消息队列、缓存设计

一、分布式锁:从SETNX到Redlock 1.1 为什么需要分布式锁? 场景:秒杀系统的超卖问题 // ❌ 错误:单机锁无法解决分布式超卖 @Service public class SeckillService { @Autowired private ProductRepository productRepository; /** * 秒杀下单(单机锁) */ public synchronized Order seckill(Long productId, Long userId) { // 1. 检查库存 Integer stock = productRepository.getStock(productId); if (stock <= 0) { throw new SoldOutException("商品已售罄"); } // 2. 扣减库存 productRepository.decrementStock(productId); // 3. 创建订单 return orderService.createOrder(productId, userId); } } // 问题: // 假设有3台服务器(Server A、B、C) // T1: Server A:检查库存=1(通过) // T2: Server B:检查库存=1(通过) // T3: Server A:扣减库存=0,创建订单1 // T4: Server B:扣减库存=-1(超卖!),创建订单2 // synchronized只能锁住单机JVM内的线程 // 无法锁住分布式环境的多个进程 核心问题:分布式环境下,单机锁无效,需要分布式锁。 ...

2025-11-03 · maneng

渠道共享库存中心:Redis分布式锁的生产实践

引子:一次价值百万的库存超卖事故 2024年双十一,凌晨00:05分,我的手机突然响起刺耳的告警声。 打开监控大盘,一行红色数字让我瞬间清醒:某爆款SKU超卖217件。这意味着我们已经卖出了比实际库存多217件商品,客户投诉、赔偿成本、品牌信任危机接踵而至。 这不是我第一次遇到超卖问题,但这次的损失格外惨重——该SKU是限量联名款,成本价就超过500元,217件的赔偿成本加上品牌损失,直接损失超过百万。 事后复盘,我们发现问题的根源:在分布式环境下,单机锁完全失效了。 10个服务实例同时处理来自Amazon、Shopify、天猫国际等多个渠道的订单,每个实例内部的synchronized锁只能保证单个JVM内的线程安全,但对于跨JVM的并发请求,库存扣减的原子性荡然无存。 这次事故后,我们用了整整一周时间,重构了整个库存中心的并发控制机制,将Redis分布式锁引入生产环境。三个月后,超卖率从5%降至0.1%,系统TPS从200提升到2000。 这篇文章,就是那次重构的完整技术总结。 问题本质:分布式环境下的并发控制 业务场景 我们的渠道共享库存中心需要服务10+电商平台,这些平台的订单会同时扣减同一个商品的库存: 时间 渠道 SKU-1001 操作 当前库存 00:01 Amazon -1 扣减 100 → 99 00:01 Shopify -2 扣减 99 → 97 00:01 天猫国际 -1 扣减 97 → 96 00:01 独立站 -3 扣减 96 → 93 在分布式部署下(假设5个服务实例),这些请求可能被不同的实例处理。如果没有正确的并发控制机制,就会出现经典的竞态条件(Race Condition)。 错误方案的代价 我们尝试过的失败方案: ❌ 方案1:数据库行锁 SELECT * FROM inventory WHERE sku = 'SKU-1001' FOR UPDATE; UPDATE inventory SET quantity = quantity - 1 WHERE sku = 'SKU-1001'; 问题: 性能极差:TPS<50,远低于业务需求 锁等待严重:高并发时大量请求超时 数据库压力大:成为系统瓶颈 ❌ 方案2:乐观锁(版本号) // 基于版本号的乐观锁 UPDATE inventory SET quantity = quantity - 1, version = version + 1 WHERE sku = 'SKU-1001' AND version = 10; 问题: ...

2025-10-15 · maneng

渠道共享库存中心:Redis分布式锁的生产实践

引子:一次价值百万的库存超卖事故 2024年双十一,凌晨00:05分,我的手机突然响起刺耳的告警声。 打开监控大盘,一行红色数字让我瞬间清醒:某爆款SKU超卖217件。这意味着我们已经卖出了比实际库存多217件商品,客户投诉、赔偿成本、品牌信任危机接踵而至。 这不是我第一次遇到超卖问题,但这次的损失格外惨重——该SKU是限量联名款,成本价就超过500元,217件的赔偿成本加上品牌损失,直接损失超过百万。 事后复盘,我们发现问题的根源:在分布式环境下,单机锁完全失效了。 10个服务实例同时处理来自Amazon、Shopify、天猫国际等多个渠道的订单,每个实例内部的synchronized锁只能保证单个JVM内的线程安全,但对于跨JVM的并发请求,库存扣减的原子性荡然无存。 这次事故后,我们用了整整一周时间,重构了整个库存中心的并发控制机制,将Redis分布式锁引入生产环境。三个月后,超卖率从5%降至0.1%,系统TPS从200提升到2000。 这篇文章,就是那次重构的完整技术总结。 问题本质:分布式环境下的并发控制 业务场景 我们的渠道共享库存中心需要服务10+电商平台,这些平台的订单会同时扣减同一个商品的库存: 时间 渠道 SKU-1001 操作 当前库存 00:01 Amazon -1 扣减 100 → 99 00:01 Shopify -2 扣减 99 → 97 00:01 天猫国际 -1 扣减 97 → 96 00:01 独立站 -3 扣减 96 → 93 在分布式部署下(假设5个服务实例),这些请求可能被不同的实例处理。如果没有正确的并发控制机制,就会出现经典的竞态条件(Race Condition)。 错误方案的代价 我们尝试过的失败方案: ❌ 方案1:数据库行锁 SELECT * FROM inventory WHERE sku = 'SKU-1001' FOR UPDATE; UPDATE inventory SET quantity = quantity - 1 WHERE sku = 'SKU-1001'; 问题: 性能极差:TPS<50,远低于业务需求 锁等待严重:高并发时大量请求超时 数据库压力大:成为系统瓶颈 ❌ 方案2:乐观锁(版本号) // 基于版本号的乐观锁 UPDATE inventory SET quantity = quantity - 1, version = version + 1 WHERE sku = 'SKU-1001' AND version = 10; 问题: ...

2025-10-15 · maneng

渠道共享库存中心:Redis分布式锁的生产实践

引子:一次价值百万的库存超卖事故 2024年双十一,凌晨00:05分,我的手机突然响起刺耳的告警声。 打开监控大盘,一行红色数字让我瞬间清醒:某爆款SKU超卖217件。这意味着我们已经卖出了比实际库存多217件商品,客户投诉、赔偿成本、品牌信任危机接踵而至。 这不是我第一次遇到超卖问题,但这次的损失格外惨重——该SKU是限量联名款,成本价就超过500元,217件的赔偿成本加上品牌损失,直接损失超过百万。 事后复盘,我们发现问题的根源:在分布式环境下,单机锁完全失效了。 10个服务实例同时处理来自Amazon、Shopify、天猫国际等多个渠道的订单,每个实例内部的synchronized锁只能保证单个JVM内的线程安全,但对于跨JVM的并发请求,库存扣减的原子性荡然无存。 这次事故后,我们用了整整一周时间,重构了整个库存中心的并发控制机制,将Redis分布式锁引入生产环境。三个月后,超卖率从5%降至0.1%,系统TPS从200提升到2000。 这篇文章,就是那次重构的完整技术总结。 问题本质:分布式环境下的并发控制 业务场景 我们的渠道共享库存中心需要服务10+电商平台,这些平台的订单会同时扣减同一个商品的库存: 时间 渠道 SKU-1001 操作 当前库存 00:01 Amazon -1 扣减 100 → 99 00:01 Shopify -2 扣减 99 → 97 00:01 天猫国际 -1 扣减 97 → 96 00:01 独立站 -3 扣减 96 → 93 在分布式部署下(假设5个服务实例),这些请求可能被不同的实例处理。如果没有正确的并发控制机制,就会出现经典的竞态条件(Race Condition)。 错误方案的代价 我们尝试过的失败方案: ❌ 方案1:数据库行锁 SELECT * FROM inventory WHERE sku = 'SKU-1001' FOR UPDATE; UPDATE inventory SET quantity = quantity - 1 WHERE sku = 'SKU-1001'; 问题: 性能极差:TPS<50,远低于业务需求 锁等待严重:高并发时大量请求超时 数据库压力大:成为系统瓶颈 ❌ 方案2:乐观锁(版本号) // 基于版本号的乐观锁 UPDATE inventory SET quantity = quantity - 1, version = version + 1 WHERE sku = 'SKU-1001' AND version = 10; 问题: ...

2025-10-15 · maneng

分布式锁实战:从SETNX到Redlock

为什么需要分布式锁? 单机锁的局限: // ❌ 单机环境有效 synchronized (this) { // 扣减库存 } // ❌ 分布式环境失效 // 服务器1和服务器2的锁互不影响 分布式锁特性: 互斥性:同一时刻只有一个客户端持有锁 安全性:只有持锁者能释放锁 可用性:高可用,避免死锁 性能:加锁解锁快速 方案演进 V1:基础版(SETNX + EXPIRE) public boolean lock(String key, String value, long expireSeconds) { Boolean success = redis.opsForValue().setIfAbsent(key, value); if (Boolean.TRUE.equals(success)) { redis.expire(key, expireSeconds, TimeUnit.SECONDS); return true; } return false; } // ❌ 问题:SETNX和EXPIRE不是原子操作 // 如果SETNX成功后宕机,锁永不过期 V2:原子操作版(SET NX EX) public boolean lock(String key, String value, long expireSeconds) { Boolean success = redis.opsForValue() .setIfAbsent(key, value, expireSeconds, TimeUnit.SECONDS); return Boolean.TRUE.equals(success); } public void unlock(String key) { redis.delete(key); } // ❌ 问题:可能释放别人的锁 // 线程A持锁超时,锁自动释放 // 线程B获得锁 // 线程A执行完,删除了线程B的锁 V3:安全释放版(Lua脚本) public class DistributedLock { @Autowired private RedisTemplate<String, String> redis; private static final String UNLOCK_SCRIPT = "if redis.call('GET', KEYS[1]) == ARGV[1] then " + " return redis.call('DEL', KEYS[1]) " + "else " + " return 0 " + "end"; // 加锁 public boolean lock(String key, String requestId, long expireSeconds) { Boolean success = redis.opsForValue() .setIfAbsent(key, requestId, expireSeconds, TimeUnit.SECONDS); return Boolean.TRUE.equals(success); } // 安全解锁 public boolean unlock(String key, String requestId) { Long result = redis.execute( RedisScript.of(UNLOCK_SCRIPT, Long.class), Collections.singletonList(key), requestId ); return result != null && result == 1; } } // ✅ 优势:原子性校验和删除 // ❌ 问题:业务执行时间超过锁过期时间 V4:自动续期版(看门狗) @Component public class RedisLockWithWatchdog { @Autowired private RedisTemplate<String, String> redis; private static final long DEFAULT_EXPIRE_SECONDS = 30; private static final long RENEW_INTERVAL_SECONDS = 10; private final Map<String, ScheduledFuture<?>> renewTasks = new ConcurrentHashMap<>(); private final ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1); public boolean lock(String key, String requestId) { Boolean success = redis.opsForValue() .setIfAbsent(key, requestId, DEFAULT_EXPIRE_SECONDS, TimeUnit.SECONDS); if (Boolean.TRUE.equals(success)) { // 启动续期任务 startRenewTask(key, requestId); return true; } return false; } private void startRenewTask(String key, String requestId) { ScheduledFuture<?> task = scheduler.scheduleAtFixedRate(() -> { try { String value = redis.opsForValue().get(key); if (requestId.equals(value)) { redis.expire(key, DEFAULT_EXPIRE_SECONDS, TimeUnit.SECONDS); log.debug("锁续期成功: {}", key); } else { // 锁已被他人持有,停止续期 stopRenewTask(key); } } catch (Exception e) { log.error("锁续期失败", e); } }, RENEW_INTERVAL_SECONDS, RENEW_INTERVAL_SECONDS, TimeUnit.SECONDS); renewTasks.put(key, task); } public boolean unlock(String key, String requestId) { stopRenewTask(key); String script = "if redis.call('GET', KEYS[1]) == ARGV[1] then " + " return redis.call('DEL', KEYS[1]) " + "else " + " return 0 " + "end"; Long result = redis.execute( RedisScript.of(script, Long.class), Collections.singletonList(key), requestId ); return result != null && result == 1; } private void stopRenewTask(String key) { ScheduledFuture<?> task = renewTasks.remove(key); if (task != null) { task.cancel(false); } } } 实战案例 案例1:库存扣减 @Service public class StockService { @Autowired private RedisLockWithWatchdog redisLock; @Autowired private RedisTemplate<String, Object> redis; public boolean deductStock(String productId, int quantity) { String lockKey = "lock:stock:" + productId; String requestId = UUID.randomUUID().toString(); try { // 尝试获取锁(超时3秒) if (!tryLock(lockKey, requestId, 3000)) { return false; // 获取锁失败 } // 检查库存 Integer stock = (Integer) redis.opsForValue().get("stock:" + productId); if (stock == null || stock < quantity) { return false; // 库存不足 } // 扣减库存 redis.opsForValue().decrement("stock:" + productId, quantity); return true; } finally { // 释放锁 redisLock.unlock(lockKey, requestId); } } private boolean tryLock(String key, String requestId, long timeoutMs) { long startTime = System.currentTimeMillis(); while (System.currentTimeMillis() - startTime < timeoutMs) { if (redisLock.lock(key, requestId)) { return true; } try { Thread.sleep(50); // 等待50ms后重试 } catch (InterruptedException e) { Thread.currentThread().interrupt(); return false; } } return false; } } 案例2:订单防重 @Service public class OrderService { @Autowired private RedisLockWithWatchdog redisLock; public String createOrder(OrderRequest request) { String lockKey = "lock:order:" + request.getUserId() + ":" + request.getOrderNo(); String requestId = UUID.randomUUID().toString(); try { if (!redisLock.lock(lockKey, requestId)) { throw new BusinessException("订单正在处理中,请勿重复提交"); } // 创建订单逻辑 Order order = new Order(); order.setOrderNo(request.getOrderNo()); // ... 保存订单 return order.getOrderNo(); } finally { redisLock.unlock(lockKey, requestId); } } } 案例3:定时任务防重 @Component public class ScheduledTaskWithLock { @Autowired private RedisLockWithWatchdog redisLock; @Scheduled(cron = "0 */5 * * * ?") // 每5分钟执行 public void syncData() { String lockKey = "lock:task:sync_data"; String requestId = UUID.randomUUID().toString(); if (!redisLock.lock(lockKey, requestId)) { log.info("其他节点正在执行同步任务,跳过"); return; } try { log.info("开始执行同步任务"); // 执行同步逻辑 doSync(); } finally { redisLock.unlock(lockKey, requestId); } } private void doSync() { // 同步逻辑 } } Redlock算法(多Redis实例) 问题:单Redis实例有单点故障风险 ...

2025-01-21 · maneng

如约数科科技工作室

浙ICP备2025203501号

👀 本站总访问量 ...| 👤 访客数 ...| 📅 今日访问 ...