苍穹外卖·核心命门精炼焚诀

发布时间:2026/9/1 5:59:03
苍穹外卖·核心命门精炼焚诀 本篇将梳理苍穹外卖项目中 Java 后端面试的“七寸”问题让你超越“会写代码”的层次拥有“架构师直觉”全文遵循STAR法则S—Situation事情是在什么情况下发生 T—Task你是如何明确你的任务的 A—Action针对这样的情况分析你采用了什么行动方式 R—Result结果怎样在这样的情况下你学习到了什么。苍穹外卖·核心命门精炼笔记这些都是 Java 后端面试的“七寸”问题挨个拆解1. 登录态设计Session vs JWTS场景项目初期单体架构用 HttpSession 存用户信息一切正常。但随着用户量激增服务必须水平扩容到 3 台机器。T任务必须保证用户在任意节点登录后再次请求被 Nginx 路由到其他机器时依然保持登录态且扩容不能引入额外的中心存储瓶颈。A行动弃用依赖服务端内存的 Session全面采用 JWT无状态 Token。用户登录成功后服务端生成含用户ID和签名的加密串后续请求由网关拦截器解密验签直接在请求头里还原用户信息。R结果服务器不再存任何状态横向扩容从“配置共享Redis”的复杂操作变成了“一键加机器”的纯计算扩容彻底零负担。2. 分布式部署下的会话陷阱Session共享危机S场景某次压测时强行复用了旧项目的 Session 方案。Nginx 默认轮询转发导致同一用户在 A 机登录后下一次请求落到 B 机B 机内存找不到 Session直接抛出空指针异常。T任务在不推翻现有架构的前提下解决多 JVM 内存不能互通导致的“反复踢下线”问题。A行动紧急启用 Nginx 的 ip_hash 粘滞会话固定 IP 打到固定机器。同时长远规划将 Session 序列化后集中存入 Redis每次请求先去 Redis 反序列化。R结果ip_hash 临时止血但带来了流量不均内网用户挤爆一台机器的问题最终推动项目全面转向 JWT彻底淘汰了 ip_hash。3. MySQL 索引为何有效B树的降维打击S场景订单表数据量达到 500 万行时按用户ID查订单的接口响应时间从 50ms 飙升到 3.5s几乎超时宕机。T任务必须将查询响应时间压回 100ms 以内且不能改动业务代码。A行动利用 MySQL InnoDB 的 B 树特性重建索引。利用其“矮胖多叉树”特点3层即可支撑 2000 万数据减少磁盘 IO同时利用叶子节点的双向链表优化范围查询。将 SELECT * 改为只查索引覆盖的字段SELECT id, user_id, amount避免回表。R结果查询稳定在 20ms 以内原本撑爆内存的 500 万行排序filesort被索引天然顺序替代。4. 慢 SQL 定位三板斧破案方法论S场景上线后监控平台频繁报警/order/list 接口在晚高峰 P99 延迟达到 5 秒。T任务需要在 10 分钟内从几十万条 SQL 日志里捞出那条致命慢 SQL并给出修复方案。A行动① 开启 slow_query_log设置 long_query_time1s 定向捕获慢日志② 将慢 SQL 粘贴后执行 EXPLAIN一眼看到 typeALL全表扫描且 rows 扫描了 80 万行③ 发现 SQL 中 WHERE DATE(create_time) ‘2026-08-29’ 对索引列用了函数导致索引失效立即改为 create_time BETWEEN ‘2026-08-29 00:00:00’ AND ‘2026-08-29 23:59:59’。R结果扫描行数从 80 万降为 200 条接口秒级恢复事后将该规则写入团队 SQL 审查规范。5. Redis 缓存与 DB 一致性取舍的艺术S场景店铺菜品信息变更频率低但读并发极高QPS 上万。用了缓存后运营修改菜品价格DB 已变缓存却还是旧价格导致用户看到的价格和结算价不符引发大量客诉。T任务做到缓存与数据库的最终一致性且不影响缓存命中率。A行动采用 Cache Aside旁路缓存 策略。更新时先改 DB再删缓存而不是更新缓存防止并发脏写。针对极端情况删缓存失败引入 延迟双删删缓存 - 更新DB - 休眠 100ms - 再删一次最后部署 Canal 监听 MySQL binlog一旦有数据变更异步再次清除对应 Key。R结果99.99% 的请求命中强一致性0.01% 的极端不一致在 1 秒内被 Canal 兜底修复彻底解决资损隐患。6. 重复请求幂等性金融级防坑S场景用户充值/下单时前端因网络抖动未收到返回用户疯狂点击“确认”按钮后端瞬间收到 5 个相同的支付请求。T任务保证无论接收多少次相同请求业务数据只生效一次扣款只扣一次。A行动双层保险。① 请求唯一 ID前端预生成 requestId后端用 Redis SETNX 占位Key业务前缀requestId处理完再删除重复请求直接拦截② 乐观锁/状态机更新订单状态时 SQL 拼接 WHERE status ‘待支付’一旦支付完成状态变更后续 SQL 影响行数为 0自然幂等。R结果压测模拟重复发送 100 次相同请求最终只落库一条数据资金完全安全。7. 超时、重试、降级生死防御链S场景双十一大促期间下游“积分服务”因流量突增响应变慢RT 从 20ms 涨到 3s导致上游订单线程池被全部阻塞触发级联雪崩。T任务在下游故障时必须保障订单主流程不被拖垮。A行动① 超时隔离连接超时设 500ms读超时设 1s超时即抛异常快速失败不占着线程② 退避重试针对幂等的查询接口采用指数退避重试1s, 2s, 4s且最多 3 次③ 熔断降级引入 Resilience4j当错误率超过 50% 时打开熔断器5 秒内不再调用积分服务直接返回 null积分稍后补发。R结果即使积分服务完全不可用订单下单成功率依然维持在 99.5%线程池水位稳定系统扛过了峰值流量。8. 日志还原完整调用链路分布式追踪S场景用户反馈下单失败但前端报错含糊。查了订单服务日志没报错查了库存服务日志也没报错但在消息队列环节卡了壳。跨服务查日志犹如大海捞针。T任务设计一套机制让一次请求分散在 3 个微服务、2 个 MQ 消费组的几十条日志能被一键聚合还原。A行动利用 MDCMapped Diagnostic Context。网关收到请求时生成全局唯一 TraceId塞入 ThreadLocal 并在日志模板配置 [%X{traceId}]。调用下游时将 TraceId 写入 Feign 请求头发送 MQ 时将 TraceId 塞入消息体 headers。下游消费者接收后第一时间从消息头取出 TraceId 放入本线程 MDC。R结果遇到问题只需在 Kibana/ELK 搜索 TraceIdxxx从网关入口到 DB 落库再到 MQ 消费整条时间线、每步耗时一目了然定位故障时间从小时级缩短至 分钟级。总结收尾这 8 个七寸问题本质是互联网分布式架构的“八荣八耻”。面试官问的不是 JWT 怎么生成而是你如何在状态爆炸的分布式世界里做取舍问的不是 B 树结构而是面对海量数据你如何用数据结构对抗物理极限。把 STAR 讲熟把 Trade-off 讲透你就是他们要的那个人。值得反复复盘的通透思维模型下面这些问题让你超越“会写代码”拥有“架构师直觉”状态归属权到底是有状态Session还是无状态JWT任何需要“扩容”的系统都必须朝“无状态”演进。你的项目里哪些地方偷偷藏了本地状态如 HashMap 本地缓存宕机丢了怎么办强一致还是最终一致缓存与 DB、Redis 库存与 MySQL 库存你选的是强一致加分布式事务 XA还是最终一致异步重试秒杀场景下丢掉一点“最终一致时间”换“高并发吞吐量”值不值“查”与“改”的逻辑隔离慢 SQL 只发生在查。秒杀场景下为什么读库存要走 Redis 而写订单要走 MySQL读写分离不仅发生在数据库主从还发生在存储介质Redis vs MySQL的物理隔离。重复的本质幂等不是防“两次点击”而是防“两次处理同一业务标识”。你需要定义清楚什么才算是“同一笔业务”订单号支付流水号。故障的连锁反应超时 - 重试 - 重试风暴打垮下游 - 上游熔断 - 整体降级。这一连串反应中熔断阈值错误率/慢调用比例设多少才能既感知故障又不误伤正常流量一句话心法所有的分布式难题最终都归结为“数据在多台机器间如何达成共识”锁、事务、一致性。所有的性能优化最终都归结为“把同步变异步、把磁盘变内存”。反复嚼这两句项目面试稳过。

相关新闻