高并发下秒杀系统瓶颈?——面试官想听的不是“列表”,而是你对系统瓶颈的穿透式理解

⚠️ 注意:面试官问“瓶颈在哪”,绝不是让你背“数据库、缓存、MQ”这五个词。他真正想考察的是:
你是否经历过真实压测?是否在流量洪峰中亲手调过参、改过架构、踩过坑?有没有把“高并发”从概念落到线程、连接、锁、缓冲区这些微观层面?

下面我用实战视角+原理拆解+常见误区,带你一层层剥开秒杀系统的“脆弱点”。


一、核心瓶颈不是“模块”,而是“资源争抢”——本质是有限资源 vs 无限请求

秒杀的本质是:10万用户同时点“立即抢购”,但库存只有100件
这不是业务逻辑问题,而是资源调度战争:CPU、内存、网络带宽、磁盘IO、连接数、锁粒度……全在抢同一块蛋糕。


二、五大瓶颈逐层深挖(附原理+代码陷阱)

🔴 1. 数据库:不是“慢”,而是“被锁死”

  • 原理:MySQL默认可重复读隔离级别下,UPDATE stock SET count = count - 1 WHERE id = 1 AND count > 0 这条语句会加行级间隙锁(Gap Lock)+ 记录锁(Record Lock),形成临键锁(Next-Key Lock)。
    → 10万请求排队等同一行锁,QPS从3000暴跌到200,大量超时。
  • 典型错误代码
    // ❌ 千万别这么写!先查再更新(非原子),还可能超卖
    int stock = jdbcTemplate.queryForObject("SELECT count FROM item WHERE id = ?", Integer.class, itemId);
    if (stock > 0) {
        jdbcTemplate.update("UPDATE item SET count = count - 1 WHERE id = ?", itemId); // 此刻已超卖!
    }
    
  • ✅ 正确姿势:SQL原子扣减 + 影子库存预校验 + 分库分表/读写分离
    UPDATE item SET count = count - 1 
    WHERE id = ? AND count >= 1; -- 返回影响行数,=1才成功
    

🟡 2. 缓存:击穿、雪崩、穿透三连击

  • 击穿:热门商品key过期瞬间,10万请求穿透到DB → 全部打在一条SQL上。
    ✅ 解法:逻辑过期 + 分布式互斥锁(Redis SETNX),不是简单加锁,而是“只让一个线程重建缓存,其余线程读旧数据”。
  • 雪崩:大量key同一时间过期 → DB雪崩。
    ✅ 解法:过期时间 + 随机偏移量(如 base + Random(1~60) 秒)
  • 穿透:恶意请求 id=-1 或超大ID → 缓存不命中,DB查不到还反复查。
    ✅ 解法:布隆过滤器(Bloom Filter)前置拦截 + 空值缓存(Cache Null)

🟢 3. 消息队列:不是“削峰”,而是“保命阀”

  • 误区:“用了MQ就高并发了?” 错!MQ本身是瓶颈:
    • Kafka:单Partition吞吐上限约10w/s,但秒杀峰值常达50w+/s → 必须多Topic + 多Partition + 异步刷盘调优
    • RabbitMQ:内存堆积导致OOM → 必须设置prefetch=1 + 死信队列兜底 + 消费端限流
  • ✅ 关键设计:下单成功即返回“排队中”,MQ异步扣库存+发券,失败走补偿任务

🔵 4. 网络IO:连接数耗尽 & TCP队列溢出

  • Linux默认 net.core.somaxconn=128net.core.netdev_max_backlog=1000
    → 10万并发连接?SYN队列满,大量Connection refused或超时。
  • ✅ 必调参数:
    echo 65535 > /proc/sys/net/core/somaxconn
    echo 65535 > /proc/sys/net/core/netdev_max_backlog
    echo 1 > /proc/sys/net/ipv4/tcp_tw_reuse  # TIME_WAIT复用
    

🟣 5. 应用服务:线程池 & GC 成为“隐形杀手”

  • Tomcat默认 maxThreads=200 → 10万请求全部进等待队列,OOM前先卡死。
  • 更致命的是:频繁创建对象 → Young GC飙升 → STW停顿 → 请求堆积 → 雪崩
    ✅ 实战方案:
    • Web层用WebFlux(Netty+Reactor)替代Servlet阻塞模型
    • 扣库存用无锁编程(CAS + LongAdder) 替代synchronized
    • 对象复用:ThreadLocal<ByteBuffer>避免GC压力

三、面试高频误区(答错直接扣分!)

误区 正解
“加Redis就能扛住秒杀” Redis只是缓存,不能替代事务一致性;没做预热/击穿防护,它就是第一个倒下的
“用MQ就解决了所有问题” MQ是异步通道,不解决超卖、不保证最终一致;没做消息幂等、重试、死信,订单会丢
“分库分表万能” 分库后分布式事务难搞,TCC太重,Seata性能差;不如先做库存分段(100件分10段,每段10个)
“前端加按钮置灰就行” 用户F5刷新、脚本攻击、代理IP绕过 → 必须服务端令牌桶限流(Guava RateLimiter or Sentinel)+ 设备指纹校验

四、一句话总结(面试收尾金句)

秒杀没有银弹,只有“层层漏斗”:前端限流 → 网关熔断 → 缓存预热 → 库存分段 → SQL原子扣减 → MQ异步化 → 补偿对账。每个环节都在和“资源竞争”搏斗,而真正的高并发经验,是你在凌晨三点盯着Arthas火焰图,发现那个String.substring()正在疯狂创建对象——那一刻,你才真正懂了瓶颈。

需要我展开讲某一层(比如Redis击穿实战代码 / Kafka分区压测调优 / Netty零拷贝优化),随时喊我。
更多Java面试题整理:

JVM面试题
MySQL面试题
Redis面试题
Spring面试题

完整面试题库:
https://myquotego.com/html/questions?_from=csdn_123_4

Logo

腾讯云面向开发者汇聚海量精品云计算使用和开发经验,营造开放的云计算技术生态圈。

更多推荐