新闻详情

新闻详情

首页 / 资讯中心 / 详情

零工抢单:一个单子 3000 人同时抢,Redis 分布式锁 + 乐观锁才稳

发布时间:2026/10/2 5:56:32来源:尧图网络
零工抢单:一个单子 3000 人同时抢,Redis 分布式锁 + 乐观锁才稳
导读xllg 日结零工一个打扫卫生的单子发出去 10 秒被抢完刚开始用 synchronized 锁单机测试没问题部署两台后直接超卖一个单子被两个人同时抢走。后来换成 Redis 分布式锁 数据库乐观锁双保险才稳住。这篇记录抢单高并发的完整改造过程。零工抢单一个单子 3000 人同时抢Redis 分布式锁 乐观锁才稳先说事故。零工平台上线第三天某老板发了 5 个单子每单 1 个名额结果后台数据显示5 个单子被抢走了 9 次同一个单子两个阿姨都显示抢单成功。客户打电话过来骂了一顿。第一版synchronized单机够用多机直接废第一版代码很简单接单方法用synchronized锁publicsynchronizedbooleangrabOrder(LongorderId,LonguserId){// 1. 查订单当前状态OrderorderorderMapper.selectById(orderId);if(order.getStatus()!OrderStatus.WAITING.getCode()){returnfalse;// 已经被抢了}// 2. 更新为已抢orderMapper.updateStatus(orderId,OrderStatus.GRABBED.getCode());// 3. 记录抢单关系grabMapper.insert(newOrderGrab(orderId,userId));returntrue;}单机压测 2000 并发全过了上生产两台服务器synchronized 锁的是各自 JVM 里的对象两台机器同时进入方法查到的都是 WAITING双双更新成功。超卖就是这么来的。第二版Redis 分布式锁锁跨 JVM先上 Redis 分布式锁setIfAbsent原子加锁AutowiredprivateStringRedisTemplateredisTemplate;privatestaticfinalStringLOCK_KEY_PREFIXgrab:lock:;publicbooleangrabOrder(LongorderId,LonguserId){// 用订单 ID 做锁 key抢单串行化StringlockKeyLOCK_KEY_PREFIXorderId;booleanlockedBoolean.TRUE.equals(redisTemplate.opsForValue().setIfAbsent(lockKey,String.valueOf(userId),10,TimeUnit.SECONDS));if(!locked){returnfalse;// 别人正在抢直接失败}try{returndoGrab(orderId,userId);}finally{// 释放锁校验是自己的锁才删防止误删别人的StringownerredisTemplate.opsForValue().get(lockKey);if(String.valueOf(userId).equals(owner)){redisTemplate.delete(lockKey);}}}分布式锁保证同一时间只有一个 JVM 能进入抢单逻辑超卖问题解决了。第三版数据库乐观锁兜底防重但光有分布式锁不够。抢单不止一条链路后台管理员改单、运营直接改库、MQ 异步补偿都可能绕过 Redis 锁。数据库层面必须兜底用乐观锁或条件更新-- 抢单核心status 条件是 CAS影响行数1 才算抢到UPDATEorder_SETstatus2,grab_user_id#{userId}, grab_time NOW()WHEREid#{orderId} AND status 1;Java 侧判断影响行数publicbooleandoGrab(LongorderId,LonguserId){// 条件更新status1待抢才更新成功天然防超卖introwsorderMapper.grabOrder(orderId,userId);if(rows0){returnfalse;// 被并发抢走了}// 抢单成功记录明细grabMapper.insert(newOrderGrab(orderId,userId));returntrue;}UPDATE ... WHERE status 1是数据库行的原子操作并发下只有一个事务能更新成功即使绕过 Redis 锁数据库也拦得住。踩坑一锁过期时间 10 秒任务跑了 15 秒现象高峰期偶发一个单子被抢两次排查发现是锁提前过期。抢单流程里有查积分、扣余额、发消息耗时超过了锁的 10 秒 TTL锁自动释放第二个请求又进来了。排查过程看日志两次抢单间隔 12 秒恰好大于锁 TTL。定位思路锁的过期时间必须大于业务最慢路径但设太长锁出问题又会卡死线程崩溃不释放。最终解决两种方案二选一——① 用 Redisson 的看门狗自动续期② 锁 TTL 按业务耗时 3 倍设置 finally 释放。我项目里用了 RedissonRLocklockredissonClient.getLock(grab:lock:orderId);booleanlockedlock.tryLock(0,30,TimeUnit.SECONDS);// 看门狗会自动续期踩坑二抢单成功但消息发不出去用户以为没抢到现象抢单接口返回成功用户却收到抢单失败通知。排查过程抢单事务提交后发 MQMQ 发送异常被 catch 吞掉前端没收到状态推送。日志里 MQ 报错connection refused恰好赶上 Redis 故障演练。定位思路抢单状态以数据库为准消息只是通知不能因为通知失败回滚抢单结果。最终解决抢单成功先落库消息发送失败走重试表不阻塞主流程TransactionalpublicbooleangrabOrder(LongorderId,LonguserId){introwsorderMapper.grabOrder(orderId,userId);if(rows0)returnfalse;try{mqTemplate.convertAndSend(grab.success,newGrabMsg(orderId,userId));}catch(Exceptione){// 消息发送失败不阻塞抢单进重试表凌晨补偿notifyRetryMapper.save(orderId,userId);log.error(抢单通知发送失败 orderId{},orderId,e);}returntrue;}可直接复用的要点抢单/秒杀这类高并发写场景分布式锁 数据库条件更新双保险别只靠一层。分布式锁 key 用业务 IDvalue 存持有者标识释放时校验是自己的锁。锁 TTL 必须大于业务最慢路径或用 Redisson 看门狗自动续期。数据库 CAS 更新UPDATE ... WHERE status 待抢影响行数1 才算成功。抢单结果以数据库为准通知类副作用失败不影响主流程。加单测压一把多线程并发抢同一单断言只有一人成功。项目源码https://gitee.com/gzqkl/xllg
网站建设高端定制企业官网
RELATED

相关资讯

更多精彩内容,欢迎继续阅读

较早相关资讯

最新相关资讯

OpenClaw数字助教部署指南:WSL2与本地模型实战 2026/10/2 7:34:20

OpenClaw数字助教部署指南:WSL2与本地模型实战

要说今年我做得最值的一件事,就是把 OpenClaw 部署成了自己的“数字助教”。先交代一下背景:我是一名中学老师,带两个班,每周二十多节课。听起来是不是觉得和“部署工具”“AI 助手”这些词完全不搭?但恰恰是老师们这种…

阅读更多 →
嵌入式Linux下与rkipc通信:RTSP拉流、控制指令与桥接实践 2026/10/2 7:34:20

嵌入式Linux下与rkipc通信:RTSP拉流、控制指令与桥接实践

做嵌入式Linux板卡开发的朋友,对“rkipc”这串字母应该都不陌生。它几乎成了Rockchip平台上摄像头/IPC方案的代名词:从RV1126、RV1109到RK3568、RK3588,很多官方SDK编译完、烧完固件、上电之后,系统里都会有一个叫rkipc的进程自动…

阅读更多 →
主动式桥梁防船撞预警系统设计与落地实践 2026/10/2 7:34:20

主动式桥梁防船撞预警系统设计与落地实践

1. 为什么“防船撞”不能只靠被动警示——从三起真实事故看系统设计的底层逻辑去年长江某支流航道上,一艘满载砂石的散货船在浓雾中偏离主航路,以12节航速径直撞向一座在建桥梁墩柱。撞击点距设计通航净空仅差0.8米,混凝土表层剥落、钢筋外露…

阅读更多 →
出淤泥而不染:把困境转化为成长养分的实操指南 2026/10/2 7:34:20

出淤泥而不染:把困境转化为成长养分的实操指南

1. 写下这几个字之前,我面对的究竟是什么1.1 我的“淤泥”具体长什么样过去很长一段时间,我的状态可以用一个词概括:淤住了。不是突然的崩溃,也不是什么惊天动地的挫折,就是那种温水煮青蛙式的困顿感——每天醒来刷手机…

阅读更多 →
AD9361 HDL工程生成:用ADI TCL脚本在Vivado中高效搭建FPGA设计 2026/10/2 7:34:13

AD9361 HDL工程生成:用ADI TCL脚本在Vivado中高效搭建FPGA设计

做AD9361相关的板子也有些年了,这次要在Vivado里用ADI官方TCL脚本从头生成AD9361的HDL工程,本来以为就是跑个脚本的事,结果版本、路径、IP核升级这些坑一个个冒出来。折腾完回头一看,整个流程其实非常有规律,只要把原理…

阅读更多 →
编译原理课设三大核心:NFA确定化、DFA最小化与First/Follow计算 2026/10/2 7:34:13

编译原理课设三大核心:NFA确定化、DFA最小化与First/Follow计算

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

阅读更多 →

今日资讯

本周资讯

本月资讯

看完文章仍有疑问?

联系尧图顾问,获取一对一建站咨询

立即免费咨询 📞 400-888-8888
📞 ✉