工作5年没碰过分布式锁,是我太菜还是公司太稳?网友:太真实了!

最近在群里看到一个哥们说:"工作5年了,简历上写了熟悉分布式系统,结果一次分布式锁都没用过。是我太菜?还是公司系统太稳了?",底下立马一堆消息。 "+1,我也是!" "别说用了,我们连分布俩字都少见。" "你们还好,我们公司连Redis集群都没有,就一台单机Redis。"

嘿嘿,我认为这应该是大部分开发的日常吧?下面我也来整理一下我理解的点吧!

1. 啥是分布式锁?

当多个服务抢同一个资源时,需要定个规矩:谁先来的谁先用。

比如:

  • 两个服务器同时给用户发红包,不能重发。
  • 秒杀活动,库存就10个,不能超卖。
  • 定时任务在多个节点上不能重复跑。

这些,才需要分布式锁。可现实是很多公司,压根没到那个量级。

2. 实际场景

举两个常见的例子,看大家有遇到过没:

场景一:超卖问题

某次大促,你们搞了一个"限量100件"的秒杀活动。结果活动结束,卖了130件,仓库傻了,客服也炸了。

原因就是:十台服务器同时卖货,每个人都检测到"还有库存",于是都成功下单。

这时候就要用分布式锁,在真正减库存之前,先抢锁,抢到了才能减。

场景二:重复退款

用户点退款,手抖连续点了两次,你的服务部署了三个节点,两个节点都接收到了请求,结果退了两次钱。

虽然最后能追回,但财务可能要提着刀来找你了。

3. Redis分布式锁实现

下面是一个基于SpringBoot的简单实现:

java 复制代码
@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);
        }
    }
}

// 使用示例
@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 {
            // 尝试获取锁,等待3秒,锁过期时间为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);
        }
    }
    
    private void checkStock(String productId) {
        // 检查库存逻辑
    }
    
    private void reduceStock(String productId, int quantity) {
        // 扣减库存逻辑
    }
    
    private void createOrderRecord(String productId, int quantity) {
        // 创建订单逻辑
    }
}

但这只是个基础版,真上生产环境还得考虑下面这几个点:

  • 锁过期时间太短:业务没执行完,锁没了,别人进来了
  • 锁过期时间太长:业务挂了,锁一直不释放,其他人傻等
  • 释放了别人的锁:自己阻塞久了,锁过期被别人抢了,然后又释放了别人的

所以实际项目中建议直接用现成的库,比如Redisson,别自己瞎造轮子。

4. Redisson分布式锁

Redisson已经帮我们解决了上面的所有问题,使用起来超级简单:

Maven加依赖

xml 复制代码
<dependency>
    <groupId>org.redisson</groupId>
    <artifactId>redisson-spring-boot-starter</artifactId>
    <version>3.24.1</version>
</dependency>

业务逻辑

java 复制代码
@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();
            }
        }
    }
    
    private boolean hasRefunded(String orderNo) {
        // 检查是否已经退款
        return false;
    }
    
    private void doRefund(String orderNo) {
        // 执行退款逻辑
    }
    
    private void recordRefund(String orderNo) {
        // 记录退款信息
    }
}

搞定,感兴趣的朋友可以试一下。

5. 小结

如果你工作几年都没碰过分布式锁,无非两种情况:

  1. 公司业务确实稳:用户量没那么大,根本不需要分布式部署;或者业务场景本来就不会冲突。
  2. 有人帮你扛了:很多框架和数据库已经内置了类似的机制,比如数据库的悲观锁(SELECT ... FOR UPDATE)、乐观锁(version字段)、Redis的原子操作(INCR/DECR)等等,你可能在不知不觉中已经避免了问题。

所以也别说自己菜,没遇到说明你们系统跑得足够顺,这是好事!

但话说回来,现在没用过不代表以后用不到。跳槽、业务发展都可能突然遇到。所以收藏一下这篇文章,真要用的时候翻出来,能救急。

分布式锁属于"平时用不着,面试老爱问"的知识点。没实际搞过不丢人,但要知道为啥要用、怎么上手。

你工作几年?用过分布式锁吗?欢迎评论区聊聊你的经历!

📌往期精彩

《写给小公司前端的 UI 规范:别让页面丑得自己都看不下去》

《只会写 Mapper 就敢说会 MyBatis?面试官:原理都没懂》

《别再手写判空了!SpringBoot 自带的 20 个高效工具类》

《别学23种了!Java项目中最常用的6个设计模式,附案例》

《Vue3+TS设计模式:5个真实场景让你代码更优雅》

相关推荐
尚久龙7 小时前
安卓学习 之 图片控件和图片按钮
android·java·学习·手机·android studio·安卓
摸鱼仙人~7 小时前
深入理解 MyBatis-Plus 的 `BaseMapper`
java·开发语言·mybatis
杨杨杨大侠7 小时前
第6章:高级特性与性能优化
java·github·eventbus
卿·静7 小时前
Node.js对接即梦AI实现“千军万马”视频
前端·javascript·人工智能·后端·node.js
Dcs7 小时前
代码评审还能更好!
java
刃神太酷啦8 小时前
C++ 异常处理机制:从基础到实践的全面解析----《Hello C++ Wrold!》(20)--(C/C++)
java·c语言·开发语言·c++·qt·算法·leetcode
蓝倾9768 小时前
小红书获取用户作品列表API接口操作指南
java·服务器·前端·python·电商开放平台·开放api接口
Seven978 小时前
剑指offer-28、数组中出现次数超过⼀半的数字
java
浮游本尊8 小时前
Java学习第19天 - 分布式缓存与Redis高级应用
java