分布式锁
分布式锁: 跨多个服务器(进程/线程)的“全局互斥令牌”。谁抢到令牌,谁就有权操作共享资源(如扣库存、修改订单)。
库存扣减流程包括: 查库存->库存扣减->订单处理
Q: 分布式环境中为了防止库存超卖,通常需保证库存检查与扣减的原子性,方案有redis lua原子操作、redission分布式锁,两者如何选择?
核心原则:
- 内存操作选Lua,涉及IO等待(数据库、RPC)选Redisson。
- 追求极致性能选Lua,追求业务容错选Redisson。
RPC
RPC(Remote Procedure Call,远程过程调用)指的是通过网络请求远程服务接口所产生的网络IO等待。
具体来说,当应用触发一次RPC调用,比如请求下游微服务、调用第三方API、或者通过Dubbo/gRPC等框架请求隔壁服务,当前线程就会进入阻塞状态。
此时,CPU并没有在计算,而是在等待网络数据包到达,这段时间就是“RPC引起的IO等待”。
在性能排查中,这个指标通常意味着:
- 网络链路存在延迟(物理距离、交换机拥堵)。
- 下游服务处理慢(对方没返回,导致你的连接一直处于读等待状态)。
- 序列化/反序列化开销大(虽然属于CPU范畴,但传输数据包过大会加剧网络传输等待)。
在实际落地中,极少用Redisson锁直接扣高并发库存(因为阻塞太伤性能),但也不会只用裸Lua(因为无法落库)。推荐采用Lua预扣 + 异步落库,也是主流方案。
- 使用Lua脚本扣减Redis中的“可售库存”,返回扣减成功/失败。
- 成功后,将扣减事件发送到MQ,消费者异步创建订单并扣减DB库存(最终一致性)。此时的Lua只负责快速拦截流量,不做强一致性事务。
Q: 避免库存重复扣减的方式有哪些?如何测试?
- 前端防抖
- 网关限流: 单用户限流,拦脚本或恶意请求
- 客户端: 唯一请求id标识,搭配联合索引防重复入库
- 分布式锁: lua预扣 + sku(规格组合)加锁 + 异步mq落库
- db兜底: update语句的where条件先查库存(高并发时不可靠)
如何测试
- 高并发: 模拟多个用户同时对sku的并发请求,验证库存不会被扣成负数;检查请求对应的响应结果(验证乐观锁和数据库行锁在竞争时的可靠性)。
- 幂等: 基于同一订单号的重复发请求,检查仅第一次请求被正常处理;根据traceid查其他请求被拦截或返回缓存结果。
- 极端场景测回滚,如扣减库存成功但后续生成订单失败,或者分布式锁超时(Nacos缩短锁过期时间)导致业务未完成。