You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

电影票预订系统高效方案问询:并发座位冲突处理

处理电影票预订系统并发座位冲突的可扩展方案

你说得太对了——单纯用Java的synchronized这类进程内同步机制,在高并发场景下确实会很快碰到扩展性天花板。一旦请求量上来,单节点的锁会把所有并发请求堵成串行,不仅响应变慢,还没法通过横向扩容来缓解压力。下面是几个更适合高并发场景的可扩展方案,结合实际业务场景给你拆解:

1. 数据库乐观锁(推荐中小规模场景)

乐观锁的核心思路是不提前加锁,而是在提交更新时验证数据是否被修改过,非常适合冲突概率不是极端高的电影票场景(毕竟同一秒抢同一个座位的用户还是少数)。

实现方式很简单:

  • 在座位表(比如seat)里加一个版本号字段version,或者用座位的status状态+更新时间戳。
  • 用户发起预订时,先查询座位的当前状态和版本号:
    SELECT id, status, version FROM seat WHERE show_id = ? AND seat_no = ?;
    
  • 确认座位可用后,更新时带上版本号做校验:
    UPDATE seat SET status = 'booked', user_id = ? WHERE id = ? AND version = ? AND status = 'available';
    
  • 最后判断更新影响的行数,如果是0,说明座位已经被别人抢了,返回用户“座位已被预订”。

优点:完全无锁,支持横向扩容,数据库压力小;缺点:冲突频繁时会有较多失败请求,需要前端配合做友好的重试提示。

2. 数据库悲观锁(适合冲突极高的热门场次)

如果是热门电影的首映场,同一座位被几百人同时抢,乐观锁的重试次数会变多,这时候可以用数据库的行级悲观锁:

  • 查询座位时直接加排他锁:
    SELECT id, status FROM seat WHERE show_id = ? AND seat_no = ? FOR UPDATE;
    
  • 拿到锁后判断座位是否可用,再执行更新操作,最后提交事务释放锁。

注意:一定要控制事务的大小,查询和更新要在同一个事务里,而且事务要尽可能短,避免长时间占用锁导致其他请求阻塞。另外,MySQL的InnoDB引擎才支持行级锁,MyISAM是表锁,千万别用。

优点:冲突处理直接,不需要前端重试;缺点:锁粒度是行级,但高并发下还是会有排队,不过比进程内锁好,因为可以跨节点共享锁。

3. 分布式锁(适合分布式集群场景)

如果你的系统是多节点部署的,进程内的synchronized完全没用,因为每个节点的锁是独立的,这时候就得用分布式锁,比如基于Redis实现:

Redis分布式锁实现思路:

  • 用户抢座时,先尝试用SETNX(SET if Not eXists)命令获取锁,锁的key可以设为lock:show:{showId}:seat:{seatNo},过期时间设个10秒左右(防止锁持有者宕机导致锁永远不释放)。
  • 拿到锁后,再去数据库查询座位状态,确认可用后执行更新,最后删除锁(注意要判断锁的持有者是自己,避免误删别人的锁,可以用Redis的DEL配合Lua脚本)。
  • 如果拿不到锁,直接返回用户“座位正在被预订,请稍后再试”。

优点:跨节点共享锁,支持大规模集群扩容;缺点:Redis锁可能存在过期时间设置不合理的问题,比如锁过期了但事务还没完成,这时候需要用“锁续约”机制(比如定时任务给锁续期)。

4. 座位预占机制(提升用户体验+降低冲突)

很多主流票务系统都会用预占机制,本质是把“预订”分成两步:预占座位确认支付

  • 用户选座后,系统先把座位标记为“预占中”,并设置一个过期时间(比如15分钟),这段时间内只有该用户能支付锁定座位。
  • 预占可以用Redis来做,把预占的座位信息存在Redis里,过期时间到了自动释放,不用操作数据库。
  • 用户支付成功后,再把数据库里的座位状态改成“已预订”;如果超时未支付,自动释放预占座位。

这个机制的好处是:把大部分并发请求拦截在Redis层,不用频繁操作数据库,而且用户体验更好——选座后有充足的时间支付,不用急着抢。

总结一下选型建议:

  • 中小规模单节点系统:优先乐观锁,简单易实现,扩展性好。
  • 热门场次高冲突场景:悲观锁+预占结合,减少重试。
  • 分布式集群系统:分布式锁+预占,配合Redis做快速拦截。

另外,不管用哪种方案,都要在前端做一些防重复提交的处理,比如按钮置灰、请求防抖,从源头减少无效的并发请求~

内容的提问来源于stack exchange,提问作者Chandan

火山引擎 最新活动