首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >工作 5 年没碰过分布式锁?这不是你的问题

工作 5 年没碰过分布式锁?这不是你的问题

作者头像
用户11877422
发布2025-11-03 18:24:52
发布2025-11-03 18:24:52
1180
举报

前言

在技术交流群里看到这样一条消息:“工作5年了,简历上写着熟悉分布式系统,结果一次分布式锁都没用到过。是我太菜,还是公司系统太稳了?“这句话像一块石头投入水中,立刻激起了无数共鸣。有人说"我们连Redis集群都没有,就一台单机Redis”,有人调侃"别说分布式锁,我们连’分布’俩字都少见”。其实这不是个例,而是大多数开发者的真实工作状态。

一、分布式锁到底是什么?

分布式锁本质上是分布式系统中的"交通规则"。当多个服务实例同时争抢同一个资源时,需要通过分布式锁来保证操作的原子性和一致性。就像十字路口的红绿灯,确保车辆有序通行,避免碰撞。

生活中类似的场景随处可见:演唱会门票限量发售时, thousands of用户同时抢购,需要防止超卖;银行转账时,两个账户间的资金划转必须原子化,不能出现一方扣款成功而另一方未到账的情况;甚至连公司里的打印机,也需要通过排队机制避免多人同时操作导致的混乱。

在技术领域,分布式锁主要解决三类问题:

  • 资源竞争:如秒杀活动中的库存争夺
  • 操作幂等:如支付退款避免重复执行
  • 任务调度:如分布式定时任务防止重复执行

但现实是,很多公司的业务规模根本达不到需要分布式锁的程度。当用户量不足百万、日均订单不过万时,单机系统配合数据库锁就能稳定运行,完全不需要引入分布式锁这种"重型武器"。

二、那些必须用分布式锁的场景

只有当业务规模达到一定量级,分布式锁才会从"选择题"变成"必答题"。以下两个真实场景,或许能帮你理解分布式锁的实际价值。

超卖事故背后的技术盲区

某电商平台在周年庆期间推出"限量100件"的秒杀活动,结果活动结束后后台显示成交130单。仓库爆仓、客服被投诉淹没、用户因无法发货发起集体维权,整个团队通宵处理客诉。

事故根源在于架构设计的疏漏:十台应用服务器同时处理下单请求,每台服务器都独立检查库存。当剩余库存为5时,可能同时有6台服务器检测到"库存充足",并分别执行扣减操作,最终导致超卖。

这时候分布式锁就成了救命稻草:在扣减库存前,所有服务器必须先争抢锁资源,只有抢到锁的服务器才能执行库存检查和扣减操作,从而保证同一时间只有一个操作在修改库存。

重复退款引发的财务危机

一位用户在退款页面连续点击两次按钮,由于服务部署在三个节点上,两个节点同时接收到请求。系统没有防重机制,导致同一笔订单被退款两次。虽然最终通过财务手段追回资金,但这个过程中产生的银行手续费、人工核查成本,以及用户对平台的信任危机,都让团队付出了沉重代价。

这类问题的技术解决方案,正是通过分布式锁将退款操作串行化:以订单号作为锁标识,确保同一订单的退款请求只能被一个服务实例处理,从根本上杜绝重复执行的可能。

三、Redis分布式锁的实现之道

Redis凭借其高性能和原子操作特性,成为实现分布式锁的首选工具。基于SpringBoot的简单实现如下:

代码语言:javascript
复制
@Component
public class RedisDistributedLock {
    
    @Autowired
    private StringRedisTemplate redisTemplate;
    
    /**
     * 尝试获取分布式锁
     * @param lockKey 锁的key
     * @param requestId 请求标识(用于释放锁时验证)
     * @param expireTime 锁的过期时间(秒)
     * @return 是否获取成功
     */
    public boolean tryLock(String lockKey, String requestId, long expireTime) {
        return redisTemplate.opsForValue()
                .setIfAbsent(lockKey, requestId, expireTime, TimeUnit.SECONDS);
    }
    
    /**
     * 释放分布式锁
     * @param lockKey 锁的key
     * @param requestId 请求标识
     */
    public void releaseLock(String lockKey, String requestId) {
        String currentValue = redisTemplate.opsForValue().get(lockKey);
        if (requestId.equals(currentValue)) {
            redisTemplate.delete(lockKey);
        }
    }
}

在业务代码中使用时,只需围绕核心逻辑添加锁控制:

代码语言:javascript
复制
@Service
public class OrderService {
    
    @Autowired
    private RedisDistributedLock distributedLock;
    
    public void createOrder(String productId, int quantity) {
        String lockKey = "lock:order:" + productId;
        String requestId = UUID.randomUUID().toString();
        
        try {
            // 尝试获取锁,锁过期时间为10秒
            boolean locked = distributedLock.tryLock(lockKey, requestId, 10);
            if (!locked) {
                throw new RuntimeException("系统繁忙,请重试");
            }
            
            // 执行核心业务逻辑
            checkStock(productId); 
            reduceStock(productId, quantity);
            createOrderRecord(productId, quantity);
            
        } finally {
            // 释放锁
            distributedLock.releaseLock(lockKey, requestId);
        }
    }
}

但这个基础版本存在三个致命问题:锁过期时间设置不合理会导致业务未完成就释放锁;服务宕机可能导致锁无法释放;并发场景下可能误释放他人持有锁。这些问题在生产环境中可能引发更严重的故障。

四、生产级方案:Redisson的优雅实现

工业级应用中,推荐使用成熟的分布式锁框架Redisson,它已内置解决了上述所有问题。

集成步骤只需两步

  1. 添加Maven依赖:
代码语言:javascript
复制
<dependency>
    <groupId>org.redisson</groupId>
    <artifactId>redisson-spring-boot-starter</artifactId>
    <version>3.24.1</version>
</dependency>
  1. 业务代码中使用:
代码语言:javascript
复制
@Service
public class PaymentService {
    
    @Autowired
    private RedissonClient redissonClient;
    
    public void processRefund(String orderNo) {
        String lockKey = "lock:refund:" + orderNo;
        RLock lock = redissonClient.getLock(lockKey);
        
        try {
            // 尝试加锁,最多等待100秒,上锁10秒后自动解锁
            boolean isLocked = lock.tryLock(100, 10, TimeUnit.SECONDS);
            if (isLocked) {
                if (hasRefunded(orderNo)) {
                    throw new RuntimeException("该订单已退款,请勿重复操作");
                }
                doRefund(orderNo);
                recordRefund(orderNo);
            }
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
            throw new RuntimeException("获取锁失败", e);
        } finally {
            if (lock.isHeldByCurrentThread()) {
                lock.unlock();
            }
        }
    }
}

Redisson的优势在于:

  • 自动续期:通过看门狗机制,在业务未完成时自动延长锁有效期
  • 公平锁支持:避免线程饥饿,按请求顺序获取锁
  • 异常处理:完善的锁释放机制,防止死锁
  • 集群支持:原生适配Redis集群,避免单点故障

五、没用过分布式锁不代表技术差

如果你工作多年从未接触过分布式锁,不必焦虑。这通常意味着两种情况:

业务规模尚未达到分布式需求

很多公司的用户量和交易量,用单机系统加数据库锁就能稳定支撑。比如地方性企业的内部管理系统,日均访问量不足万次,根本不需要分布式架构。这种情况下,不用分布式锁反而是合理的技术选择——简单的架构意味着更低的维护成本和更高的稳定性。

隐形的技术保障在默默工作

即使在分布式环境中,你也可能在不知不觉中使用了替代方案:

  • 数据库的悲观锁(SELECT … FOR UPDATE)
  • 基于版本号的乐观锁机制
  • Redis的原子操作(INCR/DECR)
  • 消息队列的串行化处理

这些技术方案在特定场景下同样能解决并发问题,只是实现方式与分布式锁不同而已。

结语:技术储备比炫技更重要

分布式锁是解决特定问题的工具,而非衡量技术水平的标准。真正的技术能力体现在:理解业务本质,选择合适的技术方案,在稳定性、性能和开发效率之间找到平衡。

当然,了解分布式锁的原理和实现仍然很有必要——它不仅是面试高频考点,更是应对业务增长的技术储备。当你的系统用户量从十万级跃升到百万级,当订单峰值从每秒几十笔增长到几千笔时,这些知识将帮助你平稳度过架构升级的关键期。

技术成长就像治水,平时的储备如同筑堤,看似无用,实则在危机来临时才能显现价值。与其纠结是否用过分布式锁,不如理解它背后的分布式一致性原理——这才是真正能迁移到各种场景的核心能力。

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2025-09-11,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 前言
    • 一、分布式锁到底是什么?
    • 二、那些必须用分布式锁的场景
    • 三、Redis分布式锁的实现之道
    • 四、生产级方案:Redisson的优雅实现
    • 五、没用过分布式锁不代表技术差
    • 结语:技术储备比炫技更重要
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档