一、一次重复扣款让我丢了工作2017年我负责的优惠券系统出了一个Bug用户领券时没有加锁导致同一个用户同时领取了多张券。虽然代码里加了if判断每个用户只能领一张但并发情况下两个请求同时通过了判断都去扣款了。后来查出来被薅了10万张券公司损失50万。我被开除了。从那以后我对分布式锁有了深刻的理解并发问题不是小问题用不好就是钱和工作的代价。二、分布式锁概述2.1 为什么需要分布式锁单机锁 vs 分布式锁 单机环境 - Java synchronized - ReentrantLock - 只能保证单个JVM内有效 分布式环境 - 多个应用实例 - 多个数据库实例 - 多个缓存实例 - 需要跨JVM的锁机制 场景 1. 库存扣减 2. 优惠券领取 3. 幂等控制 4. 任务调度 5. Leader选举2.2 分布式锁特性分布式锁必须满足 1. 互斥性 - 同一时刻只能有一个客户端持有锁 2. 安全性 - 锁只能被持有它的客户端释放 3. 死锁避免 - 即使客户端崩溃锁也要能自动释放 4. 容错性 - 集群部分节点故障时锁服务仍可用 5. 性能 - 获取/释放锁的延迟要尽量小三、Redis分布式锁3.1 基础实现/** * Redis分布式锁 */ServiceSlf4jpublicclassRedisLockService{AutowiredprivateStringRedisTemplateredisTemplate;privatestaticfinalStringLOCK_PREFIXlock:;privatestaticfinalDurationDEFAULT_EXPIREDuration.ofSeconds(30);/** * 获取锁基础版 */publicbooleanlock(Stringkey,Stringvalue){StringlockKeyLOCK_PREFIXkey;// SET key value NX EX 30BooleanresultredisTemplate.opsForValue().setIfAbsent(lockKey,value,DEFAULT_EXPIRE);returnBoolean.TRUE.equals(result);}/** * 释放锁 */publicvoidunlock(Stringkey,Stringvalue){StringlockKeyLOCK_PREFIXkey;// Lua脚本只有value相等才删除Stringscriptif redis.call(get, KEYS[1]) ARGV[1] then return redis.call(del, KEYS[1]) else return 0 end;redisTemplate.execute(newDefaultRedisScript(script,Long.class),Collections.singletonList(lockKey),value);}/** * 获取锁带重试 */publicbooleanlockWithRetry(Stringkey,Stringvalue,intmaxRetries,longwaitMs){for(inti0;imaxRetries;i){if(lock(key,value)){returntrue;}try{Thread.sleep(waitMs);}catch(InterruptedExceptione){Thread.currentThread().interrupt();returnfalse;}}returnfalse;}}3.2 Redisson实现/** * Redisson分布式锁 */ServiceSlf4jpublicclassRedissonLockService{AutowiredprivateRedissonClientredissonClient;/** * 获取锁 */publicTTexecuteWithLock(Stringkey,intwaitTime,intleaseTime,CallableTcallable){RLocklockredissonClient.getLock(key);try{// 尝试获取锁booleanacquiredlock.tryLock(waitTime,leaseTime,TimeUnit.SECONDS);if(!acquired){thrownewLockException(获取锁失败: key);}// 执行业务逻辑returncallable.call();}catch(InterruptedExceptione){Thread.currentThread().interrupt();thrownewLockException(获取锁被中断,e);}catch(Exceptione){thrownewLockException(业务执行失败,e);}finally{// 释放锁if(lock.isHeldByCurrentThread()){lock.unlock();}}}/** * 库存扣减使用分布式锁 */publicbooleandeductStock(LongproductId,Integerquantity){StringlockKeystock:lock:productId;returnexecuteWithLock(lockKey,10,30,()-{// 查询当前库存IntegerstockgetStock(productId);if(stockquantity){thrownewBusinessException(库存不足);}// 扣减库存intnewStockstock-quantity;updateStock(productId,newStock);log.info(库存扣减成功: productId{}, quantity{}, newStock{},productId,quantity,newStock);returntrue;});}}3.3 Redlock算法/** * Redlock实现多Redis实例 */ServiceSlf4jpublicclassRedlockService{privateListRedisClientredisClients;/** * Redlock获取锁 */publicbooleanlockRedlock(Stringresource,Stringvalue,intttl,intretryTimes){intnredisClients.size();intsuccessCount0;ListLongacquisitionTimesnewArrayList();// 向N个Redis实例获取锁for(RedisClientclient:redisClients){longstartTimeSystem.currentTimeMillis();try{if(client.tryLock(resource,value,ttl)){successCount;acquisitionTimes.add(System.currentTimeMillis()-startTime);}}catch(Exceptione){log.warn(获取锁失败: {},client,e);}}// 计算锁的有效时间longminAcquisitionTimeacquisitionTimes.stream().mapToLong(Long::longValue).min().orElse(0L);// 超过半数成功if(successCountn/2){// 锁的有效时间 TTL - 平均获取时间longvalidTimettl-(minAcquisitionTime/2);log.info(Redlock获取成功: resource{}, validTime{}ms,resource,validTime);returntrue;}// 释放所有获取到的锁for(RedisClientclient:redisClients){try{client.unlock(resource,value);}catch(Exceptione){log.warn(释放锁失败: {},client,e);}}log.warn(Redlock获取失败: resource{}, success{}/{},resource,successCount,n);returnfalse;}}四、ZooKeeper分布式锁4.1 原理ZooKeeper分布式锁原理 1. 临时顺序节点 - 每个获取锁的客户端创建一个临时顺序节点 - 节点路径/lock/guid-XXXXX 2. 判断最小 - 检查自己的节点是否是序号最小的 - 如果是获取锁 3. 监听前一个节点 - 如果不是最小监听前一个节点的删除事件 - 当前一个节点删除时重新判断 4. 释放锁 - 删除自己的临时节点 - 下一个节点变为最小获得锁4.2 实现/** * ZK分布式锁 */ServiceSlf4jpublicclassZkLockService{AutowiredprivateCuratorFrameworkcuratorFramework;privatestaticfinalStringLOCK_PATH/locks;/** * 获取锁 */publicStringlock(StringlockName,intwaitTime,intleaseTime)throwsException{// 创建锁根节点createRootIfNotExists();// 创建临时顺序节点StringlockPathcuratorFramework.create().creatingParentsIfNeeded().withMode(CreateMode.EPHEMERAL_SEQUENTIAL).forPath(LOCK_PATH/lockName-);log.info(创建锁节点: {},lockPath);// 尝试获取锁returntryAcquireLock(lockPath,waitTime,leaseTime);}/** * 尝试获取锁 */privateStringtryAcquireLock(StringlockPath,intwaitTime,intleaseTime)throwsException{longstartTimeSystem.currentTimeMillis();while(true){// 获取所有子节点ListStringchildrencuratorFramework.getChildren().forPath(LOCK_PATH);// 排序Collections.sort(children);// 获取当前节点序号StringcurrentNodelockPath.substring(lockPath.lastIndexOf(/)1);// 判断是否是第一个if(children.get(0).equals(currentNode)){log.info(获取锁成功: {},lockPath);returnlockPath;}// 监听前一个节点StringprevNodechildren.get(children.indexOf(currentNode)-1);StringprevPathLOCK_PATH/prevNode;// 设置监听CountDownLatchlatchnewCountDownLatch(1);CuratorWatcherwatcherevent-{if(EventType.NodeDeleted.equals(event.getType())){latch.countDown();}};curatorFramework.getData().usingWatcher(watcher).forPath(prevPath);// 等待被唤醒或超时if(latch.await(waitTime,TimeUnit.MILLISECONDS)){// 被唤醒重新尝试continue;}else{// 超时获取锁失败thrownewLockException(获取锁超时: lockPath);}}}/** * 释放锁 */publicvoidunlock(StringlockPath){try{curatorFramework.delete().forPath(lockPath);log.info(释放锁成功: {},lockPath);}catch(Exceptione){log.error(释放锁失败: {},lockPath,e);}}}五、Redis vs ZooKeeper对比5.1 对比表┌─────────────────────────────────────────────────────────────────┐ │ Redis vs ZooKeeper 对比 │ │ │ │ 指标 │ Redis │ ZooKeeper │ │ ───────────────────────────────────────────────────────────── │ │ 性能 │ 高单线程 │ 中ZAB协议 │ │ 可靠性 │ 中主从 │ 高ZAB 多数派 │ │ 复杂度 │ 低 │ 中 │ │ 支持锁类型 │ 互斥锁、读写锁 │ 互斥锁需扩展 │ │ 故障恢复 │ 依赖TTL过期 │ 临时节点自动删除 │ │ 脑裂问题 │ 存在Redlock解决 │ 不存在 │ │ 适用场景 │ 性能要求高 │ 可靠性要求高 │ │ 运维成本 │ 低 │ 中 │ │ │ └──────────────────────────────────────────────────────────────────┘5.2 选型建议/** * 分布式锁选型决策 */publicclassLockSelection{/** * 根据场景选择合适的锁 */publicstaticStringselectLockType(LockScenarioscenario){// 场景1库存扣减高频、对性能要求高if(scenarioLockScenario.STOCK_DEDUCT){// 推荐RedisreturnRedis;}// 场景2Leader选举可靠性要求高if(scenarioLockScenario.LEADER_ELECTION){// 推荐ZooKeeperreturnZooKeeper;}// 场景3分布式事务需要协调者if(scenarioLockScenario.TRANSACTION){// 推荐ZooKeeperreturnZooKeeper;}// 场景4防重复提交性能要求高if(scenarioLockScenario.IDEMPOTENT){// 推荐RedisreturnRedis;}// 默认Redis通用returnRedis;}}六、实战案例6.1 优惠券领取/** * 优惠券服务 */ServiceSlf4jpublicclassCouponService{AutowiredprivateRedisLockServiceredisLockService;/** * 领取优惠券使用分布式锁 */publicvoidreceiveCoupon(LonguserId,LongcouponId){StringlockKeycoupon:lock:couponId:userId;StringlockValueUUID.randomUUID().toString();try{// 获取分布式锁booleanacquiredredisLockService.lockWithRetry(lockKey,lockValue,3,100);if(!acquired){thrownewBusinessException(系统繁忙请稍后重试);}// 检查是否已领取if(hasReceived(userId,couponId)){thrownewBusinessException(您已领取过该优惠券);}// 扣减库存booleandeductedcouponStockService.deductStock(couponId,1);if(!deducted){thrownewBusinessException(优惠券已领完);}// 记录领取saveReceiveRecord(userId,couponId);log.info(优惠券领取成功: userId{}, couponId{},userId,couponId);}finally{// 释放锁redisLockService.unlock(lockKey,lockValue);}}}6.2 订单创建防重/** * 订单服务 */ServiceSlf4jpublicclassOrderService{AutowiredprivateRedissonLockServiceredissonLockService;/** * 创建订单分布式锁防重 */publicOrdercreateOrder(CreateOrderRequestrequest){StringlockKeyorder:create:request.getUserId();returnredissonLockService.executeWithLock(lockKey,10,30,()-{// 检查是否有未支付的订单if(hasUnpaidOrder(request.getUserId())){thrownewBusinessException(您有待支付订单请先处理);}// 检查商品库存checkInventory(request.getItems());// 创建订单OrderorderdoCreateOrder(request);// 锁定库存lockInventory(order.getItems());returnorder;});}}七、踩坑实录坑1锁过期了但业务没完成业务执行时间太长锁自动过期了被其他线程获取了锁。解决使用watchdog自动续期或预估业务执行时间设置合理的TTL。坑2非原子性释放锁判断和删除不是原子操作导致释放了别人的锁。解决使用Lua脚本保证原子性。坑3Redis主从切换丢锁主库宕机从库升级为主库锁丢失了。解决使用Redlock或使用ZooKeeper。坑4大量客户端竞争锁竞争激烈大部分客户端都在等待。解决使用分段锁或改用无锁设计。坑5运维复杂需要维护Redis集群或ZooKeeper集群增加了运维成本。解决评估是否真的需要分布式锁或使用云服务。八、总结分布式锁是分布式系统的基石Redis锁性能高但可靠性稍差ZooKeeper锁可靠性高但性能稍差Redlock多Redis实例提高可靠性最佳实践预估业务执行时间设置合理的锁过期时间释放锁时使用Lua脚本保证原子性高并发场景考虑分段锁做好监控和告警血的教训分布式锁用对了是神器用错了是灾难。用之前想清楚真的需要锁吗有没有更好的无锁方案思考题你的系统用了哪种分布式锁有没有遇到过问题个人观点仅供参考
【架构实战】分布式锁实现:Redis还是ZooKeeper?
发布时间:2026/6/2 10:39:15
一、一次重复扣款让我丢了工作2017年我负责的优惠券系统出了一个Bug用户领券时没有加锁导致同一个用户同时领取了多张券。虽然代码里加了if判断每个用户只能领一张但并发情况下两个请求同时通过了判断都去扣款了。后来查出来被薅了10万张券公司损失50万。我被开除了。从那以后我对分布式锁有了深刻的理解并发问题不是小问题用不好就是钱和工作的代价。二、分布式锁概述2.1 为什么需要分布式锁单机锁 vs 分布式锁 单机环境 - Java synchronized - ReentrantLock - 只能保证单个JVM内有效 分布式环境 - 多个应用实例 - 多个数据库实例 - 多个缓存实例 - 需要跨JVM的锁机制 场景 1. 库存扣减 2. 优惠券领取 3. 幂等控制 4. 任务调度 5. Leader选举2.2 分布式锁特性分布式锁必须满足 1. 互斥性 - 同一时刻只能有一个客户端持有锁 2. 安全性 - 锁只能被持有它的客户端释放 3. 死锁避免 - 即使客户端崩溃锁也要能自动释放 4. 容错性 - 集群部分节点故障时锁服务仍可用 5. 性能 - 获取/释放锁的延迟要尽量小三、Redis分布式锁3.1 基础实现/** * Redis分布式锁 */ServiceSlf4jpublicclassRedisLockService{AutowiredprivateStringRedisTemplateredisTemplate;privatestaticfinalStringLOCK_PREFIXlock:;privatestaticfinalDurationDEFAULT_EXPIREDuration.ofSeconds(30);/** * 获取锁基础版 */publicbooleanlock(Stringkey,Stringvalue){StringlockKeyLOCK_PREFIXkey;// SET key value NX EX 30BooleanresultredisTemplate.opsForValue().setIfAbsent(lockKey,value,DEFAULT_EXPIRE);returnBoolean.TRUE.equals(result);}/** * 释放锁 */publicvoidunlock(Stringkey,Stringvalue){StringlockKeyLOCK_PREFIXkey;// Lua脚本只有value相等才删除Stringscriptif redis.call(get, KEYS[1]) ARGV[1] then return redis.call(del, KEYS[1]) else return 0 end;redisTemplate.execute(newDefaultRedisScript(script,Long.class),Collections.singletonList(lockKey),value);}/** * 获取锁带重试 */publicbooleanlockWithRetry(Stringkey,Stringvalue,intmaxRetries,longwaitMs){for(inti0;imaxRetries;i){if(lock(key,value)){returntrue;}try{Thread.sleep(waitMs);}catch(InterruptedExceptione){Thread.currentThread().interrupt();returnfalse;}}returnfalse;}}3.2 Redisson实现/** * Redisson分布式锁 */ServiceSlf4jpublicclassRedissonLockService{AutowiredprivateRedissonClientredissonClient;/** * 获取锁 */publicTTexecuteWithLock(Stringkey,intwaitTime,intleaseTime,CallableTcallable){RLocklockredissonClient.getLock(key);try{// 尝试获取锁booleanacquiredlock.tryLock(waitTime,leaseTime,TimeUnit.SECONDS);if(!acquired){thrownewLockException(获取锁失败: key);}// 执行业务逻辑returncallable.call();}catch(InterruptedExceptione){Thread.currentThread().interrupt();thrownewLockException(获取锁被中断,e);}catch(Exceptione){thrownewLockException(业务执行失败,e);}finally{// 释放锁if(lock.isHeldByCurrentThread()){lock.unlock();}}}/** * 库存扣减使用分布式锁 */publicbooleandeductStock(LongproductId,Integerquantity){StringlockKeystock:lock:productId;returnexecuteWithLock(lockKey,10,30,()-{// 查询当前库存IntegerstockgetStock(productId);if(stockquantity){thrownewBusinessException(库存不足);}// 扣减库存intnewStockstock-quantity;updateStock(productId,newStock);log.info(库存扣减成功: productId{}, quantity{}, newStock{},productId,quantity,newStock);returntrue;});}}3.3 Redlock算法/** * Redlock实现多Redis实例 */ServiceSlf4jpublicclassRedlockService{privateListRedisClientredisClients;/** * Redlock获取锁 */publicbooleanlockRedlock(Stringresource,Stringvalue,intttl,intretryTimes){intnredisClients.size();intsuccessCount0;ListLongacquisitionTimesnewArrayList();// 向N个Redis实例获取锁for(RedisClientclient:redisClients){longstartTimeSystem.currentTimeMillis();try{if(client.tryLock(resource,value,ttl)){successCount;acquisitionTimes.add(System.currentTimeMillis()-startTime);}}catch(Exceptione){log.warn(获取锁失败: {},client,e);}}// 计算锁的有效时间longminAcquisitionTimeacquisitionTimes.stream().mapToLong(Long::longValue).min().orElse(0L);// 超过半数成功if(successCountn/2){// 锁的有效时间 TTL - 平均获取时间longvalidTimettl-(minAcquisitionTime/2);log.info(Redlock获取成功: resource{}, validTime{}ms,resource,validTime);returntrue;}// 释放所有获取到的锁for(RedisClientclient:redisClients){try{client.unlock(resource,value);}catch(Exceptione){log.warn(释放锁失败: {},client,e);}}log.warn(Redlock获取失败: resource{}, success{}/{},resource,successCount,n);returnfalse;}}四、ZooKeeper分布式锁4.1 原理ZooKeeper分布式锁原理 1. 临时顺序节点 - 每个获取锁的客户端创建一个临时顺序节点 - 节点路径/lock/guid-XXXXX 2. 判断最小 - 检查自己的节点是否是序号最小的 - 如果是获取锁 3. 监听前一个节点 - 如果不是最小监听前一个节点的删除事件 - 当前一个节点删除时重新判断 4. 释放锁 - 删除自己的临时节点 - 下一个节点变为最小获得锁4.2 实现/** * ZK分布式锁 */ServiceSlf4jpublicclassZkLockService{AutowiredprivateCuratorFrameworkcuratorFramework;privatestaticfinalStringLOCK_PATH/locks;/** * 获取锁 */publicStringlock(StringlockName,intwaitTime,intleaseTime)throwsException{// 创建锁根节点createRootIfNotExists();// 创建临时顺序节点StringlockPathcuratorFramework.create().creatingParentsIfNeeded().withMode(CreateMode.EPHEMERAL_SEQUENTIAL).forPath(LOCK_PATH/lockName-);log.info(创建锁节点: {},lockPath);// 尝试获取锁returntryAcquireLock(lockPath,waitTime,leaseTime);}/** * 尝试获取锁 */privateStringtryAcquireLock(StringlockPath,intwaitTime,intleaseTime)throwsException{longstartTimeSystem.currentTimeMillis();while(true){// 获取所有子节点ListStringchildrencuratorFramework.getChildren().forPath(LOCK_PATH);// 排序Collections.sort(children);// 获取当前节点序号StringcurrentNodelockPath.substring(lockPath.lastIndexOf(/)1);// 判断是否是第一个if(children.get(0).equals(currentNode)){log.info(获取锁成功: {},lockPath);returnlockPath;}// 监听前一个节点StringprevNodechildren.get(children.indexOf(currentNode)-1);StringprevPathLOCK_PATH/prevNode;// 设置监听CountDownLatchlatchnewCountDownLatch(1);CuratorWatcherwatcherevent-{if(EventType.NodeDeleted.equals(event.getType())){latch.countDown();}};curatorFramework.getData().usingWatcher(watcher).forPath(prevPath);// 等待被唤醒或超时if(latch.await(waitTime,TimeUnit.MILLISECONDS)){// 被唤醒重新尝试continue;}else{// 超时获取锁失败thrownewLockException(获取锁超时: lockPath);}}}/** * 释放锁 */publicvoidunlock(StringlockPath){try{curatorFramework.delete().forPath(lockPath);log.info(释放锁成功: {},lockPath);}catch(Exceptione){log.error(释放锁失败: {},lockPath,e);}}}五、Redis vs ZooKeeper对比5.1 对比表┌─────────────────────────────────────────────────────────────────┐ │ Redis vs ZooKeeper 对比 │ │ │ │ 指标 │ Redis │ ZooKeeper │ │ ───────────────────────────────────────────────────────────── │ │ 性能 │ 高单线程 │ 中ZAB协议 │ │ 可靠性 │ 中主从 │ 高ZAB 多数派 │ │ 复杂度 │ 低 │ 中 │ │ 支持锁类型 │ 互斥锁、读写锁 │ 互斥锁需扩展 │ │ 故障恢复 │ 依赖TTL过期 │ 临时节点自动删除 │ │ 脑裂问题 │ 存在Redlock解决 │ 不存在 │ │ 适用场景 │ 性能要求高 │ 可靠性要求高 │ │ 运维成本 │ 低 │ 中 │ │ │ └──────────────────────────────────────────────────────────────────┘5.2 选型建议/** * 分布式锁选型决策 */publicclassLockSelection{/** * 根据场景选择合适的锁 */publicstaticStringselectLockType(LockScenarioscenario){// 场景1库存扣减高频、对性能要求高if(scenarioLockScenario.STOCK_DEDUCT){// 推荐RedisreturnRedis;}// 场景2Leader选举可靠性要求高if(scenarioLockScenario.LEADER_ELECTION){// 推荐ZooKeeperreturnZooKeeper;}// 场景3分布式事务需要协调者if(scenarioLockScenario.TRANSACTION){// 推荐ZooKeeperreturnZooKeeper;}// 场景4防重复提交性能要求高if(scenarioLockScenario.IDEMPOTENT){// 推荐RedisreturnRedis;}// 默认Redis通用returnRedis;}}六、实战案例6.1 优惠券领取/** * 优惠券服务 */ServiceSlf4jpublicclassCouponService{AutowiredprivateRedisLockServiceredisLockService;/** * 领取优惠券使用分布式锁 */publicvoidreceiveCoupon(LonguserId,LongcouponId){StringlockKeycoupon:lock:couponId:userId;StringlockValueUUID.randomUUID().toString();try{// 获取分布式锁booleanacquiredredisLockService.lockWithRetry(lockKey,lockValue,3,100);if(!acquired){thrownewBusinessException(系统繁忙请稍后重试);}// 检查是否已领取if(hasReceived(userId,couponId)){thrownewBusinessException(您已领取过该优惠券);}// 扣减库存booleandeductedcouponStockService.deductStock(couponId,1);if(!deducted){thrownewBusinessException(优惠券已领完);}// 记录领取saveReceiveRecord(userId,couponId);log.info(优惠券领取成功: userId{}, couponId{},userId,couponId);}finally{// 释放锁redisLockService.unlock(lockKey,lockValue);}}}6.2 订单创建防重/** * 订单服务 */ServiceSlf4jpublicclassOrderService{AutowiredprivateRedissonLockServiceredissonLockService;/** * 创建订单分布式锁防重 */publicOrdercreateOrder(CreateOrderRequestrequest){StringlockKeyorder:create:request.getUserId();returnredissonLockService.executeWithLock(lockKey,10,30,()-{// 检查是否有未支付的订单if(hasUnpaidOrder(request.getUserId())){thrownewBusinessException(您有待支付订单请先处理);}// 检查商品库存checkInventory(request.getItems());// 创建订单OrderorderdoCreateOrder(request);// 锁定库存lockInventory(order.getItems());returnorder;});}}七、踩坑实录坑1锁过期了但业务没完成业务执行时间太长锁自动过期了被其他线程获取了锁。解决使用watchdog自动续期或预估业务执行时间设置合理的TTL。坑2非原子性释放锁判断和删除不是原子操作导致释放了别人的锁。解决使用Lua脚本保证原子性。坑3Redis主从切换丢锁主库宕机从库升级为主库锁丢失了。解决使用Redlock或使用ZooKeeper。坑4大量客户端竞争锁竞争激烈大部分客户端都在等待。解决使用分段锁或改用无锁设计。坑5运维复杂需要维护Redis集群或ZooKeeper集群增加了运维成本。解决评估是否真的需要分布式锁或使用云服务。八、总结分布式锁是分布式系统的基石Redis锁性能高但可靠性稍差ZooKeeper锁可靠性高但性能稍差Redlock多Redis实例提高可靠性最佳实践预估业务执行时间设置合理的锁过期时间释放锁时使用Lua脚本保证原子性高并发场景考虑分段锁做好监控和告警血的教训分布式锁用对了是神器用错了是灾难。用之前想清楚真的需要锁吗有没有更好的无锁方案思考题你的系统用了哪种分布式锁有没有遇到过问题个人观点仅供参考