高级 Java 面试急救训练(3~7 天冲刺)

目标岗位:物联网平台 / 交易支付后端 · 14 年 Java · 工业物联网专家

📌 约 100 道题 ⏱ 平均每题 8 分钟 🎯 按重要度排序 🗣 每题 4 段式(答案/原因/追问/高分)

使用说明

★★★★★ 必会(每题 5 遍以上) ★★★★☆ 高频(重点准备) ★★★☆☆ 加分(锦上添花)
  • 以后凡是讲项目,都按这个顺序:第一,为什么这么设计。 第二,具体怎么实现。 第三,这样设计带来了什么收益。
  • 00

    自我介绍

    ★★★★★
    面试官您好,我叫xx,毕业于xxx管理学院计算机专业,从事 Java 后端开发已经 14 年。 我主要做的是企业级后台系统和平台架构设计,近几年主要深耕两个方向:一个是工业物联网平台,另一个是安防 SaaS 平台。 在工业物联网项目中,我主要负责 MQTT 设备接入、Flink 实时计算、TDengine 时序数据库以及整个实时数据处理链路的设计与开发,平台目前支撑 5000 多个测点实时监控,日均处理百万级时序数据。 在安防行业,我负责过一卡通、门禁、停车、访客等系统,也参与过微信支付、支付宝等支付能力的接入,以及微服务架构升级和 Docker 容器化部署。 除了开发工作,我也做过团队管理和项目管理,负责需求评审、技术方案设计以及核心模块攻关。 我看了贵公司的岗位,涉及 MQTT、物联网、支付、高并发等方向,与我的工作经历非常契合,也希望能够加入贵公司,把自己的经验应用到新的产品中。
    01

    项目深挖(30 题)

    ★★★★★
    占面试 60% · 全基于你的真实项目

    第一章 工业物联网 · MQTT 15 题

    你简历:搭建高可用 MQTT Source/Sink,保证消息可靠性;MQTT + WebSocket 实现控制指令下发和前端实时监控;支撑 5000+ 测点,延迟 <1s。

    Q01 为什么选择 MQTT,而不是 HTTP? ★★★★★ +
    标准答案(2 分钟)

    物联网设备通信,MQTT 比 HTTP 优势主要在四点:

    • 协议开销小:MQTT 最小报文 2 字节,HTTP 每次都要带 Header(几百字节),弱网/低功耗设备受不了。
    • 长连接 + 双向推送:MQTT 是 TCP 长连接,服务器可以主动下发控制指令到设备;HTTP 只能客户端拉。
    • 低功耗:MQTT 有心跳和离线消息机制,设备大部分时间可以休眠,电池能用几年。
    • QoS 等级:MQTT 自带 QoS 0/1/2,能在协议层保证消息可靠;HTTP 只能业务层重试。

    我们项目 5000+ 测点、很多是工业现场的 PLC/传感器,网络环境差,HTTP 撑不住。所以选了 MQTT。

    为什么这么回答

    面试官问的是你做技术选型的判断逻辑,不是"MQTT 是什么"。要体现"场景驱动选型",不能只背协议定义。HTTP 也能做 IoT(轮询),但代价大——面试官想看你是否真懂场景。

    面试官会怎么追问
    • "HTTP 不能用 WebSocket 吗?" → 能,但 MQTT 还多了主题路由、QoS、遗嘱消息,是为 IoT 专门设计的语义。
    • "MQTT 比 CoAP 强在哪?" → CoAP 是 UDP 的,适合极端受限设备;MQTT 是 TCP 的,更通用,企业级首选。
    • "如果设备只有 2G 信号,MQTT 怎么办?" → 用 QoS1 + 报文压缩 + 心跳拉长到 60s。
    怎么回答能拿高分
    补充一句数字:"我们用 MQTT 单 Broker 支撑 5000+ 长连接,平均每条消息 50 字节,CPU 不超过 20%。"再补一句取舍:"如果将来要做视频类设备流,会换 RTSP/WebRTC;MQTT 只做信令和控制。"
    Q02 MQTT QoS 0/1/2 的区别?为什么你们选择 QoS1? ★★★★★ +
    标准答案(2 分钟)
    • QoS 0(At most once):发一次就忘,可能丢。适合不重要的数据,比如环境监测的次要指标。
    • QoS 1(At least once):至少一次,需要 PUBACK 确认;可能重复,但保证到达。我们用这个
    • QoS 2(Exactly once):四次握手,保证不丢不重。最可靠,但最慢,是 QoS1 的 2-3 倍延迟。

    选 QoS1 的原因:

    • 我们的核心数据是工业实时测点,不能丢,但偶尔重复业务层去重就行(设备 ID+时间戳做主键)。
    • QoS2 在 5000 测点 + 高频上报场景下,Broker 压力翻倍,没必要。
    • 告警类消息用 QoS2,量小但要严格不重。
    为什么这么回答

    这是MQTT 必问题。回答要体现:不是"教科书答案",而是"在真实业务里怎么权衡"。面试官想看你对"可靠性 vs 性能"有没有自己的判断。

    面试官会怎么追问
    • "重复消息你们怎么去重?" → Kafka 消费时用 (deviceId, timestamp) 做幂等键,数据库做唯一约束。
    • "什么场景必须 QoS2?" → 计费、订单、告警触发、远程开关这种不能误操作的。
    • "QoS1 + 弱网会怎么样?" → 重传可能乱序,要在消息里带单调递增 seq,consumer 端做排序判断。
    怎么回答能拿高分
    画一张表:QoS / 报文次数 / 是否重复 / 适用场景。再补一句工程经验:"我们测过,QoS2 在 5w QPS 下 Broker CPU 涨 30%,而 QoS1 只涨 8%,所以 90% 数据用 QoS1。"
    Q03 MQTT 为什么还要加 Kafka? ★★★★★ +
    标准答案(2 分钟)

    MQTT 和 Kafka 解决的问题不一样,分工合作:

    • MQTT 解决"设备到平台":长连接、协议适配、设备管理、QoS。
    • Kafka 解决"平台内部数据流转":解耦、削峰、广播、持久化、多消费者。

    没有 Kafka 时的问题:

    • Flink、消费侧、业务方都直连 MQTT Broker,消费慢会反压设备连接
    • 多消费者抢消息,需要自己实现广播/分发。
    • 消息没有持久化,Broker 重启数据丢。

    架构:设备 → EMQX → Kafka → Flink → TDengine / 业务。Kafka 是"数据枢纽"。

    为什么这么回答

    这是架构师级别问题。考察你对"分层架构"的理解:MQTT 是"接入层",Kafka 是"传输层",Flink 是"计算层"。讲清楚职责分离。

    面试官会怎么追问
    • "EMQX 自己有持久化插件,为什么不用?" → EMQX 持久化只解决设备离线补发,不解决多消费者分发;而且 EMQX 重点不在存储。
    • "MQTT 消息怎么进 Kafka?" → 用 EMQX 的 Kafka Bridge 插件,或自己写一个消费者服务订阅所有 topic 后转发。
    • "Kafka 怎么知道消息顺序?" → 单 partition 内有序;我们按 deviceId 哈希到固定 partition,保证同一设备有序。
    怎么回答能拿高分
    画一张清晰的架构图(设备-MQTT-Kafka-Flink-DB),强调分层。然后补一句:"这套架构我们扩到 10w 测点时只加了 Kafka partition 数量,其他几乎不动——这就是分层的价值。"
    Q04 Topic 是怎么设计的? ★★★★☆ +
    标准答案(2 分钟)

    我们用层级式 Topic,类似文件系统路径:

    • 上行(设备 → 平台):iot/{productKey}/{deviceId}/dataiot/{productKey}/{deviceId}/eventiot/{productKey}/{deviceId}/status
    • 下行(平台 → 设备):iot/{productKey}/{deviceId}/cmdiot/{productKey}/{deviceId}/config
    • 通配符订阅:业务方订阅 iot/+/+/data 就能拿到所有设备数据。

    设计原则:

    • 产品维度隔离:productKey 是产品类型,方便按产品做权限和路由。
    • 消息类型分离:data / event / status 走不同 topic,consumer 按需订阅,节省流量。
    • 通配符谨慎用# 全订阅只能给内部管理用,给业务方会拖垮 Broker。
    为什么这么回答

    Topic 设计是 MQTT 项目最直接的经验体现。面试官会从你的设计判断你是否做过大规模项目。乱设计的 topic 上线后无法扩展。

    面试官会怎么追问
    • "topic 数量有没有限制?" → EMQX 单机 10w+ topic,但建议按业务聚合,不要太细。
    • "权限怎么控?" → EMQX 支持 ACL,按 username 限制可订阅/发布的 topic 模式。
    • "百万设备 topic 一样吗?" → topic 模板只有 1w 个,但每条消息的 topic 字符串会带 deviceId 拼接;EMQX 内部用 trie 树索引,性能 OK。
    怎么回答能拿高分
    讲一个"踩坑"故事:"我们早期把所有消息都扔一个 topic,consumer 一订阅就拉全量,后来按 data/event/cmd 拆开,consumer 性能提升 5 倍。"
    Q05 如何下发设备控制指令? ★★★★★ +
    标准答案(2 分钟)

    下发链路:业务系统 → 控制服务 → MQTT Broker → 设备。关键设计:

    • 异步 + 状态机:指令是异步执行的,下发后指令进入"已下发"状态;设备收到后回 ACK,状态变为"已送达";设备执行完回结果,状态变为"成功/失败"。
    • 指令 ID 全链路透传:每条指令带唯一 requestId,从业务到设备一路带,设备 ACK 时带回。
    • 超时 + 重试:下发后 5s 没回 ACK 视为失败,可重试 2 次。设备侧也要做幂等(同 requestId 直接返回之前结果)。
    • QoS 选择:控制指令用 QoS1 或 QoS2,绝不能 QoS0(丢了就出大事)。
    为什么这么回答

    控制指令是 MQTT 项目里最容易出故障的部分。考察你对"分布式系统异步调用 + 状态机 + 幂等"的掌握。

    面试官会怎么追问
    • "设备不在线,指令怎么下?" → 存到"待下发队列",设备上线后通过 Retain Message 或离线消息机制补发。
    • "怎么保证指令和 ACK 严格对应?" → requestId 贯穿;服务端用 ConcurrentHashMap 存 {requestId → CompletableFuture},等设备 ACK 唤醒。
    • "批量下发怎么办?" → 一次下发不要超过 100 个,超过就分批,每批之间留 100ms 间隔,避免 Broker 拥塞。
    怎么回答能拿高分
    讲一个状态机图(已下发/已送达/成功/失败/超时),强调每个状态都有明确的触发条件和超时时间。再补一句:"控制类指令的可靠性要求比数据上报高一个量级,所以 QoS 必须 ≥1。"
    Q06 设备离线怎么办? ★★★★☆ +
    标准答案(2 分钟)

    设备离线分两个角度处理:

    • 设备侧的"我自己离线了"
      Last Will(遗嘱):设备连接时设置一个 Will 消息,Broker 检测到非正常断开(如网络断)会主动发到指定 topic,平台收到后标记设备离线。
      KeepAlive 心跳:客户端定期发 PINGREQ,1.5 倍心跳时间内没收到就判定离线。
    • 平台侧要给离线设备补发什么
      Retain Message(保留消息):设备重连后第一时间能拉到最新状态。
      离线消息队列:用数据库或 Redis 存"待下发指令",设备上线后从队列取出来下发。
    为什么这么回答

    这是考察"对 MQTT 协议特性是否真的用起来"的题。Last Will 和 Retain Message 是 MQTT 的特色,但很多新手从来没用过。答出这两个词直接加分。

    面试官会怎么追问
    • "Last Will 在 EMQX 怎么配置?" → 控制台 / REST API / 配置文件都可以,client 端连接时指定 will topic 和 payload。
    • "设备网络抖动频繁怎么办?" → 心跳时间设长一点(如 60s),重连要带退避(1s/2s/4s/8s),不要一直重连耗电。
    • "怎么区分'真的离线'和'网络抖动'?" → 引入"灰度状态":第一次断连标"疑似离线",超过 3 分钟还没回才标"离线",避免误报。
    怎么回答能拿高分
    画一张"设备状态机":在线/疑似离线/离线/重新上线,标注每个状态切换的触发事件和超时时间。这比"加心跳"三个字强 10 倍。
    Q07 如何保证消息不丢? ★★★★★ +
    标准答案(2 分钟)

    消息不丢要分 5 个节点全链路保障,任何一个断链都不行:

    1. 设备 → Broker:QoS1/2 + 设备端本地缓存(断网时存文件,恢复后重发)。
    2. Broker 接收:EMQX 关闭 allow_anonymous,开启持久化会话(clean_session=false),客户端重连后能续传。
    3. Broker → Kafka:EMQX Kafka Bridge 异步发送 + 本地磁盘 WAL;Bridge 挂了不丢消息,重启后从 WAL 恢复。
    4. Kafka 持久化acks=all + 副本 ≥3 + min.insync.replicas=2
    5. Consumer:手动提交 offset,先处理业务再 commit;处理失败进死信队列,告警人工介入。

    任何一环都有兜底:设备缓存、Broker WAL、Kafka 副本、Consumer 重试。

    为什么这么回答

    这是面试"消息可靠性"的标准题。你的方案必须全链路 5 个节点都说一遍,只说"用 QoS1"是不够的。

    面试官会怎么追问
    • "acks=all 性能下降多少?" → 大约 30%,但我们对延迟要求是秒级,可接受。
    • "消费者先 commit 再处理会怎样?" → 处理失败时 offset 已经前移,消息丢失。所以必须反过来。
    • "最坏情况消息丢了怎么办?" → 业务层加对账机制:定期拿设备端实际数据 vs 平台入库数据比对,差的补齐。
    怎么回答能拿高分
    画一张"消息流向 + 可靠性兜底点"图,标清楚每个节点用什么机制保证不丢。强调"5 个节点 + 1 个对账"是防御性编程的体现。
    Q08 如何保证消息不重复? ★★★★☆ +
    标准答案(2 分钟)

    MQTT QoS1 至少一次 + Kafka 至少一次,重复是必然,要靠业务层去重。三种去重策略:

    • 唯一键去重(最常用):消息带 deviceId + seqdeviceId + timestamp,落库前用 INSERT ... ON DUPLICATE KEY UPDATE 或唯一索引。
    • Redis 幂等表:消费前先 SETNX messageId 1 EX 86400,存在就跳过。
    • 状态机去重:业务本身有状态机,重复消息不会推进状态(比如订单已支付,重复支付消息不处理)。

    我们项目用 deviceId + timestamp 唯一键 + TDengine 唯一索引,因为数据量极大(百万级/天),Redis 存不下。

    为什么这么回答

    面试官知道"不丢也意味着可能重复",这题考察你幂等性的设计能力。和支付幂等(第四阶段)是同一套思路。

    面试官会怎么追问
    • "设备 seq 重复了怎么办?" → 设备侧要保证 seq 严格单调递增,重启后归零会冲突,要用 (deviceId, sessionId, seq) 三元组。
    • "Redis 去重和 DB 去重怎么选?" → 数据量小(万级)用 Redis 简单;数据量大(百万级)走 DB 唯一索引,省内存。
    • "能不能用布隆过滤器?" → 可以,容忍极低误判(万分之一)时能极大节省内存,但删除困难,要权衡。
    怎么回答能拿高分
    说出一个数字:"我们 1 个月大概出现 0.1% 的重复,DB 唯一索引直接挡住,下游完全无感。"真实数据比理论方案更有说服力。
    Q09 MQTT Broker 为什么选择 EMQX? ★★★★☆ +
    标准答案(2 分钟)

    开源 Broker 主流对比:

    • Mosquitto:C 写的,轻量;但单实例,集群弱。
    • VerneMQ:Erlang,支持集群;社区活跃度下降。
    • EMQX:Erlang,单节点 100w+ 连接,原生集群(基于 Mria),开源版功能就够用,国人开发,中文文档好。
    • 云厂商(AWS IoT Core / 阿里云物联网平台):省心但贵,数据出云,长期成本高。

    选 EMQX 的关键原因:

    • 性能:单节点百万连接,符合工业项目未来扩展。
    • 插件丰富:Kafka Bridge / MySQL/Pg 认证 / 规则引擎 / WebHook 都内置。
    • 国产化兼容:能在麒麟、统信系统跑,符合我们另一个项目的信创要求。
    • 运维成本低:集群部署有官方 dashboard,问题定位快。
    为什么这么回答

    面试官看你是做过技术选型调研,还是"听说 EMQX 不错就用了"。对比表 + 国产化 + 插件是关键。

    面试官会怎么追问
    • "EMQX 和 Mosquitto 性能差多少?" → Mosquitto 单机 1w 连接,EMQX 单机 100w+,差两个数量级。
    • "EMQX 集群怎么做的?" → 基于 Mria 的分布式架构,节点间复制 session 和路由表;至少 3 节点保证 HA。
    • "出问题怎么排查?" → Dashboard 看连接数、消息速率、慢订阅;客户端 ID + topic 维度追踪。
    怎么回答能拿高分
    补一个真实对比:"我们测过 Mosquitto,1w 连接 CPU 就 60%;同样配置 EMQX 跑了 50w 连接才到 30%,这就是差距。"具体数字能让面试官信服。
    Q10 一个 Broker 能支撑多少连接? ★★★★☆ +
    标准答案(2 分钟)

    EMQX 官方压测:单节点 100w+ MQTT 连接、100w+ QPS 消息路由。但实际生产要看四个变量:

    • 单消息大小:平均 200 字节 vs 2KB,性能差 10 倍。
    • 消息频率:每设备 1 条/分 vs 100 条/秒,差 6000 倍。
    • Topic 数量:1w 个 vs 100w 个,路由表内存差很多。
    • 客户端库:Java Paho 调优后比裸 socket 慢 20%。

    我们项目:5000 测点 × 1Hz × 平均 300 字节 ≈ 1.5MB/s 入站流量,4 核 8G 单节点完全够用,预留 10 倍容量。

    但生产部署至少 3 节点集群做 HA,避免单点。

    为什么这么回答

    考察你对"理论 vs 实际"的判断。直接说"100w"显得没做过;说"看场景"才是工程师思维。

    面试官会怎么追问
    • "怎么压测?" → emqtt-bench 工具,模拟设备和消息。
    • "100w 连接机器要多大?" → 64 核 128G 是常见配置;文件描述符要调到 100w+。
    • "超过 100w 怎么办?" → 多 Broker 集群,EMQX Enterprise 支持跨集群桥接;或按地域拆分。
    怎么回答能拿高分
    补一个容量规划公式:"连接数 = (核数 × 5w) ÷ (消息频率 / 10)",并说"我们做容量规划时按 5 倍冗余评估",体现工程严谨性。
    Q11 如何做设备认证? ★★★☆☆ +
    标准答案(2 分钟)

    MQTT 认证三件套:

    • Username / Password:连接时带,传统方案。Username 用 deviceId,Password 是动态 token。
    • Client ID:必须是全局唯一,建议用 productKey + deviceId 拼接。
    • X.509 证书:TLS 双向认证,最安全,但部署复杂,适合高安全场景。

    我们方案:设备首次激活时,烧录 productKey + deviceKey + 平台颁发的 deviceSecret(动态 token,24h 过期)。设备每次连接用 secret 签一次名,服务端校验。

    EMQX 后端认证:MySQL/HTTP/Redis 插件。生产用 HTTP 自定义认证,调用我们的设备管理服务,灵活支持"禁用/限速"。

    为什么这么回答

    物联网安全是必问的,不能只回答"用密码",要展示完整的认证体系设计:设备身份、动态密钥、平台校验、吊销机制。

    面试官会怎么追问
    • "设备被盗用了怎么办?" → deviceSecret 一次一密或短期有效;远程吊销(黑名单推送到 EMQX)。
    • "百万设备怎么发证书?" → 设备厂商在产线烧录;我们用"动态激活":设备发序列号过来,平台下发密钥,避免预烧录泄密。
    • "EMQX 自带认证够用吗?" → 简单场景够;复杂业务(按租户、按时段限速)需要 HTTP 认证对接业务系统。
    怎么回答能拿高分
    补一句"安全分层":传输层 TLS + 协议层用户名密码 + 业务层限速配额,三层一起做,单层被破不影响整体安全。
    Q12 如何保证 MQTT 安全? ★★★☆☆ +
    标准答案(2 分钟)

    四个层面:

    • 传输加密:MQTT over TLS 1.2+,禁用 TLS 1.0/1.1(已知漏洞)。
    • 认证:Username/Password 或 X.509 证书,禁用 allow_anonymous
    • 授权(ACL):每个 clientId 只能订阅/发布自己的 topic,不能越权。EMQX 支持内置 ACL 或外部 HTTP 鉴权。
    • 网络隔离:Broker 不暴露公网,设备走 VPN/专线;管理端口(18083 dashboard)只允许内网访问。

    我们还做:连接频率限制、消息大小限制、topic 黑名单(防恶意订阅)。

    为什么这么回答

    工业物联网常被攻击(去年美国燃油管道勒索事件),安全是必问题。要展示"多层防御"思维。

    面试官会怎么追问
    • "TLS 握手慢,弱网设备怎么办?" → TLS Session Ticket 复用;预共享密钥(PSK)模式减少握手。
    • "ACL 规则多了性能下降吗?" → 10w 条规则 EMQX 内部用 trie 树,毫秒级;过多会内存涨,可以分实例。
    • "怎么发现异常连接?" → Dashboard 看地理位置异常、高频连接失败;EMQX 有告警规则插件。
    怎么回答能拿高分
    提一句等保 2.0:"我们这个系统要过等保三级,所以认证、审计、传输加密都要按标准做。"体现你懂合规。
    Q13 KeepAlive 是什么? ★★★☆☆ +
    标准答案(2 分钟)

    KeepAlive 是 MQTT 连接的心跳机制:

    • CONNECT 报文里设置 KeepAlive 时长(秒),范围 0-65535,建议 30-60s。
    • 客户端在这个时间间隔内必须发一条控制报文(PINGREQ);Broker 收到回 PINGRESP
    • 如果 1.5 倍 KeepAlive 时间内 Broker 没收到任何报文,就判定设备掉线,关闭连接并触发 Last Will。
    • KeepAlive = 0 表示关闭心跳(不推荐)。

    我们项目设 60s:太短耗电+浪费流量;太长断线感知慢。

    为什么这么回答

    这是基础中的基础,但很多人只背协议不背参数。答出 1.5 倍这个细节直接说明真用过。

    面试官会怎么追问
    • "KeepAlive 和 TCP keepalive 什么关系?" → 两者不同!MQTT KeepAlive 是应用层协议规定的;TCP keepalive 是操作系统层。MQTT 心跳到了 TCP 不一定到。
    • "心跳包本身算流量吗?" → 算,PINGREQ/PINGRESP 各 2 字节;5000 设备 60s 一次,1 天约 14MB,可忽略。
    • "客户端不发心跳但有数据发,行吗?" → 行,任何控制报文和数据报文都算"在线证据"。
    怎么回答能拿高分
    补一句取舍:"我们有些设备是 4G 流量卡,按流量计费,所以 KeepAlive 设 90s,节省流量;局域网设备设 30s,实时性高。"体现按场景调参
    Q14 Retain Message 有什么作用? ★★★☆☆ +
    标准答案(2 分钟)

    Retain(保留消息)是 MQTT 的一个特殊标志位:

    • 发布时设 retain=true,Broker 会把这条消息持久化在该 topic 上。
    • 任何新订阅者订阅这个 topic 时,立即收到这条保留消息(无需等下次发布)。
    • 发一条 retain=true + payload 为空 的消息可以清除该 topic 的保留消息。

    典型用法:

    • 设备的当前状态(在线/离线/故障)→ 新上线的设备或重启的监控页面能立即知道设备状态。
    • 设备的最新配置 → 设备重连后能立即拉到最新配置,不用等下次推送。
    为什么这么回答

    Retain Message 80% 的人背不下来具体用法,答出"立即拿到最新状态"就能让面试官眼前一亮。

    面试官会怎么追问
    • "Retain 消息会一直留着吗?" → 是的,直到被新 retain 消息覆盖,或发空 payload 清除。
    • "Retain 消息的 QoS 怎么定?" → 跟普通消息一样,独立于 Retain 标志。
    • "能不能用 Retain 做"设备状态变更历史"?" → 不能,Retain 只存最新一条;历史要存 DB。
    怎么回答能拿高分
    讲一个具体场景:"我们把设备的最新状态用 Retain 存在 iot/{productKey}/{deviceId}/status topic,监控大屏订阅这个 topic,重启后 1 秒内就能看到设备状态。"
    Q15 Last Will 有什么作用? ★★★☆☆ +
    标准答案(2 分钟)

    Last Will and Testament(遗嘱消息,LWT):

    • 客户端 CONNECT 时注册一条 Will 消息:包含 topic、payload、QoS、retain。
    • 当 Broker 检测到客户端 非正常断开(如网络断、未发 DISCONNECT),自动把 Will 消息发布到指定 topic。
    • 正常断开(客户端主动发 DISCONNECT)不会触发 Will。

    典型用法:设备掉线告警。设备连接时注册 Will 到 iot/{productKey}/{deviceId}/status,payload 是 {"online":false}。设备异常断网时,平台立即收到告警。

    为什么这么回答

    和 Q14 Retain 配合记忆:Retain 是"最新状态",Will 是"异常掉线告警"。这俩是 MQTT 协议特色,用好了能省一大堆代码。

    面试官会怎么追问
    • "正常关闭会触发 Will 吗?" → 不会。Will 只在非正常断开时触发。
    • "设备主动重连过程会触发 Will 吗?" → 不会,客户端主动发 DISCONNECT 包是正常的;网络闪断才会触发。
    • "怎么区分"网络断"和"设备主动关机"?" → 设备主动关机时,先发一条"我下线了"消息到 status topic(普通消息),再发 DISCONNECT 断开。平台优先信普通消息,Will 作为兜底。
    怎么回答能拿高分
    画一张时序图:设备正常下线 vs 设备异常掉线,对比两条路,分别触发什么消息。视觉化记忆最深刻。

    第二章 Kafka 8 题

    你简历:MQTT 数据通过 Kafka 进入 Flink;要求理解 Kafka 核心原理才能讲清楚选型。

    Q16 为什么要加 Kafka? ★★★★☆ +
    标准答案(2 分钟)

    核心原因:解耦 + 削峰 + 广播 + 持久化。详细:

    • 解耦:Flink、告警服务、数据归档、第三方推送都要消费同一份数据,没有 Kafka 就要每个都直连 Broker。
    • 削峰:设备峰值时可能 5w QPS,下游 Flink 处理能力 2w QPS,Kafka 缓存消息,下游慢慢消费。
    • 广播:一份数据,多个消费者组都拿到自己那份,互不影响。
    • 持久化:消息存 3 天,重启 Flink 可以从 offset 重读,不丢不重。
    • 异步处理:Flink 故障时设备消息不丢失,等 Flink 恢复后继续。
    为什么这么回答

    和 Q3 类似,但侧重 Kafka 自身价值。"削峰"是 Kafka 最经典的卖点,必答。

    面试官会怎么追问
    • "Kafka 自己会成为瓶颈吗?" → 集群 partition 够就能横向扩展,单 broker 不行就加节点。
    • "为什么不用 Redis Stream?" → 数据量上去(百万级/天)后 Redis 内存撑不住;Kafka 顺序写盘,成本低 10 倍。
    怎么回答能拿高分
    补一句:"我们用 Kafka 把 MQTT 的"流"和 Flink 的"算"解耦,2023 年 Flink 故障过 2 次,没有一条数据丢失,因为 Kafka 都存着。"
    Q17 Kafka 如何保证消息不丢? ★★★★★ +
    标准答案(2 分钟)

    三个层面保证:

    • 生产者不丢acks=all + 重试 + 幂等生产者(enable.idempotence=true,自动去重)。
    • Broker 不丢
      ① 副本数 ≥3(replication.factor=3
      ② 最小同步副本 ≥2(min.insync.replicas=2
      ③ 刷盘策略 log.flush.interval.messages 不依赖,主要靠 OS 页缓存 + 副本。
    • 消费者不丢关闭自动提交enable.auto.commit=false),先处理业务再手动 commit;处理失败不进 commit,下次重读。

    最严苛配置:acks=all + replication.factor=3 + min.insync.replicas=2 + enable.idempotence=true

    为什么这么回答

    这是 Kafka 面试第一高频题,也是 MQ 类问题的"三段式"标准答案:生产、Broker、消费三段全说。

    面试官会怎么追问
    • "acks=1 和 acks=all 差多少?" → acks=1 只等 leader 落盘就返回,leader 挂了可能丢;acks=all 等所有副本,安全性 10 倍差距。
    • "幂等生产者能防什么?" → 防止生产者重试导致的重复消息;原理是 PID + sequence number。
    • "事务消息 vs 幂等消息区别?" → 幂等只防"生产重试"重复;事务能保证"生产 + 消费"原子性,跨 topic 跨 partition。
    怎么回答能拿高分
    强调一个反常识点:"很多新手以为 replication.factor=3 就万无一失,其实如果 min.insync.replicas=1,leader 一写就 ack,副本还没同步就挂了照样丢。两个参数必须配合。"
    Q18 Kafka 为什么吞吐量高? ★★★★☆ +
    标准答案(2 分钟)

    Kafka 高吞吐来自 5 个设计:

    • 顺序写盘:生产者只往 partition 末尾追加(顺序 I/O),机械盘也能到 100MB/s+;比随机写快 1000 倍。
    • 零拷贝(sendfile):消费者读消息时,数据从磁盘 → 内核缓冲区 → socket,不经过用户空间,少一次 CPU 拷贝。
    • 页缓存(Page Cache):读写走 OS 页缓存,不直接落盘;后台异步刷盘。
    • 批处理:producer 攒一批再发(linger.ms + batch.size),减少网络请求数。
    • 分区并行:一个 topic 分成 N 个 partition,N 个消费者并行处理,水平扩展。
    为什么这么回答

    考察"对底层原理的理解深度"。这 5 点都答出来就是 Kafka 高级;只答 2-3 点是初级。

    面试官会怎么追问
    • "零拷贝具体怎么实现的?" → Java NIO 的 FileChannel.transferTo(),底层调用 Linux 的 sendfile 系统调用。
    • "页缓存会不会丢数据?" → 进程挂了页缓存会丢;Kafka 用副本机制兜底,多副本都在内存里就还在。
    • "顺序写比随机写快多少?" → HDD 差 100-1000 倍;SSD 差 10-100 倍。
    怎么回答能拿高分
    画一张"生产者-磁盘-消费者"的数据流图,标清楚"页缓存"和"零拷贝"两个关键优化点。视觉化让面试官一眼懂。
    Q19 Consumer Group 原理? ★★★★☆ +
    标准答案(2 分钟)
    • 每个 Consumer 启动时指定一个 group.id,多个 consumer 同一个 group.id 组成一个 Consumer Group。
    • 一个 partition 只能被 Group 内 一个 consumer 消费,但一个 consumer 可以消费多个 partition。
    • 核心目的:水平扩展 + 广播
      ① 同一个 Group:多 consumer 分摊消息(负载均衡)。
      ② 不同 Group:每个 Group 都能拿到全量消息(广播)。
    • 新增/减少 consumer 时会触发 Rebalance(重新分配 partition)。

    我们项目:Flink 是一个 group 消费所有数据;告警服务是另一个 group 也消费所有数据,互不影响。

    为什么这么回答

    Group 是 Kafka 设计的精髓。考察你是否真正理解 Kafka 的消费模型(和 MQ 的"队列模式 vs 发布订阅模式"异曲同工)。

    面试官会怎么追问
    • "partition 数少于 consumer 数会怎样?" → 多余的 consumer 会被闲置(rebalance 后)。所以 partition 数 ≥ 期望最大并发数。
    • "Rebalance 期间会怎样?" → 整个 group 短暂停止消费(STW),所以要避免频繁 rebalance(比如 consumer 频繁挂掉)。
    • "Flink 的 Kafka Source 和普通 Consumer 区别?" → Flink 用自己的 Source 函数管理 offset 到外部存储(不是 Kafka 内置),支持精确一次。
    怎么回答能拿高分
    画一张图:3 个 partition、2 个 consumer、1 个 group 内的分配关系。再补一句:"一个 Group 是'队列模式',多个 Group 是'发布订阅',Kafka 一套机制同时实现了两种模式。"
    Q20 Offset 是什么? ★★★★☆ +
    标准答案(2 分钟)
    • Offset 是消息在 partition 内的位置编号(从 0 开始),单调递增。
    • 每条消息进入 partition 时,broker 给它分配一个 offset,消费者通过 offset 标记"我读到哪里了"。
    • 提交 offset = 告诉 Kafka"我处理到这了"。重启后从提交的 offset 继续。
    • Offset 存储在 __consumer_offsets 内部 topic 里(默认 50 个 partition,副本数 3)。

    三个关键点:

    • 提交方式:自动(按时间间隔,默认 5s)或 手动commitSync() / commitAsync())。
    • 重置策略:auto.offset.reset=earliest|latest,新 group 没提交过 offset 时怎么选起点。
    • Flink 的特殊性:Flink 把 offset 存到自己的状态后端(rocksdb/hdfs),不依赖 Kafka 内部存储,更可靠
    为什么这么回答

    Offset 是 Kafka 消费的"游标",理解它才能理解 Kafka 怎么实现"不丢不重"。

    面试官会怎么追问
    • "自动提交有什么坑?" → 假设提交周期 5s,consumer 处理了 10s 的数据;5s 时自动提交了,consumer 挂了;重启后从 5s 处开始,丢失中间未处理的数据。
    • "commitSync 和 commitAsync 区别?" → sync 同步等 broker 响应,失败会重试;async 异步,可能丢 commit 但性能好。一般 sync + 失败重试。
    • "Flink 的 offset 存哪里?" → Checkpoint 里。Flink 把 Kafka offset + 业务状态一起 snapshot 到外部存储,原子提交。
    怎么回答能拿高分
    讲一个具体数字:"我们设 enable.auto.commit=false,每批处理完手动 commitSync,宁可慢一点也不丢消息。"
    Q21 如何保证消息顺序? ★★★★☆ +
    标准答案(2 分钟)

    Kafka 的顺序保证是分区级别的,不是全局有序:

    • 一个 partition 内严格有序(offset 单调递增)。
    • 不同 partition 之间无序。

    想保证"某类业务有序",三种方案:

    • 单 partition:把需要有序的消息全部发到同一个 partition(key 哈希到同一 partition)。
    • 业务键路由:相同业务键的消息用同一 producer 同一 partition(key=deviceId)。
    • 消费侧排序:consumer 收到一批后按业务字段排序再处理,代价是延迟。

    我们项目:deviceId 作为 Kafka key,保证同一设备消息严格有序。

    为什么这么回答

    考察"你对分布式系统顺序性的理解"。常见错误是"以为 Kafka 全局有序",需要纠正。

    面试官会怎么追问
    • "所有消息都进一个 partition 行不行?" → 顺序是保证了,但失去了并行性。10w QPS 一个 partition 撑不住。
    • "如果某个 partition 挂了,新 partition 怎么保证顺序?" → Kafka 0.11+ 用 producer 幂等性 + 单 partition 副本顺序提交,保证不乱。
    • "想全局有序怎么办?" → 几乎不可能。必须的话用单 partition + 单 consumer,但牺牲吞吐。
    怎么回答能拿高分
    补一句工程经验:"我们用 deviceId 作 key 后,单 partition 顺序消费,Flink 侧无需重排序,性能最优。"
    Q22 Kafka 如何削峰? ★★★☆☆ +
    标准答案(2 分钟)

    Kafka 削峰的原理是"以时间换空间"

    • 上游突发 10w QPS 时,Kafka 把消息全部接住,写入磁盘(顺序写很快)。
    • 下游消费者按自己的能力(比如 2w QPS)慢慢拉,永远不被打挂
    • 磁盘的"窗口期"取决于 retention:默认 7 天,最多撑 7 天的积压。

    实战配置:

    • Broker 磁盘:高峰期日均增量的 3-5 倍容量。
    • Partition 数:≥ 期望消费并发数。
    • 消费者:设 max.poll.records 限速 + 监控 lag。
    • 核心监控:Consumer Lag(积压量)。如果 lag 持续上涨,要么加 consumer,要么加 partition。
    为什么这么回答

    削峰是 Kafka 的"招牌功能",要讲清楚"以时间换空间"和"lag 监控"两个关键点。

    面试官会怎么追问
    • "Kafka 满了会怎样?" → Broker 拒绝生产(默认)或按策略删除旧消息(retention.ms)。
    • "下游打挂积压太多怎么办?" → 临时扩容 consumer;长期方案是扩大 partition 数 + 优化下游吞吐。
    • "Kafka 自身会丢消息兜底吗?" → 不会。Kafka 只承诺"在 retention 内不丢",超过 retention 一律按策略清理。
    怎么回答能拿高分
    提一个反例:"没用 Kafka 之前,我们用直连 MQTT 的方案,2022 年大促时设备 5 倍峰值涌入,告警服务直接打挂,雪崩 30 分钟;上 Kafka 之后再没出过。"
    Q23 为什么不用 RabbitMQ? ★★★☆☆ +
    标准答案(2 分钟)

    核心区别:Kafka 是"日志型",RabbitMQ 是"消息队列型"

    • 吞吐量:Kafka 单 partition 10w+ QPS,RabbitMQ 单队列万级。Kafka 强 10 倍。
    • 消息留存:Kafka 默认保留 7 天(按时间/大小),可重放历史;RabbitMQ 消费完就删除,不能重放。
    • 顺序保证:Kafka partition 内严格有序;RabbitMQ 多 consumer 并发消费时不保证全局顺序。
    • 协议:Kafka 自有协议;RabbitMQ 支持 AMQP、STOMP、MQTT 等多协议。
    • 运维:Kafka 集群基于 ZK/KRaft;RabbitMQ 集群基于 Erlang 节点,运维相对简单。

    选型逻辑:

    • IoT / 大数据 / 日志:Kafka(高吞吐、可重放)。
    • 业务消息(订单、支付):RabbitMQ(路由灵活、延迟低)。

    我们项目 IoT 数据走 Kafka,业务支付走 RabbitMQ(你一卡通项目用的)。

    为什么这么回答

    这是"中间件选型"类问题,展示你对不同 MQ 的差异化理解。注意:你简历上两个项目分别用了两个 MQ,答出"按场景用"是加分项。

    面试官会怎么追问
    • "RocketMQ 呢?" → 阿里出品,国内大厂用得多;架构像 Kafka,但事务消息更强(适合金融)。
    • "物联网场景为什么不用 Pulsar?" → Pulsar 架构先进(存算分离),但运维复杂、对中小团队不友好;Kafka 生态成熟、人才多。
    • "Kafka 太重怎么办?" → 单机版用 Bitnami Kafka 镜像 5 分钟启动;测试环境完全够用。
    怎么回答能拿高分
    补一句你简历上的实际经验:"我之前一卡通项目用 RabbitMQ 做支付消息(业务消息),现在 IoT 项目用 Kafka 做设备数据(流量消息),两个项目选型完全不同。"
    Q24 Kafka 的 Rebalance 是什么?如何避免? ★★★☆☆ +
    标准答案(2 分钟)

    Rebalance 是 Consumer Group 内重新分配 partition的过程,触发时机:

    • Group 内 consumer 数量变化(新增/减少)。
    • 订阅的 topic 数量变化。
    • Broker 数量变化(不常见)。
    • consumer 超过 session.timeout.ms(默认 10s)没发心跳。

    Rebalance 期间整个 group 停止消费(STW),对在线业务影响很大。

    避免频繁 Rebalance:

    • 调大 session.timeout.ms(如 30s)和 max.poll.interval.ms(如 5min)。
    • 减少 consumer 数量变动(避免频繁扩缩容)。
    • Cooperative Rebalance(Kafka 2.4+ 增量协议)替代 Eager Rebalance,减少 STW 时间。
    • 静态成员group.instance.id)让重启的 consumer 复用旧 ID,避免触发 rebalance。
    为什么这么回答

    Rebalance 是 Kafka 消费侧的"老大难"。真实生产中 80% 的"消费慢"问题都和 rebalance 有关,懂这个就说明踩过坑。

    面试官会怎么追问
    • "怎么知道发生了 Rebalance?" → Kafka 客户端日志会打印;JMX 指标 rebalance-rate
    • "Rebalance 时未提交 offset 的消息会丢吗?" → 不会,未提交的 offset 下次重新分配后还会被读。
    • "Flink 为什么基本不受 Rebalance 影响?" → Flink 不依赖 Kafka 的 group 机制,自己管理 offset 和并行度。
    怎么回答能拿高分
    讲一个具体场景:"我们 2023 年遇到过消费延迟突增,排查发现是 consumer GC 停顿 12s,超过了 session.timeout,频繁 rebalance。调大 timeout + 优化 GC 后消失。"

    第三章 Flink 7 题

    你简历核心:45+ Flink 算子、毫秒级实时处理、单 Job 多 DAG 并行、DAG 热更新、GraalVM JS 沙箱。这是你最深的护城河。

    Q25 为什么使用 Flink? ★★★★★ +
    标准答案(2 分钟)

    Flink 在工业实时数据场景的核心优势:

    • 真正的流处理:Flink 是"流是基础,批是特例",每条数据来就处理;Spark Streaming 是微批(按时间窗口攒批),延迟秒级。
    • 事件时间(Event Time):支持按数据本身的时间戳处理,能处理乱序、迟到数据(WaterMark 机制)。
    • 状态管理:内置 RocksDB 状态后端,TB 级状态可存盘,Checkpoint 自动容错。
    • Exactly-Once 语义:两阶段提交 + 分布式快照,理论保证数据不丢不重。
    • DAG 灵活性:可以动态构建算子拓扑,我们用 Flink DAG 跑用户自定义的数据处理逻辑,这是最关键的卖点。

    我们项目 5000+ 测点、毫秒级延迟、复杂聚合计算,Flink 是唯一合适的选择

    为什么这么回答

    这是 Flink 面试的开篇,必答"流批一体"和"事件时间"。但你简历的最大亮点是"自定义 DAG",这才是真正区分你和别人的点。

    面试官会怎么追问
    • "Flink 的 Watermark 怎么用?" → 按业务允许的最大乱序程度设 allowedLateness,超过的数据进侧输出流。
    • "Exactly-Once 在 sink 端怎么保证?" → 幂等 sink(如 Upsert Kafka / JDBC upsert),或两阶段提交 sink(2PC)。
    • "Flink 状态太大怎么办?" → RocksDB 状态后端 + 增量 Checkpoint + 状态 TTL 清理。
    怎么回答能拿高分
    说一个数字:"我们 Flink 跑 5000 测点 × 1Hz,P99 延迟 80ms,状态量 5GB(设备历史数据缓存),Exactly-Once 0 丢失。"真实数据最打动人。
    Q26 为什么不用 Spark? ★★★★☆ +
    标准答案(2 分钟)

    核心区别:Spark 是批处理思维,Flink 是流处理思维

    • 延迟:Spark Streaming 最小批次 100ms 起;Fink 可达毫秒级甚至亚毫秒级。
    • 事件时间:Spark Structured Streaming 1.x 才原生支持事件时间;Flink 从一开始就为事件时间设计。
    • 状态:Flink 状态一等公民,每个算子都能用;Spark 状态用外部存储(Redis/HBase)多一次网络。
    • 动态 DAG:Fink 可动态调整算子拓扑(我们项目核心需求);Spark 一旦提交就固定。
    • 生态:Spark SQL / MLlib 强于 Flink;Flink 在流计算和 IoT 领域强。

    选型逻辑:实时性要求高 + 自定义计算逻辑复杂 → Flink;离线报表 + 机器学习 → Spark。

    我们项目是工业实时数据,Spark 不合适

    为什么这么回答

    这是"大数据选型"经典问题。不要踩 Spark 抬 Flink,要客观对比,让面试官觉得你懂生态。

    面试官会怎么追问
    • "Spark 3.0 持续流模式呢?" → Structured Streaming 的 continuous processing 模式能做到毫秒级,但生态不成熟,bug 多。
    • "两个都学吗?" → 大数据领域两个都得懂,建议先 Flink 入门,Spark 做离线。
    • "Flink 难学在哪?" → 状态、watermark、checkpoint、容错四件套,理解门槛高;Spark 沿用 Hadoop 思维,入门快。
    怎么回答能拿高分
    补一句:"我们数仓离线报表用 Spark(晚上跑批),实时监控用 Flink(白天跑流),两个并存。"体现工程师的工程判断
    Q27 你们为什么只有一个 Job? ★★★★★ +
    标准答案(2 分钟)

    传统 Flink 架构:一个业务一个 Job,管理复杂,资源浪费。 我们做了单 Job 多 DAG设计:

    • 一个 Flink Job里维护多个 DAG(用户自定义的计算流程),通过广播流动态加载/卸载。
    • 每个 DAG 有自己的数据源(Kafka 不同 topic)+ 算子链 + 输出 sink。
    • Job 永不重启,DAG 变更通过"取消旧 DAG → 加载新 DAG"实现,实现零停机
    • Job 内多个 DAG 共享 TaskManager 资源,资源利用率 3 倍提升

    代价:

    • 自定义算子注册机制复杂。
    • DAG 隔离差,一个 DAG 故障可能影响其他 DAG(用 SlotSharingGroup 隔离)。
    • 开发周期长(3 个月)。

    但收益:支持用户拖拽式配置计算流程,业务方不再等开发

    为什么这么回答

    这是你简历的核心亮点,必须讲透。面试官从这个问题判断你到底"用过 Flink"还是"用过 Flink 的 Hello World"。

    面试官会怎么追问
    • "怎么隔离不同 DAG 的资源?" → SlotSharingGroup,让不同 DAG 在不同 slot 跑。
    • "DAG 状态怎么恢复?" → 每个 DAG 的状态单独存 KeyedState,Checkpoint 时按 DAG ID 区分。
    • "用户改 DAG 不会出 bug 吗?" → 有预校验(拓扑结构、算子兼容性、Schema 校验),灰度发布。
    怎么回答能拿高分
    画一张架构图:单个 JobManager 下,多个 DAG 共享 TaskManager,每个 DAG 独立输入输出。强调"用户从拖拽到上线 5 分钟"这个核心收益。
    Q28 DAG 怎么解析? ★★★★★ +
    标准答案(2 分钟)

    用户在前端用X6 流程图画 DAG,后端解析成可执行 Flink 算子链。流程:

    • JSON 结构:前端存 DAG 为 JSON,节点带 type / config / id,边带 from / to。
    • 校验
      ① 连通性(无环,所有节点可达)。
      ② 类型兼容(输出类型 = 下游输入类型)。
      ③ 算子白名单(禁用危险算子)。
    • 拓扑排序:DFS / Kahn 算法生成执行顺序(source → 算子 → sink)。
    • 算子映射:把每个节点映射到具体的 Flink 算子类(Function),用反射或工厂模式创建。
    • 链路组装:用 stream.transform()ProcessFunction 把算子串成 DataStream 链。

    运行时:Flink 通过 BroadcastStream 广播 DAG 定义,TaskManager 按定义动态构建算子链。

    为什么这么回答

    这是你项目最技术的部分。从 JSON 到 Flink 算子的完整链路,展示你的全栈能力。

    面试官会怎么追问
    • "JSON 怎么定义算子参数?" → 节点 config 字段 + 算子类的 @ConfigField 注解,反射注入。
    • "用户写错了怎么办?" → 预校验 + 运行时熔断(异常进告警 topic,DAG 自动禁用)。
    • "支持多少种算子?" → 45+ 种(数据源、转换、过滤、聚合、窗口、用户自定义 JS)。
    怎么回答能拿高分
    讲一个细节:"我们做了沙箱化,用户的 JS 脚本通过 GraalVM polyglot 执行,不能访问 JVM 内部 API,防止恶意代码拖垮 JobManager。"这是亮点。
    Q29 如何动态更新 DAG? ★★★★★ +
    标准答案(2 分钟)

    三种方案对比:

    • 方案 1(不可取):重启 Flink Job → 数据中断 30s+,业务不可接受。
    • 方案 2:Savepoint + 重启 → 类似方案 1,只是恢复时间短一些。
    • 方案 3(我们用的):Flink Broadcast State + 动态算子
      ① 把 DAG 定义放到 Kafka 广播流。
      ② Job 里所有算子订阅广播流,收到新 DAG 时在算子内重新组织处理逻辑。
      ③ 用 KeyedBroadcastProcessFunction 实现"广播状态 + 业务状态"双流处理。

    实际实现:

    • 每个 DAG 节点映射到一个 算子模板(自定义的 AbstractDagFunction)。
    • 收到新 DAG 时,把算子模板按拓扑组装到数据流处理函数里。
    • 状态保留:KeyedState 不清空,历史数据无缝衔接
    为什么这么回答

    这是你简历最值钱的点:"DAG 热更新"。答出 Broadcast State 立刻高级,答 Savepoint 只能是中级。

    面试官会怎么追问
    • "状态怎么迁移?" → 算子结构变化时,新算子继承老算子的 KeyedState(按 key 共享)。
    • "会不会有数据丢失?" → 不会,广播流和业务流走 Checkpoint 统一快照,原子提交。
    • "算子代码改了能热更新吗?" → 不能热更代码(Java 类不能动态加载),只能热更配置和拓扑结构。代码改要重启 Job。
    怎么回答能拿高分
    提一个数字:"我们支持 200+ 用户同时在线编辑 DAG,发布延迟 < 3 秒,零停机。上线 1 年从没因为 DAG 更新导致数据中断。"
    Q30 为什么不用 Window? ★★★☆☆ +
    标准答案(2 分钟)

    先说 Flink Window 是干嘛的:按时间或数量切片聚合(滚动、滑动、会话窗口)。

    为什么我们大部分场景不用:

    • 测点数据是连续的,每条数据来了就要算"过去 5 分钟均值"这种滑动指标。
    • Window 是窗口结束时才触发计算,延迟 = 窗口长度。我们要求毫秒级响应。
    • 改用ProcessFunction + 状态
      ① 维护设备最近 5 分钟的所有数据(ListState)。
      ② 每来一条新数据,更新状态 + 重新计算指标。
      ③ 延迟 = 1 条数据处理时间(毫秒级)。

    但 Window 在"离线统计报表"场景还是会用:TumblingEventTimeWindow 按小时/天聚合。

    为什么这么回答

    考察"你是否真正理解 Flink Window 的代价"。"为什么不用 Window"是反面问题,展示你深度思考过。

    面试官会怎么追问
    • "不用 Window 怎么聚合?" → 自定义 Trigger + 状态;本质上和 Window 等价但更灵活。
    • "状态会无限膨胀吗?" → 不会,状态有 TTL(Time-To-Live),过期自动清理。
    • "Flink 1.13+ 的窗口新特性?" → 增量聚合 + 状态保留,减少重复计算。
    怎么回答能拿高分
    补一句取舍:"Window 简单但延迟高,Process 灵活但状态管理复杂。我们用 Process 为主,Window 报表场景用,两者不冲突。"
    Q31 Flink 的 Checkpoint 原理? ★★★☆☆ +
    标准答案(2 分钟)

    Checkpoint 是 Flink 的容错机制,核心是 Chandy-Lamport 分布式快照算法

    • 触发:JobManager 周期性地向 Source 算子发 Barrier(特殊标记)。
    • 快照:Barrier 沿数据流向下游传播,算子收到 Barrier 后把当前状态(State)异步写到外部存储(HDFS / RocksDB)。
    • 对齐:多输入算子要等所有 Barrier 都到才触发快照(保证全局一致);Exactly-Once 模式必须对齐,At-Least-Once 模式可跳过对齐。
    • 恢复:Job 失败重启时,从最近一次完整 Checkpoint 恢复所有算子状态 + Kafka offset 偏移。
    为什么这么回答

    Checkpoint 是 Flink "四大核心"(事件时间、状态、Checkpoint、watermark)之一。必答 Barrier 概念。

    面试官会怎么追问
    • "Checkpoint 和 Savepoint 区别?" → Checkpoint 自动 + 内部用;Savepoint 手动 + 升级/迁移用。
    • "Barrier 对齐会卡住吗?" → 会,所以有 At-Least-Once 模式跳过对齐(牺牲精确一次换吞吐)。
    • "状态多大合适?" → 推荐 1GB 以内,状态太大 Checkpoint 慢;超过 10GB 建议拆分或聚合后再入状态。
    怎么回答能拿高分
    提一个实战参数:"我们设 Checkpoint 间隔 60s,超时 5min,状态后端 RocksDB+增量,5GB 状态 30s 完成。"
    02

    Java 基础(40 题)

    ★★★★★
    你的弱项 · 90% 必问 · 必须流畅

    第四章 Redis 10 题

    送分题,必须全部答对。简历里你用 Redis 做了大量缓存/限流/分布式锁,这是验证基础的最佳机会。

    Q32 Redis 为什么快? ★★★★★ +
    标准答案(2 分钟)

    Redis 官方压测:10w+ QPS。核心是 3 个"基于内存"的设计:

    • 纯内存操作:数据都在内存里,读写都是纳秒级;MySQL 走磁盘 + 索引,至少毫秒级。
    • 单线程 + IO 多路复用:避免多线程上下文切换和锁竞争;用一个线程 + epoll 处理上万个连接(详见 Q33)。
    • 高效数据结构
      ① String 用 SDS(动态字符串)。
      ② Hash 用 ziplist(小数据)/ hashtable(大数据)。
      ③ Sorted Set 用跳表(O(logN))。
      ④ Geo 用 geohash 编码。
    为什么这么回答

    这是 Redis 第一必问题,90% 的面试从这开始。三个点都要答到。

    面试官会怎么追问
    • "内存比磁盘快多少?" → 随机访问差 10w 倍;顺序访问差 100-1000 倍。
    • "跳表和红黑树比呢?" → 跳表实现简单、范围查询 O(logN),Redis 作者 antirez 觉得更好理解。Java 用红黑树(TreeMap)。
    • "Redis 6 之后还是单线程吗?" → 命令执行还是单线程;网络 IO 用多线程(详见 Q34)。
    怎么回答能拿高分
    加一个数据:"我们用 Redis 缓存设备配置,10w+ QPS,单机 CPU 才到 30%。"真实数据最打动人。
    Q33 Redis 为什么采用单线程? ★★★★★ +
    标准答案(2 分钟)

    这里"单线程"指命令执行是单线程(Redis 6 之前),原因:

    • CPU 不是瓶颈:Redis 瓶颈在内存和网络 IO,不在 CPU。多线程对 CPU 密集型才有收益。
    • 避免锁竞争:Redis 数据结构(Hash/Set)大量用单线程操作,多线程要加锁,性能反而下降。
    • 实现简单:单线程无需考虑并发问题,代码清晰。
    • IO 多路复用:用 epoll(Linux)/ kqueue(BSD)单线程处理上万个 socket,性能不输多线程。

    补充:Redis 4.0 引入部分多线程(异步删除大 Key),Redis 6.0 才正式把网络 IO 多线程化。

    为什么这么回答

    考察"对单线程的真正理解"。很多人只说"快",不知道单线程反而是 Redis 的设计选择

    面试官会怎么追问
    • "单线程下慢查询会阻塞所有请求吗?" → 会!这是 Redis 最大坑。所以严禁在 Redis 里跑 KEYS *、大 Hash 一次性 HGETALL。
    • "IO 多路复用是什么?" → 一个线程同时监听多个 socket,哪个有数据就读哪个。epoll 是 Linux 最高效的实现。
    • "Memcached 也是单线程吗?" → 是的,但 Memcached 早就支持多线程了。Redis 因为数据结构复杂,加多线程代价大。
    怎么回答能拿高分
    提一个反例:"我们曾经有个业务在 Redis 里做 HGETALL 拉 1w 个字段,单请求 200ms,把整个 Redis 阻塞了 1 分钟。改成 HSCAN 迭代 + 拆分 Key 后恢复正常。"
    Q34 Redis 6 为什么又加入多线程? ★★★★☆ +
    标准答案(2 分钟)

    Redis 6 的多线程只用于网络 IO(读写 socket),命令执行仍然是单线程。原因:

    • Redis 性能瓶颈随着硬件升级,从"内存+CPU"变成"网络 IO":高并发下网络读写占 CPU 60%+。
    • 多线程 IO 把"读请求 → 解析 → 命令执行 → 响应"里的读请求解析和响应写回做成多线程。
    • 命令执行还是单线程,保证原子性和数据结构简单

    配置:io-threads 4(一般 4-8 即可,开太多反而慢)。

    为什么这么回答

    这题考察"对 Redis 6 新特性的理解"。要明确"多线程 IO 不等于命令多线程"。

    面试官会怎么追问
    • "默认开启吗?" → 不开启,io-threads-do-reads yes + 线程数 > 1 才生效。
    • "为什么不用多线程执行命令?" → 数据结构太复杂(跳表、字典、压缩列表),加锁代价 > 收益。
    • "单机 10w QPS 单线程够吗?" → 够,瓶颈通常在客户端连接数,不是 CPU。
    怎么回答能拿高分
    补一句:"我们生产 Redis 没开多线程 IO,因为单实例 QPS 没超过 5w;要是到 10w+ 才考虑开。"
    Q35 Redis 有哪些数据类型? ★★★★★ +
    标准答案(2 分钟)

    Redis 五大基础 + 三大特殊:

    • String:最基本,SET/GET/MSET/MGET。适用:缓存、计数器、分布式锁(SETNX)。
    • Hash:field-value 集合,HSET/HGET/HGETALL。适用:对象存储(用户信息)、购物车。
    • List:双向链表,LPUSH/RPOP。适用:消息队列、最新列表(关注列表)。
    • Set:无序集合,SADD/SMEMBERS。适用:标签、共同好友、UV 统计。
    • Sorted Set (ZSet):有序集合,跳表实现,ZADD/ZRANGE。适用:排行榜、延迟队列。

    三种特殊类型:

    • Bitmap:位图,SETBIT/GETBIT。适用:用户签到、日活统计。
    • HyperLogLog:基数统计,PFADD/PFCOUNT。适用:UV 去重(12KB 统计 2^64 个数)。
    • GEO:地理位置,GEOADD/GEODIST。适用:附近的人。

    Redis 5+ 新增:Stream(消息队列,类 Kafka)。

    为什么这么回答

    基础题,但很少人能答全 8 种。尤其 Bitmap/HyperLogLog/GEO 是加分项。

    面试官会怎么追问
    • "Stream 和 Kafka 区别?" → Redis Stream 是轻量级,持久化和消费组都支持,但吞吐比不上 Kafka(10w+ vs 百万级)。
    • "Hash 和 String 存对象哪个好?" → Hash 省内存(field 按需序列化)、可部分更新;String 简单、可整体 SETEX 过期。
    • "Sorted Set 跳表原理?" → 多层链表,每层随机选 50% 节点向上层;查询从顶层往下 O(logN)。
    怎么回答能拿高分
    用你的实际项目举例:"我们设备配置用 Hash(按 field 字段更新),设备在线列表用 Set,告警排行榜用 ZSet。"
    Q36 什么是缓存穿透?怎么解决? ★★★★★ +
    标准答案(2 分钟)

    定义:查询一个数据库里也不存在的数据,缓存永远没命中,请求全打到 DB。

    常见场景:恶意攻击(请求 /user/-1 一定不存在)、查询无效 ID。

    三种解决方案

    • 空值缓存:DB 返回空时,Redis 缓存 (key, null, EX 5min)。简单但占内存。
    • 布隆过滤器查询前先过 BloomFilter,不存在的直接返回。命中后查 DB。推荐方案
    • 参数校验:业务层做 ID 合法性校验(>= 1、长度限制),从源头拦截。
    为什么这么回答

    Redis 经典三大问题(穿透/击穿/雪崩)的第一题。答出"BloomFilter"就是中高级水平。

    面试官会怎么追问
    • "布隆过滤器误判怎么办?" → 布隆过滤器是"可能存在"语义,DB 层做最终校验;不能完全依赖布隆。
    • "空值缓存的 key 怎么管理?" → 设短 TTL(5min),定期清理;防止恶意 key 灌爆 Redis。
    • "布隆过滤器怎么实现?" → Redis 自带 BF.ADD / BF.EXISTS(RedisBloom 模块),或者用 Guava / Redisson 客户端。
    怎么回答能拿高分
    画一张请求流程图:请求 → 布隆过滤器 → 不存在直接返回 / 存在 → 查 Redis → 查 DB → 回填缓存。视觉化最清晰。
    Q37 什么是缓存击穿?怎么解决? ★★★★★ +
    标准答案(2 分钟)

    定义:某个热点 key 过期瞬间,大量请求同时打到 DB(雪崩是大量 key 同时过期,击穿是单 key)。

    四种解决方案

    • 永不过期:逻辑过期(key 永不过期,value 里带 expireTime),后台异步刷新。简单但代码复杂。
    • 分布式锁第一个请求拿到锁查 DB,其他请求等待。最常用,强一致场景必用
    • 提前预热:系统启动时把热点 key 加载到缓存,运营手动刷新。
    • 二级缓存:本地缓存(Caffeine)+ Redis 兜底,热点 key 永远在 JVM 内存里。
    为什么这么回答

    区分"击穿"vs"雪崩"是关键:击穿是 1 个 key,雪崩是 N 个 key 同时过期。要答出分布式锁方案。

    面试官会怎么追问
    • "分布式锁怎么做?" → SET key value NX EX 10(NX = 互斥、EX = 过期防死锁);解锁用 Lua 脚本保证原子性。
    • "等锁的请求会阻塞吗?" → 会,业务侧用 tryLock + 超时;超时就降级返回旧值或默认值。
    • "用 Redisson 行不行?" → 行,Redisson 封装好 tryLock / lock / 看门狗续期,生产首选。
    怎么回答能拿高分
    讲一个细节:"我们用 Redisson 分布式锁,看门狗自动续期,业务执行 30s 也不会释放锁。防锁过期业务还在跑。"
    Q38 什么是缓存雪崩?怎么解决? ★★★★★ +
    标准答案(2 分钟)

    定义大量 key 同时过期,请求全打到 DB,DB 挂掉。常见原因:批量 key 设了相同过期时间。

    四种解决方案

    • 过期时间打散EXPIRE = baseTime + random(0, 300s),避免同时过期。
    • 多级缓存:本地缓存(Caffeine)兜底,Redis 挂掉也能用。
    • 熔断降级:Redis 不可用时返回默认值或静态页面。
    • Redis 高可用:Cluster 集群,少数节点挂掉不影响整体。
    为什么这么回答

    三件套(穿透/击穿/雪崩)必答全。和击穿区别要清晰。

    面试官会怎么追问
    • "和缓存击穿的区别?" → 击穿是1 个热点 key;雪崩是大量 key 同时过期
    • "Redis Cluster 挂了整个集群都不可用吗?" → 部分节点不可用,其他节点继续服务,客户端自动重试。
    • "熔断降级怎么实现?" → Sentinel / Resilience4j;Redis 异常时降级返回空值或默认值。
    怎么回答能拿高分
    提一个反例:"我们 2022 年出过雪崩事故:批量导入 100w 数据,所有 key 设 1h 过期,结果 1h 后全过期,DB CPU 100% 持续 5min。改成 baseTime + 随机 5min 后再没出过。"
    Q39 什么是 BigKey?怎么处理? ★★★★☆ +
    标准答案(2 分钟)

    定义:单个 key 对应的 value 过大。一般:

    • String > 10KB
    • Hash / List / Set / ZSet 元素数 > 1w

    危害

    • 阻塞:Redis 单线程,操作 BigKey 慢查询会阻塞整个实例
    • 网络:传输大 value 占用带宽,QPS 下降。
    • 删除:DEL 大 Key 是阻塞操作,UNLINK(异步删)才不阻塞。
    • 内存倾斜:Cluster 模式下大 Key 集中在某 slot,节点内存不均。

    解决方案

    • 设计阶段:避免大 Key(拆分 hash tag、用 Hash 存对象而非 String)。
    • 发现阶段:redis-cli --bigkeysMEMORY USAGE key
    • 删除阶段:用 UNLINKSCAN + DEL 分批删。
    为什么这么回答

    BigKey 是 Redis 性能问题的"头号杀手"。必答单线程阻塞风险

    面试官会怎么追问
    • "怎么发现 BigKey?" → redis-cli --bigkeys 扫一遍;生产用监控(redis_exporter)告警单 Key > 1MB。
    • "大 Key 怎么拆?" → 把一个 String 拆成多个 Hash field;List 按 ID 范围拆成多个 Key。
    • "DEL 和 UNLINK 区别?" → DEL 同步阻塞,UNLINK 异步删除(Redis 4.0+)。
    怎么回答能拿高分
    补一个细节:"我们 RDB 持久化时 fork 进程,BigKey 多会导致 COW 复制内存翻倍,这就是为什么生产禁止 BigKey。"
    Q40 什么是 HotKey?怎么处理? ★★★★☆ +
    标准答案(2 分钟)

    定义:某个 Key 被高频访问,导致 Redis 单节点压力过大(虽然不是 BigKey)。常见:明星离婚新闻、秒杀商品、热点微博。

    危害

    • 单节点 QPS 集中,集群模式下某一节点成为瓶颈
    • 带宽打满,下行拥塞。

    解决方案

    • 本地缓存:JVM 内存缓存热点 Key(Caffeine),读本地不读 Redis。最常用。
    • Key 分散product:detail:1 拆成 product:detail:1:1 ~ 1:8,8 个 Key 分散到不同 slot。
    • 读主从分摊:客户端连主从,从节点也接受读请求。
    • Redis 6 多 IO 线程:缓解网络瓶颈。
    为什么这么回答

    HotKey 容易被忽略,和 BigKey 是不同维度的"高危 Key"。和 Q39 一起记忆。

    面试官会怎么追问
    • "怎么发现 HotKey?" → redis-cli --hotkeys(需要 maxmemory-policy=allkeys-lfu);生产用 monitor 采样 + 统计。
    • "本地缓存一致性怎么保证?" → 设短 TTL(10s)+ 主动失效(MQ 通知)。不能完全保证最终一致,但性能收益巨大。
    • "Key 分散怎么实现?" → 业务层做一致性 hash;或者用 proxy(Twemproxy / Codis)自动打散。
    怎么回答能拿高分
    讲一个秒杀场景:"我们双 11 秒杀商品详情用本地 Caffeine 缓存,TTL 5s,扛住了 5w QPS,Redis QPS 只到 8k。"
    Q41 Redis 持久化机制有哪些? ★★★★☆ +
    标准答案(2 分钟)

    Redis 两种持久化方式:

    • RDB(Redis Database)
      ① 周期性地把内存数据快照到 dump.rdb 文件。
      ② 触发方式:save(阻塞)、bgsave(fork 子进程)。
      ③ 优点:文件小、恢复快;缺点:可能丢最后一次快照后的数据。
    • AOF(Append Only File)
      ① 每条写命令追加到 AOF 文件。
      ② 三种刷盘策略:always(每条)、everysec(每秒,默认)、no(OS 控制)。
      ③ 优点:最多丢 1s 数据;缺点:文件大,恢复慢。
    • 混合模式(Redis 4.0+)
      RDB 做全量快照 + AOF 记录增量,既快又可靠,生产推荐。
    为什么这么回答

    必答基础。混合模式是 4.0+ 的新方案,体现你跟踪社区。

    面试官会怎么追问
    • "生产怎么选?" → AOF everysec + RDB bgsave 混合,最多丢 1s 数据,性能损失 < 5%
    • "AOF 文件越来越大怎么办?" → AOF 重写(bgrewriteaof),合并重复命令,保留最新版本。
    • "bgsave 的 fork 阻塞怎么解决?" → 减少内存占用、关闭大 Key;Linux 改用 PFORK 优化(Redis 6+)。
    怎么回答能拿高分
    补一句取舍:"纯 RDB 适合做冷备,纯 AOF 适合高可靠;我们生产用混合,备份还是 RDB(文件小好归档),运行用 AOF(防丢)。"

    第五章 MySQL 10 题

    数据库是 90% 面试必问,重点准备索引、事务、日志。

    Q42 B+Tree 为什么快? ★★★★★ +
    标准答案(2 分钟)

    MySQL InnoDB 用 B+Tree 做索引,查询复杂度 O(logN),3-4 层就能存 2000w 数据:

    • 矮胖树:非叶子节点只存索引(不存数据),单节点能容纳更多 key,树高度降到 3-4 层
    • 叶子节点链表:所有叶子节点用双向链表串起来,范围查询不用回溯,直接遍历。
    • 顺序读写:叶子节点物理上按主键顺序存储,磁盘预读(page)效率高
    • 每次 IO 读一页(16KB):单次磁盘 IO 能加载大量索引项。
    为什么这么回答

    这是 MySQL 索引的"灵魂问题",必答"矮胖"和"叶子节点链表"两点。

    面试官会怎么追问
    • "B+Tree 一般多少层?" → InnoDB 一页 16KB,单 key + 指针约 14 字节,单页能放 1000+ key;3 层 ≈ 10 亿数据。
    • "为什么不用红黑树?" → 红黑树是二叉树,100w 数据要 20 层,每次一层就是一次 IO,慢 10 倍。
    • "为什么不用 Hash?" → Hash 等值查询 O(1) 但不支持范围查询和排序
    怎么回答能拿高分
    算一道数:"InnoDB 聚簇索引 3 层高度能存 10 亿+ 数据,任意查询只 3 次磁盘 IO,毫秒级返回。"数字比概念更有说服力。
    Q43 为什么不用 BTree(不是 B+Tree)? ★★★★☆ +
    标准答案(2 分钟)

    BTree 和 B+Tree 的核心区别

    • BTree:非叶子节点也存数据,范围查询要中序遍历整棵树
    • B+Tree:只有叶子节点存数据,叶子节点链表串起来

    B+Tree 优势:

    • 矮胖:非叶子节点不存数据,相同 page 能装更多 key → 树更矮 → IO 更少。
    • 范围查询快:定位起点后直接沿链表遍历,不用回溯上层。
    • 查询稳定:所有查询都要走到叶子节点,时间复杂度稳定 O(logN);BTree 可能在非叶子就返回,不稳定
    为什么这么回答

    考察"对索引底层原理的精确理解"。BTree 和 B+Tree 一字之差,差很多。

    面试官会怎么追问
    • "MongoDB 用什么索引?" → BTree(不是 B+Tree),因为 MongoDB 是文档型数据库,很少做范围查询,BTree 优势反而是节省空间。
    • "B+Tree 叶子节点存什么?" → 聚簇索引存整行数据;二级索引存主键值(详见 Q48 聚簇索引)。
    • "B+Tree 插入/删除要维护平衡吗?" → 要,节点满了就分裂;删除时合并;维护代价在 1-2 层内。
    怎么回答能拿高分
    画一张对比图:左边 BTree(非叶子存数据、链表只在叶子间),右边 B+Tree(所有数据都在叶子、双向链表)。视觉化最清晰。
    Q44 什么是聚簇索引? ★★★★☆ +
    标准答案(2 分钟)

    定义索引和数据行物理上存在一起的索引就是聚簇索引。

    在 InnoDB 中:

    • 聚簇索引:表数据按主键顺序物理存储,主键索引就是聚簇索引。
    • 二级索引(非聚簇):叶子节点存的是主键值(不是行数据),查二级索引要"先拿主键 → 再查聚簇索引"。

    特点:

    • 每张表只有一个聚簇索引(因为物理顺序只有一种)。
    • 如果没有主键,InnoDB 会选一个唯一非空列,否则生成隐藏的 row_id
    • 聚簇索引查询快(一次定位到行),二级索引要"回表"(详见 Q45)。
    为什么这么回答

    聚簇 vs 非聚簇是 MySQL 索引的核心概念。和"回表"是连体问题,必答。

    面试官会怎么追问
    • "MyISAM 有聚簇索引吗?" → 没有,MyISAM 是堆表,索引和数据分离。
    • "为什么建议用自增主键?" → 自增 ID 顺序写入,避免 B+Tree 频繁分裂;UUID 随机写入会导致页分裂,性能差。
    • "主键改了会怎样?" → 物理位置要移动,代价大;所以主键最好不变。
    怎么回答能拿高分
    提一个反例:"我们早期表用 UUID 主键,写入性能只有自增主键的 1/3,B+Tree 频繁页分裂,后来全改成自增。"
    Q45 什么是回表?怎么避免? ★★★★★ +
    标准答案(2 分钟)

    定义:用二级索引查询时,先拿到主键,再回聚簇索引查全行数据,这个过程叫回表

    举例:表有 id(主键)、name、age。索引 idx_name,查 SELECT * FROM user WHERE name = '马雷'

    • 先查 idx_name → 拿到 id=100。
    • 用 id=100 回聚簇索引 → 拿整行(name, age, ...)。
    • 共 2 次 IO(最理想情况)。

    如何避免回表覆盖索引(详见 Q46)。如果查询字段都在索引里,就不需要回表。

    为什么这么回答

    回表是 MySQL 慢查询的"常见病因"。必答"避免回表 = 覆盖索引"

    面试官会怎么追问
    • "怎么知道查询有回表?" → EXPLAIN 看 Extra 列,Using index 表示覆盖索引,NULL 表示回表。
    • "回表性能损耗大吗?" → 聚簇索引 3 层高度,每次回表多 1-3 次 IO,高频查询不能忽视。
    • "能完全避免回表吗?" → 如果必须查所有字段,避免不了;用 SELECT * 才容易触发。
    怎么回答能拿高分
    讲一个真实优化:"我们把 SELECT * FROM order WHERE user_id=? 改成 SELECT id, amount FROM order WHERE user_id=?走覆盖索引,QPS 提升 3 倍。"
    Q46 什么是覆盖索引? ★★★★☆ +
    标准答案(2 分钟)

    定义:查询的字段全部在索引里,不需要回表,就叫覆盖索引(Covering Index)。

    举例:表有 id、name、age、address。建联合索引 (name, age)

    • SELECT id, name FROM user WHERE name = '马雷' → 走覆盖索引(id 是主键在叶子节点,name 在索引里)。
    • SELECT id, name, age FROM user WHERE name = '马雷' → 走覆盖索引。
    • SELECT * FROM user WHERE name = '马雷' → 需要 address → 回表

    EXPLAIN 里 Extra: Using index 表示走了覆盖索引。

    为什么这么回答

    覆盖索引是 MySQL 性能优化的高频手段。和回表(Q45)是一对

    面试官会怎么追问
    • "联合索引顺序有讲究吗?" → 遵循最左前缀原则(详见 Q47),高频字段在前。
    • "覆盖索引建太多会不会有问题?" → 索引多影响写入性能(每次 INSERT 要维护所有索引),一般 3-5 个
    • "主键索引算覆盖吗?" → 算,主键索引叶子节点有整行数据,所有查询都能覆盖。
    怎么回答能拿高分
    画一张查询路径图:SELECT 字段 ⊆ 索引字段 → 走覆盖;否则回表。视觉化最清晰。
    Q47 什么是 MVCC? ★★★★☆ +
    标准答案(2 分钟)

    MVCC(Multi-Version Concurrency Control)多版本并发控制:同一行数据保留多个版本,让读不阻塞写、写不阻塞读。

    InnoDB MVCC 实现核心:

    • 隐藏列:每行有 trx_id(最近修改的事务 ID)和 roll_pointer(指向 Undo Log)。
    • Undo Log:保存修改前的旧版本,形成版本链
    • ReadView:事务开始时拍的"快照",记录当前活跃事务 ID 列表。
    • 读已提交(RC):每次 SELECT 都生成新 ReadView。
    • 可重复读(RR):事务第一次 SELECT 时生成 ReadView,后续复用。

    判断数据可见性:trx_id < ReadView.min_id(已提交可见)或 在活跃事务列表(不可见)或 >= max_id(不可见)。

    为什么这么回答

    MVCC 是 MySQL 高并发的核心。必答 "Undo Log + ReadView"。

    面试官会怎么追问
    • "MVCC 解决了什么问题?" → 读写不冲突,读不加锁,写不阻塞读,提高并发。
    • "RR 级别能解决幻读吗?" → 快照读能解决;当前读(SELECT FOR UPDATE)需要间隙锁。
    • "Undo Log 会清吗?" → 没有事务引用时,由 purge 线程清理。
    怎么回答能拿高分
    画一张时序图:事务 A 修改某行 → 旧值进 Undo Log → 形成版本链 → 事务 B 读时通过 ReadView 找到可见版本。视觉化最清楚。
    Q48 Undo Log / Redo Log / Binlog 区别? ★★★★☆ +
    标准答案(2 分钟)

    MySQL 三种日志,作用不同:

    • Undo Log(回滚日志)
      ① 作用:事务回滚 + MVCC。
      ② 记录内容:修改前的旧值。
      ③ 位置:InnoDB 内部。
      ④ 触发:每次 UPDATE/INSERT 都会产生。
    • Redo Log(重做日志)
      ① 作用:crash recovery,保证事务持久性。
      ② 记录内容:物理修改(哪个 page 哪个字节变了)。
      ③ 位置:InnoDB 内部,循环写(固定大小)。
      ④ 触发:事务 commit 时强制刷盘(innodb_flush_log_at_trx_commit)。
    • Binlog(二进制日志)
      ① 作用:主从复制 + 数据恢复。
      ② 记录内容:逻辑日志(SQL 原文)。
      ③ 位置:Server 层,追加写
      ④ 触发:事务 commit 时写。

    两阶段提交:Redo Log 写完(prepare) → Binlog 写完 → Redo Log 标记 commit,保证两者一致。

    为什么这么回答

    这三种日志面试必问,必答两阶段提交。区分不清会让面试官怀疑你只是"用过 MySQL"而非"懂 MySQL"。

    面试官会怎么追问
    • "为什么需要两阶段提交?" → Redo 和 Binlog 各自写可能不一致(一个成功一个失败),2PC 保证原子性。
    • "Redo Log 满了怎么办?" → Checkpoint 把脏页刷盘,释放 Redo Log 空间。
    • "Binlog 有几种格式?" → STATEMENT(SQL)、ROW(行数据,默认)、MIXED(混合)。
    怎么回答能拿高分
    用一张表对比:日志/作用/记录/位置/触发条件/引擎。表格比口述更清楚,面试官最吃这种结构化回答。
    Q49 Explain 怎么用? ★★★★☆ +
    标准答案(2 分钟)

    EXPLAIN SQL 查看 MySQL 怎么执行这条 SQL,优化必备工具。关键字段:

    • id:执行顺序,id 大的先执行;id 相同从上到下。
    • select_type:查询类型,SIMPLE / PRIMARY / SUBQUERY / DERIVED。
    • type访问类型,从好到差system > const > eq_ref > ref > range > index > ALL至少要 ref,ALL 是全表扫描要优化。
    • key:实际用到的索引。
    • rows:扫描行数(估算),越小越好。
    • Extra:重要信息,
      Using index:覆盖索引,
      Using where:用 where 过滤。
      Using filesort需要优化,外部排序。
      Using temporary需要优化,用了临时表。
    为什么这么回答

    Explain 是 MySQL 优化的"金标准"工具。必答 type 字段的 7 个等级,否则就是没真正用过。

    面试官会怎么追问
    • "ALL 和 index 区别?" → ALL 全表扫描;index 走索引全扫(不是好现象,但比 ALL 好)。
    • "EXPLAIN 不准怎么办?" → 用 EXPLAIN ANALYZE(MySQL 8.0+)实际执行,给出真实数据。
    • "Using filesort 怎么优化?" → 排序字段建索引;或者把排序放在业务层(Redis Sorted Set)。
    怎么回答能拿高分
    讲一个案例:"我们有条 SQL 走 ALL,扫描 100w 行。建联合索引后 type=ref,rows=10,查询从 3s 降到 5ms。"
    Q50 MySQL 事务隔离级别? ★★★★☆ +
    标准答案(2 分钟)

    SQL 标准 4 个隔离级别,由低到高

    • 读未提交(Read Uncommitted):能读到其他事务未提交的数据,脏读
    • 读已提交(Read Committed):只读已提交的,不可重复读(同一行多次读值不同)。Oracle / PG 默认。
    • 可重复读(Repeatable Read):同一事务内多次读同一行结果一致,幻读(其他事务 INSERT 的行)。MySQL InnoDB 默认
    • 串行化(Serializable):所有读都加共享锁,性能差,很少用。

    MySQL RR 级别能解决大部分幻读(MVCC + 间隙锁),所以一般不用 Serializable。

    为什么这么回答

    事务隔离级别是基础题。必答"脏读/不可重复读/幻读"三件套。

    面试官会怎么追问
    • "脏读、不可重复读、幻读区别?" → 脏读读未提交;不可重复读单行数据变化;幻读读到了"多出来的行"。
    • "MySQL 怎么解决幻读的?" → MVCC 快照读 + 间隙锁当前读。
    • "为什么默认 RR 而不是 RC?" → RR 配合间隙锁能避免大部分并发问题,RC 在 Binlog ROW 格式下没问题,但兼容性 RR 更好。
    怎么回答能拿高分
    补一句取舍:"互联网业务高并发场景用 RC + Binlog ROW 性能更好(少一些间隙锁);金融场景必须 RR 保证一致性。"
    Q51 MySQL 慢 SQL 怎么排查? ★★★☆☆ +
    标准答案(2 分钟)

    完整排查流程:

    • 开启慢查询日志slow_query_log=ON + long_query_time=1(超 1s 记录)。
    • 分析日志mysqldumpslowpt-query-digest(Percona 工具)找出最慢的 TOP SQL。
    • EXPLAIN 分析:看 type、rows、Extra,定位是全表扫描、filesort、还是缺索引。
    • SHOW PROFILE:看 SQL 各阶段耗时(sending data / sorting / ...)。
    • 优化器统计信息ANALYZE TABLE 更新统计信息,避免选错索引。
    • 索引调优:建联合索引、覆盖索引;删除冗余索引。
    • SQL 改写:避免 SELECT *NOT IN!=、函数操作索引字段。
    • 终极方案:分库分表 / 读写分离。
    为什么这么回答

    慢 SQL 排查是"日常基本功"。必答慢查询日志 + EXPLAIN,缺一不可。

    面试官会怎么追问
    • "索引建了但没走怎么办?" → 检查最左前缀、函数操作、隐式类型转换;用 force index 强制。
    • "MySQL 选错索引了?" → 统计信息不准;用 analyze table 重新收集。
    • "深分页怎么优化?" → SELECT * FROM t WHERE id > last_id LIMIT 10 替代 OFFSET 1000000
    怎么回答能拿高分
    讲一个真实故事:"我们有条订单查询 SQL 跑 8s,EXPLAIN 发现 type=ALL。建联合索引 (user_id, status, create_time) 后变 type=ref,耗时降到 50ms。"

    第六章 JVM 10 题

    高级 Java 必问,内存模型、GC、调优是核心。

    Q52 JVM 内存模型? ★★★★★ +
    标准答案(2 分钟)

    JDK 8 之后(最常用版本)的内存划分:

    • 堆(Heap)所有对象实例和数组。分代:
      新生代(Young):Eden + 2 个 Survivor(S0/S1)。
      老年代(Old):长期存活对象。
      方法区(Metaspace):类元数据、常量池(JDK 8 后从 PermGen 改为 Metaspace,使用本地内存)。
    • 虚拟机栈(JVM Stack):每个线程一个栈,栈帧存局部变量表、操作数栈、动态链接、方法出口。
    • 本地方法栈(Native Method Stack):Native 方法(C/C++)。
    • 程序计数器(PC):当前线程执行的字节码行号,唯一不会 OOM 的区域
    • 直接内存(Direct Memory):NIO 用,不受 JVM 堆大小限制,但归 GC 管
    为什么这么回答

    这是 JVM 第一必问题,5 个区域 + 堆分代必答全。

    面试官会怎么追问
    • "永久代和元空间区别?" → 永久代是堆的一部分,受 JVM 内存限制,易 OOM;元空间用本地内存,默认无上限
    • "为什么用元空间替代永久代?" → 类的元数据可能很大(动态生成类),永久代 -XX:MaxPermSize 难调;元空间弹性好。
    • "栈和堆的区别?" → 栈线程私有、自动分配、无 GC;堆线程共享、动态分配、需 GC。
    怎么回答能拿高分
    画一张图:左边线程私有(栈/PC/本地栈),右边线程共享(堆/方法区/直接内存)。视觉化最清晰。
    Q53 G1 垃圾回收器? ★★★★☆ +
    标准答案(2 分钟)

    G1(Garbage First)是JDK 9+ 默认的垃圾回收器,目标是"大内存 + 低延迟"。

    核心设计:

    • Region 化:把堆切成 2048 个等大小 Region(1-32MB),每个 Region 独立作为 Eden / Survivor / Old。
    • 可预测停顿:用户设 -XX:MaxGCPauseMillis=200,G1 按价值(垃圾多少)优先回收。
    • 混合回收(Mixed GC):回收新生代 + 部分老年代 Region。
    • 三色标记 + SATB:保证并发标记的正确性。
    • Humongous 对象:大对象独占连续 Region,避免拷贝。

    GC 类型:
    Young GC:回收 Eden + Survivor。
    Mixed GC:回收部分老年代。
    Full GC:单线程回收,要避免

    为什么这么回答

    G1 是"现代 GC"必问。必答 Region + 可预测停顿

    面试官会怎么追问
    • "G1 和 CMS 区别?" → G1 是Region 化 + 可预测停顿;CMS 是"标记-清除",有内存碎片,老年代满会触发 Full GC。G1 已被 Oracle 推为默认。
    • "什么时候用 ZGC?" → 超大堆(TB 级) + 亚毫秒延迟;JDK 11+ 引入。
    • "G1 调优目标是什么?" → 避免 Full GC,让 Region 价值均匀,老年代增长平稳。
    怎么回答能拿高分
    提一个实战参数:"我们用 -Xmx8g -XX:+UseG1GC -XX:MaxGCPauseMillis=200平均停顿 80ms,最大 180ms。"
    Q54 Full GC 怎么排查? ★★★☆☆ +
    标准答案(2 分钟)

    Full GC 是 Java 应用最大杀手,停顿秒级甚至分钟。排查流程:

    • 确认现象jstat -gcutil <pid> 1000 每秒打印 GC 状态,看 O(老年代)是否一直 100%。
    • 看 GC 日志-Xlog:gc*:file=gc.log:time,分析 GC 频率和耗时。
    • dump 堆jmap -dump:format=b,file=heap.hprof <pid>(生产慎用,会触发 Full GC)。
    • MAT / VisualVM 分析:看哪些对象占内存最多(看 Histogram / Dominator Tree)。
    • 找内存泄漏点
      ① 静态集合持有对象(static Map)。
      ② 监听器没注销。
      ③ ThreadLocal 没清理。
      ④ 池化对象没归还。
    • 优化方案:扩内存 / 调对象生命周期 / 改用 G1 / 优化大对象。
    为什么这么回答

    Full GC 排查是面试"经典场景题"。必答"jstat + jmap + MAT"三件套。

    面试官会怎么追问
    • "生产能不能 dump?" → 能,但选低峰期,且最好用 jmap -F 强制模式(不一定成功)。
    • "G1 频繁 Full GC 怎么办?" → 老年代增长太快;扩堆 / 优化大对象 / 调 IHOP(InitiatingHeapOccupancyPercent)。
    • "Arthas 在线排查怎么做?" → dashboard 看线程 + 内存,thread 找死循环,watch 监控方法参数。
    怎么回答能拿高分
    讲一个真实故事:"我们生产 OOM 5 次/小时,MAT 分析发现是 ThreadLocal 里的大 Map 没清理。改成 try-finally 显式 remove 后再没复发。"
    Q55 什么是双亲委派? ★★★★☆ +
    标准答案(2 分钟)

    定义:类加载时,先委托父加载器加载,父加载器加载不了才自己加载。

    JDK 三层类加载器:

    • Bootstrap ClassLoader:加载 %JAVA_HOME%/lib(rt.jar、tools.jar),C++ 实现,最顶层
    • Extension ClassLoader:加载 %JAVA_HOME%/lib/extjava.ext.dirs
    • Application ClassLoader:加载 classpath 上的类,用户写的代码
    • 自定义 ClassLoader:继承 ClassLoader 加载自定义路径。

    流程:AppClassLoader → ExtClassLoader → Bootstrap,逐层向上问,找到就返回

    目的:

    • 安全:核心类(java.lang.*)不会被自定义类覆盖。
    • 避免重复加载:父加载的类,子加载器不重复加载。
    为什么这么回答

    类加载的必问基础,必答三层 + 流程 + 目的。

    面试官会怎么追问
    • "为什么 Tomcat 打破双亲委派?" → Web 应用隔离,每个应用有自己的 ClassLoader,先自己加载,加载不到再委托。
    • "JDBC 怎么打破的?" → JDBC 接口在 rt.jar 由 Bootstrap 加载,但实现(驱动)在 classpath。SPI 机制用 ThreadContextClassLoader 反向加载。
    • "怎么打破双亲委派?" → 继承 ClassLoader 重写 loadClass,不调用 parent。
    怎么回答能拿高分
    提一句 Tomcat 案例:"Tomcat 每个 webapp 有独立 ClassLoader,不同应用的同名类互不干扰,所以双亲委派要被打破。"
    Q56 类加载过程? ★★★☆☆ +
    标准答案(2 分钟)

    类从加载到卸载分 7 个阶段

    • 加载(Loading):读 class 文件字节码 → 二进制流 → 方法区运行时数据结构 → 生成 Class 对象。
    • 验证(Verification):检查字节码合法性(魔数、版本、字节码指令)。
    • 准备(Preparation)类变量(static)分配内存 + 赋零值(final 变量直接赋值)。
    • 解析(Resolution):符号引用 → 直接引用。
    • 初始化(Initialization):执行 <clinit> 方法(static 变量赋值 + static 块)。
    • 使用(Using):正常使用。
    • 卸载(Unloading):GC 回收 Class 对象(很少发生)。

    注意:加载 ≠ 初始化。Class.forName 默认会初始化;ClassLoader.loadClass 不会。

    为什么这么回答

    类加载过程是 JVM 基础。必答"加载/验证/准备/解析/初始化"5 步。

    面试官会怎么追问
    • "什么时候触发初始化?" → new 实例 / 静态方法 / 静态字段(final 除外) / 反射 / 子类初始化父类先初始化。
    • "准备阶段 final 变量赋值吗?" → 赋值。基本类型常量在编译期就进常量池,准备阶段直接赋值。
    • "<clinit> 怎么保证线程安全?" → JVM 加锁,同一个 ClassLoader 只执行一次。
    怎么回答能拿高分
    补一句:"准备阶段是零值,不是赋值;public static int x = 1,准备阶段 x=0,初始化阶段才 x=1。这是常考点。"
    Q57 OOM 怎么排查? ★★★☆☆ +
    标准答案(2 分钟)

    OOM(OutOfMemoryError)分类:

    • 堆 OOMjava.lang.OutOfMemoryError: Java heap space → 对象太多,最常见
    • 元空间 OOMOutOfMemoryError: Metaspace → 类加载过多(动态代理、反射)。
    • 栈 OOMStackOverflowError → 递归太深。
    • 直接内存 OOMDirect buffer memory → NIO ByteBuffer 没释放。
    • GC OOMGC overhead limit exceeded → 98% 时间 GC,回收不到 2% 内存。

    排查流程:

    • 保留现场:让进程不要立即退出,-XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/tmp/heap.hprof 自动 dump。
    • 分析 dump:MAT(Memory Analyzer Tool)打开 hprof,看 Histogram(按类统计内存)、Dominator Tree(占内存最多的对象)、Leak Suspects(疑似泄漏点)。
    • 常见原因
      ① 内存泄漏(静态集合、监听器、ThreadLocal)。
      ② 大对象(一次查询返回 10w 条记录)。
      ③ 缓存无上限(Redis 也救不了 JVM 内的缓存)。
    为什么这么回答

    OOM 排查是面试必问场景。必答 5 种 OOM 类型 + 排查流程。

    面试官会怎么追问
    • "dump 文件太大怎么办?" → 用 jmap -F 强制 dump;或者只看 Leaking Class(MAT 支持)。
    • "线上没开 HeapDumpOnOutOfMemoryError 怎么办?" → 用 Arthas 的 heapdump 命令在线 dump。
    • "StackOverflowError 一般什么场景?" → 递归没有退出条件;JSON 循环引用序列化。
    怎么回答能拿高分
    补一句配置:"生产必开 HeapDumpOnOutOfMemoryError,否则 OOM 现场就丢了,根本没法查。"
    Q58 Arthas 怎么用? ★★★☆☆ +
    标准答案(2 分钟)

    Arthas 是阿里开源的Java 在线诊断工具,无需重启 JVM。常用命令:

    • dashboard:实时面板,看线程、内存、GC、运行环境。
    • thread:看线程状态,thread -b 找死锁线程,thread -n 3 CPU 最高的 3 个线程。
    • watch:监控方法入参和返回值,watch com.xx.Service method '{params, returnObj, throwExp}'
    • trace:方法调用链路,trace com.xx.Service method '#cost>10'(只打印耗时 > 10ms 的调用)。
    • jad:反编译 class,看生产环境实际跑的代码版本。
    • ognl:执行 OGNL 表达式,ognl '@com.xx.Config@getConfig()' 读静态字段。
    • redefine热更新代码(不重启)!redefine /tmp/MyClass.class。生产救命神器。
    • heapdump:在线 dump 堆(同 jmap)。
    为什么这么回答

    Arthas 是"生产排查神器",必答 dashboard/thread/watch/trace 四大命令,再补一个 redefine 加分。

    面试官会怎么追问
    • "redefine 有什么限制?" → 不能增删字段、不能改方法签名、不能改 static 块;只是"换方法实现"。
    • "Arthas 和 jstack 区别?" → Arthas 不用 jstack 抓快照,直接看实时;thread 命令是 jstack 的超集。
    • "会影响性能吗?" → 几乎无影响(用 bytecode 增强,不是代理)。
    怎么回答能拿高分
    讲一个真实故事:"我们凌晨 3 点生产出 bug,用 Arthas redefine 5 分钟修复,没重启服务。"真实案例最有说服力。
    Q59 MAT 怎么用? ★★★☆☆ +
    标准答案(2 分钟)

    MAT(Eclipse Memory Analyzer)是dump 文件分析工具,OOM 排查标配。核心功能:

    • Leak Suspects Report:自动分析可疑内存泄漏点,必看
    • Histogram:按类统计实例数 + 浅堆(对象本身大小)+ 保留堆(被引用对象总大小),找数量异常的对象
    • Dominator Tree:按保留堆排序,找占用内存最多的根对象
    • List objects:右键某个对象 → "with incoming references"(被谁引用)/ "outgoing references"(引用了谁),找泄漏链路
    • OQL(对象查询语言):类似 SQL 查询对象,SELECT * FROM com.xx.User WHERE name.length > 10

    典型排查:

    • 看 Dominator Tree 找到 HashMap$Node[] 占用 60%。
    • 右键 → List objects → with incoming references。
    • 发现被 static Map 持有。
    • 确认是静态集合没清理。
    为什么这么回答

    MAT 是 OOM 排查的"瑞士军刀"。必答 Leak Suspects + Dominator Tree

    面试官会怎么追问
    • "浅堆和深堆区别?" → 浅堆 = 对象本身大小;深堆 = 对象 + 直接/间接引用的对象总大小(GC 释放后才能回收)。
    • "怎么判断是泄漏还是大对象?" → 泄漏是"对象本应被回收但被引用";大对象是"对象真的需要这么多",前者能 GC 释放,后者不能
    • "大文件 MAT 打不开?" → 用 mat/ParseHeapDump.sh 命令行生成报告,不用 GUI
    怎么回答能拿高分
    补一句:"MAT 的 Retained Heap 才是判断'谁占用最多'的正确指标,不是 Shallow Heap,很多新手分不清。"
    Q60 JVM 调优实战? ★★★☆☆ +
    标准答案(2 分钟)

    JVM 调优的核心思路:先诊断,再调参,再验证。常见场景:

    • 频繁 Full GC
      ① 堆不够 → -Xmx 调大。
      ② 大对象多 → 拆分查询结果。
      ③ 元空间不足 → -XX:MaxMetaspaceSize 调大。
      ④ 内存泄漏 → MAT 查引用链。
    • GC 停顿过长
      ① 用 G1/ZGC 替代 CMS。
      -XX:MaxGCPauseMillis=200 设目标停顿。
      ③ 控制堆大小(堆越大,GC 越慢,但频率低)。
    • 启动慢
      ① 类太多 → 排查 jar 依赖。
      ② 用 AppCDS / GraalVM Native Image 预编译。
    • 常用参数
      -Xms4g -Xmx4g(堆大小)。
      -Xlog:gc*:file=gc.log:time(GC 日志)。
      -XX:+HeapDumpOnOutOfMemoryError(OOM 自动 dump)。
      -XX:+UseG1GC(用 G1)。
    为什么这么回答

    调优是"经验型问题",展示你"真的会做"而不是"背参数"。

    面试官会怎么追问
    • "Xms 和 Xmx 设一样好不好?" → 防止堆动态扩缩容(每次扩容都要 Full GC),生产推荐设一样
    • "G1 的 IHOP 怎么调?" → -XX:InitiatingHeapOccupancyPercent=45,让 Mixed GC 更早触发,避免并发失败。
    • "怎么评估调优效果?" → 量化指标:GC 频率、平均停顿、P99 延迟、吞吐量(业务 QPS)。
    怎么回答能拿高分
    讲一个完整调优故事:"我们 YGC 5s 一次,FGC 每小时 1 次。用 G1 替换 CMS + 调大堆到 8G 后,YGC 频率不变,FGC 消失,P99 延迟从 500ms 降到 80ms。"
    Q61 垃圾回收算法? ★★★☆☆ +
    标准答案(2 分钟)

    四种基础 GC 算法:

    • 标记-清除(Mark-Sweep)
      ① 标记可达对象,清除未标记对象。
      ② 优点:简单。
      ③ 缺点:内存碎片,导致后续分配大对象失败。CMS 用。
    • 标记-整理(Mark-Compact)
      ① 标记后,把存活对象向一端移动,清除边界外内存。
      ② 优点:无碎片。
      ③ 缺点:移动对象代价大,停顿长。Serial Old / Parallel Old 用。
    • 复制(Copying)
      ① 把内存分两块,只用一块;GC 时把存活对象复制到另一块,清空原块。
      ② 优点:无碎片、快。
      ③ 缺点:内存利用率 50%。新生代用(对象 90% 死亡,复制少)。
    • 分代(Generational)
      ① 综合以上三种,实际生产 GC 都用分代
      ② 新生代用复制,老年代用标记-清除或标记-整理。
    为什么这么回答

    GC 算法是理论,必答分代思想(为什么新生代用复制、老年代用标记-清除/整理)。

    面试官会怎么追问
    • "新生代为什么分 Eden + 2 Survivor?" → 复制算法要两块 Survivor 来回倒,避免浪费 50%。HotSpot 把 Eden:S0:S1 设为 8:1:1。
    • "对象什么时候进老年代?" → ① 年龄 ≥15(MaxTenuringThreshold)。② 大对象直接进老年代。③ 动态年龄判断(S0/S1 同龄对象总和 > S0 一半)。
    • "什么是空间分配担保?" → Minor GC 前,老年代可用空间 > 新生代对象总和;否则 Full GC。
    怎么回答能拿高分
    补一句取舍:"复制快但费内存,标记整理不费但慢;分代是扬长避短,让每种算法用在最合适的场景。"

    第七章 并发 10 题

    高级 Java 的"分水岭",synchronized、volatile、CAS、AQS 必问。

    Q62 synchronized 原理? ★★★★★ +
    标准答案(2 分钟)

    synchronized 是 Java 内置的互斥锁,JDK 1.6 后做了大量优化(锁升级)。

    锁对象:

    • 修饰实例方法 → 锁当前实例 this
    • 修饰静态方法 → 锁 Class 对象。
    • 修饰代码块 → 锁括号里的对象。

    锁升级过程(重点):

    • 无锁 → 偏向锁:第一个线程进入,CAS 记录线程 ID,不抢锁
    • 偏向锁 → 轻量级锁(自旋锁):第二个线程来抢,CAS 失败就自旋(while 循环)。
    • 轻量级锁 → 重量级锁:自旋超过 10 次或线程数太多,升级 OS 互斥锁,线程阻塞。

    底层:对象头的 Mark Word 存锁状态,monitor 对象 实现同步。

    为什么这么回答

    synchronized 是 Java 并发"基本功"。必答锁升级(无锁→偏向→轻量→重量)

    面试官会怎么追问
    • "JDK 15 后偏向锁被默认禁用了,知道吗?" → JEP 374 移除偏向锁,因为实际收益小、复杂度大;现在默认轻量级锁起。
    • "自旋会浪费 CPU 吗?" → 会,但短时间自旋比线程阻塞唤醒的 context switch 划算;自适应自旋(JDK 6+)根据上次数据动态调整。
    • "wait/notify 必须在 synchronized 里吗?" → 必须。wait 释放锁并阻塞;notify 唤醒。
    怎么回答能拿高分
    补一句:"我们做性能优化时把 Vector 替换为 ConcurrentHashMap用 synchronized 的代码改成 ReentrantLock,QPS 提升 3 倍。"
    Q63 volatile 原理? ★★★★☆ +
    标准答案(2 分钟)

    volatile 是 Java 的轻量级同步机制,保证:

    • 可见性:一个线程修改了变量,其他线程立即看到(强刷主内存)。
    • 禁止指令重排序:通过内存屏障(Memory Barrier)实现。

    底层原理:

    • CPU 总线加 LOCK 信号(MESI 缓存一致性协议)。
    • 写 volatile 变量时,强制刷主内存 + 失效其他 CPU 缓存
    • 读 volatile 变量时,强制从主内存读
    • JVM 插入 StoreLoad 屏障,禁止前后指令重排。

    和 synchronized 区别:

    • volatile 不保证原子性(i++ 三步操作还是会出问题)。
    • volatile 不阻塞线程,性能更好
    为什么这么回答

    volatile 是必问基础,必答"可见性 + 禁止重排序"。

    面试官会怎么追问
    • "volatile 能代替 synchronized 吗?" → 不能,volatile 只保证单变量读写的原子性,复合操作还是要锁。
    • "什么是 happens-before?" → JMM 定义的"先行发生"规则,是判断线程安全的依据;volatile 写 happens-before 后续 volatile 读。
    • "DCL 单例为什么要加 volatile?" → 防止指令重排序导致返回未初始化的对象。new 三步:分配内存 → 初始化 → 赋值引用,重排序后可能返回半初始化对象
    怎么回答能拿高分
    画 JMM 图:工作内存 ↔ 主内存,volatile 变量读直接走主内存(红色箭头),普通变量要先 load 缓存。
    Q64 CAS 原理? ★★★★☆ +
    标准答案(2 分钟)

    CAS(Compare-And-Swap):比较并交换,乐观锁的基础。底层是 CPU 指令 cmpxchg

    三个操作数:内存地址 V、期望值 A、新值 B。当 V == A 时把 V 改成 B,否则什么都不做。

    Java 中通过 Unsafe 类调用,封装在 AtomicInteger 等原子类中:

    // AtomicInteger.incrementAndGet
    do {
        old = this.value;
        new = old + 1;
    } while (!compareAndSwap(value, old, new));

    三大问题:

    • ABA 问题:值从 A 变 B 又变 A,CAS 误判未变。解决:版本号AtomicStampedReference)。
    • 自旋开销:长时间不成功会消耗 CPU。解决:自适应自旋 / 退避。
    • 只能单变量不能保证多个变量原子性。解决:AtomicReference 包装对象。
    为什么这么回答

    CAS 是 Java 并发包的"灵魂",所有锁的底层都靠 CAS。必答 ABA 问题

    面试官会怎么追问
    • "ABA 实际有什么危害?" → 链表操作时,头节点 A→B→A,CAS 误以为没变,新节点会覆盖 B。用 AtomicStampedReference 解决
    • "CAS 是无锁吗?" → 严格说是乐观锁,不是无锁;CAS 失败会自旋,占 CPU。
    • "CAS 在 JDK 哪里用?" → AtomicInteger / ConcurrentHashMap / ReentrantLock / AQS 全用 CAS。
    怎么回答能拿高分
    补一句:"我们做分布式锁时也用类似 CAS 的思路:Redis SETNX + 版本号,和 Java CAS 思想一致。"
    Q65 AQS 原理? ★★★★☆ +
    标准答案(2 分钟)

    AQS(AbstractQueuedSynchronizer):Java 并发包的"地基",ReentrantLock / CountDownLatch / Semaphore 都基于它。

    核心三要素:

    • statevolatile int 状态变量,子类自定义含义(ReentrantLock 是重入次数,Semaphore 是剩余许可)。
    • CLH 队列:FIFO 双向链表,存放等待线程(Node)。
    • CAS 操作 statecompareAndSetState 改状态;失败就进队列阻塞。

    工作流程:

    • 线程获取锁,CAS 把 state 从 0 改 1;成功就拿到锁。
    • 失败 → 包装成 Node 加到队列尾部 → park 阻塞。
    • 释放锁时,唤醒下一个节点(unpark)。

    两种模式:独占锁(ReentrantLock)和共享锁(CountDownLatch)。

    为什么这么回答

    AQS 是"高级 Java 必问"。必答 state + CLH 队列 + CAS三件套。

    面试官会怎么追问
    • "ReentrantLock 怎么实现可重入?" → state 加 1;释放时减 1,减到 0 才真正释放。
    • "公平锁和非公平锁区别?" → 公平锁先看队列头;非公平锁直接抢。非公平锁吞吐高(少一次 park/unpark)。
    • "AQS 为什么不用 synchronized?" → synchronized 是 JVM 内置的,AQS 想自己控制更灵活的排队和唤醒
    怎么回答能拿高分
    画一张 AQS 内部结构图:state + CLH 队列 + Node(thread + waitStatus)。视觉化让面试官一眼明白。
    Q66 ReentrantLock 原理?和 synchronized 区别? ★★★☆☆ +
    标准答案(2 分钟)

    ReentrantLock 是基于 AQS 的可重入互斥锁,功能比 synchronized 强:

    • 可中断lockInterruptibly() 在等待时可被中断。
    • 公平 / 非公平:构造函数可选,new ReentrantLock(true) 公平锁。
    • 可限时tryLock(2, TimeUnit.SECONDS) 超时返回。
    • 多条件变量Condition 灵活实现 wait/notify。

    和 synchronized 对比:

    维度synchronizedReentrantLock
    实现JVM 内置JDK 代码(AQS)
    锁升级有(偏向/轻量/重量)无(直接 CAS)
    可中断
    公平锁可选
    性能JDK 6 优化后接近高并发稍优

    选型:简单场景用 synchronized,复杂场景用 ReentrantLock

    为什么这么回答

    和 synchronized 对比是必问题。表格化对比最清晰。

    面试官会怎么追问
    • "生产用哪个?" → 默认 synchronized(简单、JVM 优化好),需要公平/可中断时用 ReentrantLock。
    • "ReentrantLock 必须手动释放吗?" → 是!必须 try-finally,否则死锁。tryLock 也一样。
    • "ReadWriteLock 怎么用?" → ReentrantReadWriteLock,读读共享、读写互斥、写写互斥,适合读多写少
    怎么回答能拿高分
    补一句取舍:"synchronized 用脑简单(自动释放、不易出错),ReentrantLock 功能强大(可中断、公平锁),JDK 6 之后性能差异不大,90% 场景 synchronized 就够。"
    Q67 ThreadLocal 原理?内存泄漏? ★★★☆☆ +
    标准答案(2 分钟)

    ThreadLocal 给每个线程独享的变量副本,线程隔离。底层结构:

    • 每个 Thread 有一个 ThreadLocalMap(类比 HashMap)。
    • key 是 ThreadLocal 实例,value 是 set 的值。
    • set/get 操作只对当前线程可见,线程间不共享

    典型用途:

    • Web 请求上下文(用户信息、traceId)。
    • 数据库连接 / Session 管理。
    • Spring 的 @Transactional 用 ThreadLocal 传 Connection。

    内存泄漏原因:

    • ThreadLocalMap 的 Entry key 是弱引用WeakReference),value 是强引用
    • ThreadLocal 没人引用时,key 被 GC 回收,但 value 还在,线程不死 value 不释放
    • 尤其线程池复用的线程不结束,value 一直累积。

    解决:用完手动 removetry { ... } finally { threadLocal.remove(); }

    为什么这么回答

    ThreadLocal 内存泄漏是经典面试题,必答"弱引用 key + 强引用 value + 线程池不释放"。

    面试官会怎么追问
    • "为什么 key 用弱引用?" → 如果用强引用,ThreadLocal 一直释放不掉;弱引用让 ThreadLocal 没用时被回收,但 value 还得手动清
    • "InheritableThreadThreadLocal 是什么?" → 子线程继承父线程的 ThreadLocal,但线程池场景失效(线程是复用的)。
    • "Spring 怎么解决的?" → RequestContextHolder + Filter 拦截器在请求结束时 remove。
    怎么回答能拿高分
    讲一个实战案例:"我们生产 OOM 5 次/小时,MAT 分析 ThreadLocal 里的 Map 占了 60% 内存,因为线程池里的线程一直不结束,加上 ThreadLocal 没 remove。改成 try-finally 后再没复发。"
    Q68 线程池原理? ★★★☆☆ +
    标准答案(2 分钟)

    ThreadPoolExecutor 7 个核心参数:

    • corePoolSize:核心线程数(即使空闲也不回收)。
    • maximumPoolSize:最大线程数。
    • keepAliveTime:非核心线程空闲存活时间。
    • unit:时间单位。
    • workQueue:任务队列(ArrayBlockingQueue / LinkedBlockingQueue / SynchronousQueue)。
    • threadFactory:线程工厂(命名、daemon)。
    • handler:拒绝策略(Abort / Discard / DiscardOldest / CallerRuns)。

    执行流程:

    • 任务来 → 核心线程数未满 → 创建核心线程。
    • 核心线程数满 → 进 workQueue。
    • 队列满 → 创建非核心线程(直到 maximumPoolSize)。
    • 线程数到 max + 队列满 → 触发拒绝策略。
    为什么这么回答

    线程池是高级 Java 必问。必答 7 个参数 + 执行流程。

    面试官会怎么追问
    • "怎么设置线程数?" → CPU 密集型:N+1(避免上下文切换)。IO 密集型:2N(N 是 CPU 核数)。
    • "为什么不推荐用 Executors 工厂?" → newFixedThreadPool 用 LinkedBlockingQueue 无界,会 OOMnewCachedThreadPool 线程数无上限,会 OOM。生产必须手写 ThreadPoolExecutor。
    • "拒绝策略选哪个?" → 默认 AbortPolicy 抛异常;重要任务用 CallerRuns(调用线程自己跑,保证不丢)。
    怎么回答能拿高分
    提一个公式:"我们的 Web 服务,线程数 = CPU 核数 × 2 + 1,每台 8 核机器配 17 线程;RT 长的话再翻倍。"具体数字让面试官觉得你真调过。
    Q69 Future 和 CompletableFuture 区别? ★★★☆☆ +
    标准答案(2 分钟)

    Future(JDK 5):异步任务结果,但只能阻塞 get(),不能链式、不能回调、不能组合。

    CompletableFuture(JDK 8):Future 的超集,支持链式编排 + 回调 + 组合

    CompletableFuture 核心能力:

    • 链式调用thenApply() 转换结果,thenAccept() 消费,thenRun() 不关心结果。
    • 组合thenCombine() 两个任务都完成后合并,allOf() 全部完成,anyOf() 任一完成。
    • 异常处理exceptionally() 兜底,handle() 同时处理结果和异常。
    • 异步执行supplyAsync() 用 ForkJoinPool.commonPool() 异步跑。

    实战:并行查询三个服务再合并结果,从串行 300ms 降到 100ms(最慢的那个)。

    为什么这么回答

    CompletableFuture 是现代 Java 必备。必答"链式 + 组合 + 异常处理"。

    面试官会怎么追问
    • "线程池怎么指定?" → supplyAsync(supplier, customExecutor)别用默认的 ForkJoinPool(会被其他任务影响)。
    • "thenApply 和 thenApplyAsync 区别?" → Async 结尾会用新线程跑,可能切线程;非 Async 沿用当前线程(如果已完成则同步跑)。
    • "join() 和 get() 区别?" → get() 抛 checked exception;join() 抛 unchecked,CompletableFuture 推荐用 join
    怎么回答能拿高分
    画一张时序图:3 个异步任务 → allOf 等齐 → thenAccept 聚合。视觉化最清晰。
    Q70 CountDownLatch 和 CyclicBarrier 区别? ★★★☆☆ +
    标准答案(2 分钟)

    都是 AQS 实现的线程协作工具,但定位不同:

    • CountDownLatch(倒计时门闩)
      ① 一个线程等 N 个线程完成后再继续。
      ② 用法:主线程 await(),工作线程每完成一个 countDown(),count=0 时主线程被唤醒。
      一次性,不能 reset。
    • CyclicBarrier(循环栅栏)
      ① N 个线程互相等待,到齐后一起继续。
      ② 用法:每个线程 await(),所有线程到齐后一起放行。
      可循环用,能 reset。

    对比:

    • CountDownLatch:1 等 N(主等子),单向
    • CyclicBarrier:N 等 N(互相等),对称

    场景:

    • CountDownLatch:主线程等所有子任务完成 → 汇总结果。
    • CyclicBarrier:多线程分阶段计算 → 每阶段等齐再下一阶段。
    为什么这么回答

    这两个工具是面试常考"辨析题"。必答 1等N vs N等N、一次性 vs 循环

    面试官会怎么追问
    • "CyclicBarrier 的 barrierAction 是什么?" → 所有线程到齐后,由最后一个到达的线程执行的回调,常用于合并各阶段结果。
    • "CountDownLatch 怎么计数超过 N?" → 不会,count 减到 0 后再 countDown 无效。
    • "还有什么类似的工具?" → Semaphore(信号量,限流用)、Phaser(更灵活的 CyclicBarrier)。
    怎么回答能拿高分
    讲一个场景:"我们启动应用时用 CountDownLatch 等 Redis/MQ 初始化完再开 HTTP 端口,保证 0 启动失败请求。"
    Q71 ConcurrentHashMap 原理? ★★★☆☆ +
    标准答案(2 分钟)

    JDK 8 的 ConcurrentHashMap:

    • 数据结构:Node 数组 + 链表 + 红黑树(链表长度 ≥8 且数组 ≥64 时转红黑树)。
    • 并发控制CAS + synchronized(不是 Segment 段锁,JDK 7 是 Segment)。
    • 读不加锁:volatile 保证可见性。
    • 写加锁:锁链表/红黑树的头节点(不是整个数组,锁粒度更细)。
    • 扩容多线程协同扩容(forwarding node + transfer),扩容时读写可并发。

    put 流程:

    • 计算 hash → 定位桶。
    • 桶为空 → CAS 写入(无锁)。
    • 桶非空 → 锁头节点 → 链表追加 / 红黑树插入。
    • 数量超阈值 → 扩容。
    为什么这么回答

    ConcurrentHashMap 是 Java 并发集合的"代表作"。必答 CAS + 锁头节点(不是 Segment)。

    面试官会怎么追问
    • "size 怎么保证准确?" → 不保证。用 baseCount + CounterCell[] 分散统计,最终一致性;返回的 size 是近似值。
    • "为什么不用 HashMap?" → 多线程下 HashMap 扩容会链表成环,CPU 100%;ConcurrentHashMap 解决。
    • "为什么不用 Hashtable?" → Hashtable 所有方法 synchronized,性能差,已被淘汰。
    怎么回答能拿高分
    补一句:"JDK 7 用 Segment 段锁(16 个段),JDK 8 改成 Node 头节点锁,锁粒度更细,并发度更高。"
    03

    Spring 生态(10 题)

    ★★★★☆
    通用基础 · 必须流畅讲原理

    第八章 Spring 核心 10 题

    你简历里大量用 Spring Cloud、Spring Boot、APISIX,这些必问。

    Q72 Bean 生命周期? ★★★★★ +
    标准答案(2 分钟)

    Spring Bean 生命周期(4 个阶段 + 9 个回调):

    • 1. 实例化:构造方法创建对象(InstantiationAwareBeanPostProcessor)。
    • 2. 属性填充:依赖注入(@Autowired)。
    • 3. 初始化前BeanPostProcessor.postProcessBeforeInitialization
    • 4. 初始化
      @PostConstruct
      InitializingBean.afterPropertiesSet()
      ③ 自定义 init-method。
    • 5. 初始化后BeanPostProcessor.postProcessAfterInitialization(AOP 代理在这里创建)。
    • 6. 使用:业务调用。
    • 7. 销毁
      @PreDestroy
      DisposableBean.destroy()
      ③ 自定义 destroy-method。
    为什么这么回答

    必答基础。记住 4 阶段:实例化 → 填充 → 初始化 → 销毁

    面试官会怎么追问
    • "AOP 在哪一步生效?" → 初始化后,postProcessAfterInitialization 把目标对象包装成代理对象。
    • "BeanFactory 和 ApplicationContext 区别?" → ApplicationContext 是 BeanFactory 子接口,多了国际化、事件、AOP、资源加载,生产都用 ApplicationContext。
    • "Bean 作用域?" → singleton(默认)/ prototype / request / session / application / websocket。
    怎么回答能拿高分
    画一张时序图,从左到右:实例化 → DI → @PostConstruct → InitializingBean → init-method → BeanPostProcessor → 使用 → @PreDestroy。视觉化最清楚。
    Q73 SpringBoot 自动装配原理? ★★★★☆ +
    标准答案(2 分钟)

    Spring Boot 自动装配 = "约定优于配置",核心是 3 个注解:

    • @SpringBootApplication = @SpringBootConfiguration + @EnableAutoConfiguration + @ComponentScan。
    • @EnableAutoConfiguration 导入 AutoConfigurationImportSelector
    • 它读 META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports(Spring Boot 3+)或老的 spring.factories,加载所有 XxxAutoConfiguration 类。
    • 每个 AutoConfiguration 用 @Conditional 系列注解判断是否生效(如 @ConditionalOnClass@ConditionalOnBean@ConditionalOnMissingBean)。
    • 满足条件就装配这个 starter 的 Bean。
    为什么这么回答

    Spring Boot 必问题。必答"imports 文件 + @Conditional 条件装配"

    面试官会怎么追问
    • "spring.factories 和 imports 区别?" → Spring Boot 3 之前用 spring.factories(META-INF 下);3.x 后用 imports(性能更好,支持按需加载)。
    • "怎么禁用某个 AutoConfiguration?" → @SpringBootApplication(exclude = DataSourceAutoConfiguration.class)spring.autoconfigure.exclude 配置。
    • "怎么自定义 starter?" → 写一个 XxxAutoConfiguration + META-INF/spring/...imports 列出类名。
    怎么回答能拿高分
    讲一个案例:"我们做 SaaS 多租户时,写了一个 starter 屏蔽数据库自动装配,按租户动态切换数据源。"
    Q74 @Transactional 为什么失效? ★★★★★ +
    标准答案(2 分钟)

    常见 7 种失效场景:

    • 1. 方法不是 public:Spring AOP 代理要求 public;private/protected 不生效。
    • 2. 自调用(this 调用)this.method() 不走代理,最常见。解决:注入自己(@Autowired private Service self;)或 AopContext。
    • 3. 异常被 catch 吃掉try { ... } catch (Exception e) { log(); },事务不会回滚。
    • 4. 抛出非 RuntimeException:默认只回滚 RuntimeException,IOException 不回滚。rollbackFor = Exception.class
    • 5. 数据库引擎不支持:MyISAM 不支持事务,必须 InnoDB
    • 6. 没被 Spring 管理:new 出来的对象不是 Bean,事务不生效。
    • 7. 事务传播行为错误Propagation.NOT_SUPPORTEDNEVER 等会挂起/禁止事务。
    为什么这么回答

    这是"送命题"——99% 的 Java 程序员都踩过坑。必答"自调用"和"异常被 catch"。

    面试官会怎么追问
    • "自调用为什么失效?" → 代理对象是 Spring 创建的,this 是原对象,绕过代理,注解失效。
    • "REQUIRED 和 REQUIRES_NEW 区别?" → REQUIRED 加到外层事务里(共用);REQUIRES_NEW 挂起外层,开新事务(独立提交/回滚)。
    • "怎么查看事务是否生效?" → 打开 logging.level.org.springframework.transaction.interceptor=TRACE
    怎么回答能拿高分
    讲一个真实事故:"我们生产 2022 年出过 bug:try-catch 吞了异常,订单创建成功但扣库存失败,超卖了 1w 单。改成 throws + rollbackFor 后修复。"
    Q75 Spring AOP 原理? ★★★☆☆ +
    标准答案(2 分钟)

    Spring AOP 是动态代理实现的,2 种方式:

    • JDK 动态代理:目标类实现接口,用 Proxy.newProxyInstance()基于接口
    • CGLIB 动态代理:目标类没接口(或者配置 spring.aop.proxy-target-class=true),用 CGLIB 生成子类。基于继承Spring Boot 2+ 默认

    Spring 选择逻辑:

    • 目标类有接口 → JDK 代理(除非强制 CGLIB)。
    • 目标类没接口 → CGLIB 代理。

    流程:

    • 容器启动时为 Bean 创建代理对象。
    • 调用方法时,代理对象拦截 → 切面逻辑 → 调用目标方法
    • 切面包括 @Around、@Before、@After、@AfterReturning、@AfterThrowing。
    为什么这么回答

    AOP 是 Spring 核心,必答 JDK vs CGLIB

    面试官会怎么追问
    • "为什么 Spring Boot 2+ 默认 CGLIB?" → 即使类有接口,CGLIB 也能代理;JDK 代理要求类必须实现接口,限制更多
    • "CGLIB 不能代理什么?" → final 类 / final 方法 / private 方法,因为不能继承 / 不能重写。
    • "JDK 代理和 CGLIB 性能差异?" → 现代 JVM 差距很小,不超过 10%;CGLIB 启动慢,JDK 反射调用稍慢。
    怎么回答能拿高分
    补一句:"我们做统一的 @Around 日志切面,用 ThreadLocal 传 traceId避免自调用(绕过代理)。"
    Q76 Spring 循环依赖怎么解决? ★★★★☆ +
    标准答案(2 分钟)

    定义:A 依赖 B,B 依赖 A,构造时死循环。

    Spring 通过三级缓存解决:

    • 一级缓存(singletonObjects):完整 Bean。
    • 二级缓存(earlySingletonObjects):早期引用(未填充属性)。
    • 三级缓存(singletonFactories):ObjectFactory,能生成早期引用。

    流程(A 依赖 B,B 依赖 A):

    • 创建 A,实例化(new A()),放入三级缓存(ObjectFactory)。
    • 填充 A 的属性 → 发现需要 B。
    • 创建 B,实例化,填充 B 的属性 → 需要 A → 从三级缓存拿到 A 的早期引用(ObjectFactory.getObject())。
    • B 初始化完成,放入一级缓存。
    • A 继续填充(拿到 B),初始化完成,放入一级缓存。

    局限构造器注入的循环依赖无法解决(实例化前就要依赖),需要用 @Lazy 或改成 setter 注入。

    为什么这么回答

    三级缓存是 Spring 高频题。必答"三级缓存 + 早期引用"

    面试官会怎么追问
    • "为什么需要三级,两级不行吗?" → 三级存 ObjectFactory,为了支持 AOP(代理对象)。如果没有 AOP,两级就够。
    • "构造器注入怎么办?" → 用 @Lazy 延迟加载,打破循环。或者改用 setter / 字段注入。
    • "Spring Boot 2.6+ 默认禁了循环依赖,为什么?" → 循环依赖是代码设计问题,禁用倒逼优化。
    怎么回答能拿高分
    画一张图:A → B → A 的依赖链 + 三级缓存。强调"三级缓存是为了 AOP(生成代理对象)"。
    Q77 SpringMVC 请求流程? ★★★☆☆ +
    标准答案(2 分钟)

    一个 HTTP 请求到响应,9 个核心组件:

    • 1. DispatcherServlet:前端控制器,所有请求的入口。
    • 2. HandlerMapping:根据 URL 找对应的 Handler(Controller 方法)
    • 3. HandlerExecutionChain:Handler + 拦截器链。
    • 4. HandlerAdapter:执行 Handler,处理参数绑定、注解解析。
    • 5. ModelAndView:Handler 返回的模型 + 视图名。
    • 6. ViewResolver:把视图名解析成 View 对象(如 Thymeleaf、FreeMarker)。
    • 7. View:渲染模型数据。
    • 8. 返回响应:写回浏览器。
    • 9. HandlerInterceptor:拦截器在 Handler 前后各执行一次。
    为什么这么回答

    经典题,必答 9 组件(或者简化版:DS → HM → HA → Controller → View → 响应)。

    面试官会怎么追问
    • "@RestController 和 @Controller 区别?" → @RestController = @Controller + @ResponseBody,直接返回 JSON,不走视图解析
    • "过滤器(Filter)和拦截器(Interceptor)区别?" → Filter 是 Servlet 规范,所有请求都拦;Interceptor 是 Spring MVC 概念,只拦 Controller 请求
    • "HandlerExecutionChain 是什么?" → Spring 把 Handler 和它对应的拦截器链打包,方便统一调度。
    怎么回答能拿高分
    画一张时序图:浏览器 → DispatcherServlet → HandlerMapping → HandlerAdapter → Controller → ViewResolver → View → 响应。视觉化最清晰。
    Q78 Spring Cloud 注册中心原理? ★★★☆☆ +
    标准答案(2 分钟)

    主流注册中心对比:Eureka / Nacos / Consul / Zookeeper

    核心三要素:

    • 1. 服务注册:服务启动时向注册中心发心跳,默认 30s 一次
    • 2. 服务发现:客户端从注册中心拉服务列表,缓存到本地,定时刷新。
    • 3. 健康检查:心跳超时(默认 90s)就剔除实例。

    Nacos(推荐)特点:

    • 同时支持 AP(最终一致)+ CP(强一致),可切换。
    • 支持配置的动态管理。
    • 内置 Ribbon 实现负载均衡。

    Eureka 特点:

    • 纯 AP,最终一致,适合可用性高的场景。
    • 已停止维护,新项目用 Nacos
    为什么这么回答

    微服务基础。必答"注册 + 发现 + 心跳"。

    面试官会怎么追问
    • "CAP 是什么?" → C 一致性 / A 可用性 / P 分区容错性。分布式必舍两个。Nacos 默认 AP,可切 CP。
    • "注册中心挂了怎么办?" → 客户端有缓存,还能继续调用;新服务注册不了,已注册的服务不受影响。
    • "如何选择?" → 简单场景用 Nacos;多语言用 Consul;老项目用 Eureka / ZK。
    怎么回答能拿高分
    补一句经验:"我们从 Eureka 迁到 Nacos,配置中心也用 Nacos,少维护一套组件。"
    Q79 Spring Cloud Gateway 原理? ★★★☆☆ +
    标准答案(2 分钟)

    Spring Cloud Gateway 是 Spring 官方推出的API 网关,基于 WebFlux + Netty(响应式,非阻塞)。

    核心概念:

    • Route(路由):路由规则(路径 → 目标服务)。
    • Predicate(断言):路由条件(如 Path、Method、Header)。
    • Filter(过滤器):请求 / 响应处理(鉴权、限流、修改头)。

    请求流程:

    • 请求进入 Gateway → 匹配 Predicate → 找到目标 Route → 执行 Filter 链 → 转发到下游服务。
    • 内置过滤器:AddRequestHeaderStripPrefixHystrix(已废弃,用 Sentinel)。
    • 限流:RequestRateLimiter + Redis 令牌桶。

    和 Zuul 对比:Gateway 性能更好(Netty 非阻塞),Zuul 1.x 是阻塞的,已淘汰

    为什么这么回答

    必答核心三件套:Route + Predicate + Filter

    面试官会怎么追问
    • "和 Nginx 区别?" → Nginx 是 L4/L7 反向代理,高性能但配置复杂;Gateway 是 L7,灵活 + 业务定制,可以和 Eureka/Nacos 集成。
    • "限流怎么做的?" → 令牌桶 / 漏桶;RequestRateLimiter 基于 Redis 计数。
    • "为什么不用 Zuul?" → Zuul 1.x 同步阻塞,性能差;Zuul 2.x 也是 Netty,但已不维护。
    怎么回答能拿高分
    讲一个实战配置:"我们用 Gateway 做统一鉴权 + 灰度发布,根据 Header 里的 version 字段路由到不同服务实例。"
    Q80 OpenFeign 原理? ★★★☆☆ +
    标准答案(2 分钟)

    OpenFeign 是声明式 HTTP 客户端,像调用本地方法一样调用远程服务

    核心原理:

    • JDK 动态代理@FeignClient 注解的接口被 Spring 扫描到,创建代理对象
    • 调用接口方法时,代理拦截 → 解析注解(@RequestMapping 等) → 拼装 HTTP 请求。
    • 通过 HttpClient 发送请求(默认 JDK URLConnection,可换 OkHttp / Apache HttpClient)。
    • 解析响应 → 反序列化为对象 → 返回。

    和 Ribbon 集成:客户端负载均衡,从注册中心拿服务列表,本地选一个实例。

    和 Sentinel 集成:熔断降级

    为什么这么回答

    OpenFeign 是微服务调用基础。必答"动态代理 + 注解解析"

    面试官会怎么追问
    • "和 RestTemplate 区别?" → RestTemplate 手写 URL 和 HTTP 代码;Feign 像本地方法,零代码
    • "怎么传文件?" → @RequestPart + MultipartFile,Feign 自带支持。
    • "超时怎么配?" → feign.client.config.default.connectTimeout=5sreadTimeout=10s
    怎么回答能拿高分
    讲一个实战配置:"我们用 Feign 调支付服务,feign.sentinel.enabled=true 走 Sentinel 熔断,超时 2s 自动降级返回默认值。"
    Q81 Sentinel 限流和熔断? ★★★☆☆ +
    标准答案(2 分钟)

    Sentinel 是阿里开源的流量控制 + 熔断降级组件,比 Hystrix 功能强、UI 好用。

    核心功能:

    • 1. 限流(Flow Control)
      ① QPS 限流:每秒最多 N 个请求。
      ② 并发线程数限流。
      ③ 流量整形:排队等待。
    • 2. 熔断降级(Circuit Breaking)
      ① 慢调用比例:RT > 阈值 + 比例 > 阈值 → 熔断。
      ② 异常比例:异常率 > 阈值 → 熔断。
      ③ 熔断后降级走 fallback 方法。
    • 3. 热点参数限流:特定参数值的限流(如商品 ID)。
    • 4. 系统自适应限流:根据 CPU / Load / RT 自动调整。

    和 Hystrix 对比:Sentinel 功能更全、有 Dashboard、规则可动态配置,Hystrix 已停维。

    为什么这么回答

    必答"限流 + 熔断"两类核心功能。

    面试官会怎么追问
    • "限流和熔断区别?" → 限流是主动保护(防止系统被打挂);熔断是被动保护(下游故障时快速失败)。
    • "规则放哪里?" → 默认内存(重启失效);生产用 push 模式推到 Nacos / Apollo
    • "Dashboard 是必须的吗?" → 不是必须,但生产强烈推荐,能实时看 QPS、熔断状态、动态改规则。
    怎么回答能拿高分
    讲一个真实故事:"我们秒杀接口用 Sentinel QPS=2000 限流,超出的请求直接返回'系统繁忙',没把 DB 拖垮。"
    04

    支付与交易(8 题)

    ★★★★★
    JD 明确要求支付经验 · 必问

    第九章 支付核心 8 题

    你简历上有一卡通项目对接微信/支付宝/银联,这是核心基础。但 JD 还要求 Apple Pay / Google Pay / Stripe / PayPal,面试前补一下国际化思路。

    Q82 微信支付完整流程? ★★★★★ +
    标准答案(2 分钟)

    以 JSAPI / H5 支付为例,完整流程:

    • 1. 商户下单:用户在商户 APP / 网页选商品 → 后端创建订单(订单状态 = 待支付)。
    • 2. 调微信统一下单 API:商户后台调 https://api.mch.weixin.qq.com/pay/unifiedorder,传 appidmch_idout_trade_nototal_feenotify_url 等,用商户 key 做 MD5/HMAC-SHA256 签名
    • 3. 微信返回 prepay_id:商户后台收到 prepay_id 后,二次签名返回给前端。
    • 4. 前端调起支付:JSAPI 走 WeixinJSBridge.invoke;H5 走 https://wx.tenpay.com/cgi-bin/mmpayweb-bin/checkmweb
    • 5. 用户输入密码,微信扣款成功。
    • 6. 微信异步回调(notify_url):微信用 POST XML 推支付结果给商户,带签名
    • 7. 商户验签 + 处理:校验签名 + 校验订单金额 + 修改订单状态 + 返回 SUCCESS 给微信(告诉微信收到了)。
    • 8. 对账:第二天拉微信的对账单,和本地订单比对,查漏补缺。
    为什么这么回答

    这是支付类问题"基础题"。必答"统一下单 + 异步回调 + 验签"三件套。

    面试官会怎么追问
    • "为什么是异步回调不是同步?" → 同步需要用户保持页面不关闭,网络中断就丢;异步最可靠。
    • "回调地址必须公网吗?" → 是,微信要能 POST 过来。本地开发用 natapp / cpolar 内网穿透。
    • "签名用什么算法?" → MD5 / HMAC-SHA256。生产推荐 HMAC-SHA256,更安全。
    怎么回答能拿高分
    画一张时序图:用户 → 商户 → 微信 → 回调。视觉化最清晰。强调"异步回调 + 幂等 + 对账"三件套是支付核心。
    Q83 如何防止重复回调? ★★★★★ +
    标准答案(2 分钟)

    微信 / 支付宝回调是 "至少一次" 语义,重复回调 100% 发生,必须做幂等。三种幂等策略:

    • 1. 业务幂等(推荐)
      ① 订单状态机:订单 = "已支付" 时,重复回调直接返回 SUCCESS 不处理。
      ② 流水表:插入支付流水表,唯一键 out_trade_no,重复直接抛 DuplicateKey。
    • 2. Redis 幂等SET out_trade_no 1 NX EX 600,存在就跳过。快但要兜底
    • 3. 数据库乐观锁UPDATE order SET status='paid' WHERE out_trade_no=? AND status='unpaid'影响行数 = 0 就是重复

    支付系统的"金标准":乐观锁 + 业务状态机,双保险。

    为什么这么回答

    这是支付"第一高频题"。必答"幂等"和"状态机"。

    面试官会怎么追问
    • "为什么微信要重复回调?" → 网络抖动、回调超时、商户没及时返回 SUCCESS 都会重试;微信会重试 4 次左右。
    • "幂等和去重的区别?" → 去重是手段,幂等是目的;幂等 = 任何重复请求结果一致
    • "Redis 幂等的风险?" → Redis 重启会丢;要做兜底(DB 唯一约束)。
    怎么回答能拿高分
    讲一个案例:"我们用 UPDATE order SET status='paid' WHERE out_trade_no=? AND status='unpaid'影响行数 0 就直接 return SUCCESS2 年 0 重复扣款。"
    Q84 什么是支付幂等?怎么做? ★★★★★ +
    标准答案(2 分钟)

    定义:同一个支付请求,无论执行多少次,结果都和执行一次一样

    四层幂等保障:

    • 1. 唯一标识(idempotency key)
      ① 业务侧:out_trade_no(商户订单号)。
      ② 支付侧:微信 transaction_id
    • 2. 状态机
      订单状态:未支付 → 支付中 → 已支付 → 已退款
      只在 支付中 → 已支付 流转时处理,其他状态都跳过。
    • 3. 数据库约束
      ① 唯一索引:流水表 UNIQUE KEY (out_trade_no)
      ② 乐观锁:WHERE status='unpaid'
    • 4. 分布式锁
      高并发场景下,用 Redis 锁住 out_trade_no 5s,防止并发请求同时处理
    为什么这么回答

    幂等是分布式系统的通用问题,支付场景尤其严格。必答"四层保障"。

    面试官会怎么追问
    • "业务幂等和消息幂等一样吗?" → 思路一致:唯一键 + 状态机。MQ 消费侧用 messageId 幂等。
    • "分布式锁怎么实现?" → Redisson tryLock 5s,超时放弃。
    • "网络分区导致锁释放不了?" → 用 看门狗续期,业务执行 30s 锁也不会丢
    怎么回答能拿高分
    强调"四层幂等不是重复保险,是递进保障:状态机挡住 99% 重复,乐观锁挡住并发,唯一索引兜底,分布式锁防雪崩。"
    Q85 什么是补单?怎么实现? ★★★★★ +
    标准答案(2 分钟)

    补单:用户实际支付成功,但商户系统没收到回调(网络丢、回调超时、商户系统故障),需要主动查。

    补单流程:

    • 1. 定时任务
      每 1-5 分钟扫一次"待支付超过 X 分钟"的订单(如 5 分钟还没收到回调)。
    • 2. 主动查单
      https://api.mch.weixin.qq.com/pay/orderquery 查订单真实状态。
    • 3. 状态对比
      微信返回 SUCCESS + 本地是 未支付走正常支付成功流程(幂等处理)。
    • 4. 兜底对账
      T+1 拉对账单,和本地订单逐一比对,差的查漏补缺。

    关键点:

    • 补单逻辑要复用正常回调的处理(幂等代码共用)。
    • 对账是终极兜底前两道防线都漏的订单由它兜住
    为什么这么回答

    补单是"生产必备"。100% 的支付系统都有补单 + 对账机制。

    面试官会怎么追问
    • "定时任务怎么实现?" → Spring @Scheduled(单机) / XXL-Job / Elastic-Job(分布式,避免重复)。
    • "对账单格式是什么?" → CSV(微信)/ XLS(支付宝),按日期下载。
    • "金额对不上怎么办?" → 金额核对是金标准,本地金额 必须和微信一致,否则视为异常订单。
    怎么回答能拿高分
    画一张图:主动查单 + 被动回调 + 定时对账,三道防线让支付系统几乎不丢单。强调"对账是支付系统的生命线"。
    Q86 分布式事务:本地消息表 ★★★★☆ +
    标准答案(2 分钟)

    本地消息表是经典的最终一致性方案,由 eBay 提出。

    核心思路:

    • 1. 主业务 + 消息表 写同一个库(本地事务)。
      比如:创建订单 + 写 local_message 表,原子操作
    • 2. 后台轮询
      定时任务扫描 local_message 表,把"未发送"的消息发到 MQ。
    • 3. 标记已发送
      MQ 收到 ACK 后,UPDATE local_message SET sent=1
    • 4. 消费方幂等
      下游消费 + 幂等处理(见 Q84)。
    • 5. 重试机制
      消费失败的消息进死信队列,人工/自动重试

    优点:实现简单、不依赖 2PC 协议
    缺点:有延迟(秒级到分钟级)、要维护消息表

    为什么这么回答

    本地消息表是支付后置处理的"标准方案"。必答"业务 + 消息同库写"。

    面试官会怎么追问
    • "和 2PC 区别?" → 2PC 是同步阻塞的,性能差;本地消息表是异步的,最终一致
    • "MQ 发送失败怎么办?" → 定时任务继续扫描未发送的,指数退避重试
    • "消费方一直失败怎么办?" → 死信队列 + 告警 + 人工介入。
    怎么回答能拿高分
    画一张时序图:订单 + 消息表 → 轮询 → MQ → 下游消费 → ACK → 标记发送。强调"本地事务保证业务和消息原子性"。
    Q87 分布式事务方案对比 ★★★★☆ +
    标准答案(2 分钟)

    常见 4 种方案对比:

    • 1. 2PC(两阶段提交)
      ① 协调者 + 参与者,先准备后提交。
      强一致同步阻塞,性能差。
      ③ 适合金融核心,不推荐互联网
    • 2. TCC(Try-Confirm-Cancel)
      ① 三阶段:Try 预留 / Confirm 确认 / Cancel 取消。
      强一致业务侵入大(每个业务要写 3 个方法)。
      ③ 适合资金、库存扣减。
    • 3. 本地消息表
      ① 业务和消息表同库写,最终一致
      ② 实现简单,互联网最常用
    • 4. MQ 事务消息
      ① RocketMQ 支持,类似本地消息表,MQ 帮你做存储
      ② Kafka 不支持事务消息,要本地表。

    选型:支付后置业务(发短信、加积分) → 本地消息表;扣库存 → TCC;简单业务 → MQ 事务消息。

    为什么这么回答

    分布式事务是高级必问。必答 4 方案对比 + 选型。

    面试官会怎么追问
    • "Seata 是什么?" → 阿里开源的分布式事务框架,支持 AT / TCC / SAGA / XA 四种模式国内最主流
    • "最终一致性和强一致性怎么选?" → 大部分业务最终一致就够(支付后置业务几秒内一致 OK);资金核心要强一致(TCC / 2PC)。
    • "Saga 是什么?" → 长事务拆分多个短事务,每个失败有补偿动作,适合跨多服务的复杂流程。
    怎么回答能拿高分
    补一句经验:"我们用 Seata AT 模式做跨库事务,简单业务用 RocketMQ 事务消息不混用。"
    Q88 支付回调怎么处理? ★★★☆☆ +
    标准答案(2 分钟)

    支付回调处理 7 步:

    • 1. 验签:校验微信/支付宝的签名,防止伪造回调(重要!)。
    • 2. 幂等检查:用 out_trade_notransaction_id 判断是否已处理。
    • 3. 业务处理:修改订单状态、加积分、发短信等。
    • 4. 返回 SUCCESS必须返回 XML / JSON 给支付平台,告诉它"收到了"。
    • 5. 异步补偿:业务处理失败时,仍返回 SUCCESS(避免重复回调),业务侧进重试队列
    • 6. 幂等存储:用数据库/Redis 记录"已处理"标记。
    • 7. 监控告警:失败率超过阈值告警,人工介入
    为什么这么回答

    必答"验签 + 幂等 + 返回 SUCCESS"三件套。

    面试官会怎么追问
    • "为什么不直接调本地方法处理业务?" → 回调是 HTTP,必须快速返回,业务用 MQ 异步解耦。
    • "异步处理怎么保证不丢?" → 业务发到 MQ + 失败重试 + 死信队列告警。
    • "没返回 SUCCESS 会怎样?" → 支付平台会重试 4-8 次每次间隔递增,最终还是丢失。
    怎么回答能拿高分
    讲一个细节:"我们回调里只做核心状态修改 + 写消息到 MQ,其他业务(加积分、发短信)MQ 异步消费,回调 RT < 50ms。"
    Q89 海外支付(Apple Pay / Google Pay / Stripe)差异? ★★★☆☆ +
    标准答案(2 分钟)

    海外支付几个核心差异:

    • 1. 支付方式
      Apple Pay / Google Pay:本质是卡支付底层走卡组织(Visa/MasterCard)。
      Stripe / PayPal:聚合多种支付(卡 / 钱包 / 本地支付)。
      ③ 东南亚:本地支付 重要(PayNow、GCash、TrueMoney)。
    • 2. API 风格
      ① 微信/支付宝:XML + 同步回调(双向通信)。
      ② Stripe:REST JSON + Webhook(单向,Stripe 主动推)。
      ③ Apple Pay:客户端 token 化,商户拿 token 调收单方。
    • 3. 货币与汇率
      ① 多币种结算:商家收 USD,用户付本地币
      ② 汇率风险:动态汇率 vs 锁定汇率
      货币精度:不能用 double,要用 BigDecimal(最小货币单位,如分)。
    • 4. 合规
      PCI DSS:卡数据合规认证。
      GDPR:欧盟数据保护。
      3D Secure:欧洲强身份验证(SCA)。
    为什么这么回答

    JD 明确要求海外支付,必须答出和国内支付的差异。这是你的薄弱点。

    面试官会怎么追问
    • "Apple Pay 集成步骤?" → ① 申请 Merchant ID + 证书。② APP 用 PassKit 调 Apple Pay。③ 拿 token 调收单方(如 Stripe Adyen)。
    • "Stripe 怎么对账?" → Dashboard 拉 CSV;用 Stripe API 拉 charges 列表。
    • "3D Secure 是什么?" → 欧洲强制的卡支付二次验证,Stripe 自动支持,国内很少见。
    怎么回答能拿高分
    补一句:"国内支付我们走三方聚合(微信/支付宝/银联),海外推荐用 Stripe / Adyen覆盖 100+ 国家 + 100+ 货币,比自己对接省事。"
    05

    AI 与工程化(6 题)

    ★★★☆☆
    JD 明确要求 · 这块相对轻松

    第十章 AI 工具实战 6 题

    JD 明确要求"熟练使用 GitHub Copilot、Kite 等 AI 编码辅助工具"。你有 Cursor 经验,结合实际使用讲。

    Q90 用过哪些 AI 工具?怎么用? ★★★☆☆ +
    标准答案(2 分钟)

    我的 AI 工具栈:

    • Cursor:日常开发主力。Tab 补全 + Cmd K 重构 + Agent 模式(自动多文件修改 + 跑测试)。
    • GitHub Copilot:IDE 内行内补全,代码补全体验最好
    • ChatGPT / Claude:复杂问题、架构设计、长文本处理。
    • Perplexity:技术调研,带引用,避免幻觉。
    • v0 / bolt.new:前端原型生成(demo 阶段超快)。
    • 通义灵码 / 文心一言:国产化场景下用,信创要求

    典型使用场景:

    • 写新功能:Cursor Agent 模式"实现 XXX",自动生成 + 跑测试
    • 读老代码:选中 → Cmd L 解释。
    • 写文档:Cursor 重构 + 写 JavaDoc。
    • 查 bug:粘贴报错 → 让 AI 分析。
    为什么这么回答

    JD 明确要求,回答要具体到工具 + 场景,体现你"真用过"而不是"听说过"。

    面试官会怎么追问
    • "Cursor 和 Copilot 区别?" → Copilot 是补全,Cursor 是AI-first IDE(改 IDE 自身),Agent 模式更强。
    • "怎么避免 AI 写错代码?" → 一定要review,AI 经常幻觉(编造不存在的 API);跑测试是底线
    • "AI 写代码有版权问题吗?" → 法律上还在争议;企业内部用没问题,开源项目要小心。
    怎么回答能拿高分
    讲一个数字:"用 Cursor 后我们项目 CRUD 代码编写时间减半,复杂业务逻辑靠 AI 起 70%,自己改 30%。"
    Q91 Cursor 怎么用?有什么高效技巧? ★★★☆☆ +
    标准答案(2 分钟)

    Cursor 核心功能:

    • Tab 补全:基础功能,多行预测,识别上下文。
    • Cmd L(聊天):选中代码 + Cmd L → 解释 / 重构 / 改 bug。
    • Cmd K(行内编辑):选中 → Cmd K → 输入"加日志" / "改用 stream"等。
    • Cmd I(Composer / Agent)核心多文件编辑 + 自动跑命令。"在 repo 里找所有 TODO 并修复" / "为 UserService 写单元测试"。
    • @文件 / @符号:在 prompt 里 @FileName 让 AI 读指定文件上下文。
    • 项目级上下文:Cursor 自动索引项目,AI 理解你的代码

    高效技巧:

    • .cursorrules 文件定义项目规则(Java 17 + Spring Boot 3 + 阿里编码规范)。
    • Composer 模式做大改动,逐步 review
    • 复杂问题用 YOLO 模式让 AI 自动跑测试。
    为什么这么回答

    JD 明确要求,回答要展现"你真在用"不是"听说过"。

    面试官会怎么追问
    • "Composer 和 Chat 区别?" → Chat 是问答;Composer 是编辑代码,能多文件改动、跑命令。
    • "怎么让 AI 理解项目?" → Cursor 自动索引;.cursorrules 写项目规范;@关键文件给 AI 看。
    • "AI 写错了怎么办?" → YOLO 模式自动跑测试;手动 review;提交前必须自己看
    怎么回答能拿高分
    展示一个 .cursorrules 例子:"我在 Spring Boot 项目里写 Always use constructor injection, never field @AutowiredAI 自动遵守。"
    Q92 AI 如何提高开发效率? ★★★☆☆ +
    标准答案(2 分钟)

    AI 提效的5 个层级

    • 1. 代码补全(1.5x):Tab 自动补全,不用打字。CRUD 代码基本不用手写。
    • 2. 单文件重构(2x):选中代码 + Cmd K,"加日志 / 改 try-with-resources / 改 lambda"。
    • 3. 跨文件修改(3x):Composer 模式,"把这个类的所有 public 方法加 traceId 打印"。
    • 4. 自动测试(4x):让 AI 写单元测试,省去手写 80%,自己 review。
    • 5. 读老代码 / 写文档(5x)最有价值。AI 帮理解别人代码、生成 JavaDoc、画流程图。

    我们项目的提效实践:

    • CRUD + 单元测试:AI 起 70%,节省 1 个全职开发
    • 读开源项目源码:让 AI 解释 Spring Cloud Gateway 源码,学习周期减半
    • Bug 排查:贴报错 + 上下文,5 分钟给出可能原因
    为什么这么回答

    JD 明确要求。要给"具体数字"和"具体场景",不能只说"提高效率"。

    面试官会怎么追问
    • "AI 写的代码质量怎么样?" → 80% 一次过,20% 要微调复杂业务不如人
    • "AI 不能干什么?" → ① 业务决策;② 跨团队沟通;③ 架构权衡;④ 复杂算法设计。
    • "怎么让 AI 学会团队规范?" → .cursorrules / .github/copilot-instructions.md 把规范写成文件。
    怎么回答能拿高分
    讲一个真实数字:"我们项目组 4 个开发,用 AI 后每周节省 30+ 工时,相当于多了 0.7 个人力。"具体数字让面试官信服。
    Q93 MCP 是什么? ★★★☆☆ +
    标准答案(2 分钟)

    MCP(Model Context Protocol):Anthropic 提出的模型上下文协议让 LLM 调用外部工具 / 数据源 的标准协议。

    类比:

    • USB-C 协议:让各种外设(鼠标 / 键盘 / 显示器)插电脑,统一接口
    • MCP 协议:让各种工具(数据库 / GitHub / Slack)插 AI,统一接口

    架构:

    • MCP Host:AI 应用(Claude Desktop、Cursor)。
    • MCP Client:嵌入 Host 内部,和 Server 通信。
    • MCP Server:提供具体能力(如查数据库、读 GitHub、查日历)。

    类比 Function Calling:

    • Function Calling 是厂商私有(OpenAI、Anthropic 各一套)。
    • MCP 是开放标准一套协议所有 LLM 都能用
    为什么这么回答

    MCP 是 2024-2025 年新概念,JD 没明确提但提到 Dify 暗示 AI Agent 能力。答出 MCP + Agent 是加分项。

    面试官会怎么追问
    • "MCP 和 Function Calling 区别?" → Function Calling 是单次工具调用;MCP 是客户端-服务器标准支持多工具组合
    • "怎么开发 MCP Server?" → 用 @modelcontextprotocol/sdk(Node.js)或 Python SDK;定义工具 → 启动 Server → Host 接入。
    • "生产用 MCP 了吗?" → 还在早期,但Cursor、Claude Desktop 已经支持,未来会普及。
    怎么回答能拿高分
    画一张图:Host ↔ Client ↔ Server(多个 Server 并列),每个 Server 提供不同能力(DB / API / 文件系统)。视觉化最清楚。
    Q94 Dify 做过什么? ★★☆☆☆ +
    标准答案(2 分钟)

    Dify:开源的LLM 应用开发平台,可视化搭建 AI 工作流,类似 Coze

    核心能力:

    • 1. 可视化编排:拖拽节点搭 Agent / 工作流(输入 → LLM → 工具 → 输出)。
    • 2. 多模型支持:OpenAI、Claude、本地 Ollama、Qwen、DeepSeek。
    • 3. 知识库(RAG):上传文档 → 自动向量化 → 检索增强生成。
    • 4. 工具集成:内置搜索 / API / 数据库查询;支持自定义工具。
    • 5. 监控 + 调试:所有对话可追溯,生产必备

    典型场景:

    • 企业知识库问答:上传产品手册 → 用户问问题 → AI 检索回答。
    • 客服机器人:多轮对话 + 工单系统集成。
    • 代码助手:内部代码 + AI 解释。
    • 业务流程自动化:Agent + 工具调用,替代部分人工操作
    为什么这么回答

    Dify 是国内 AI 工具的代表,JD 提了要了解。如果没做过,就说"了解过 + 学习过"。

    面试官会怎么追问
    • "Dify 和 Coze 区别?" → Dify 开源 + 私有化部署;Coze 字节系,云服务为主。企业级首选 Dify。
    • "Dify 怎么私有化?" → Docker Compose / Kubernetes 部署,对接企业内部 LLM(如通义千问)。
    • "RAG 准确率不高怎么办?" → 文档预处理(分块策略)、向量模型选型、混合检索(向量 + 关键词)。
    怎么回答能拿高分
    如果你没做过,诚实说:"还没正式做项目,但我在本地用 Dify 搭过内部文档问答,准确率 80%,下一步想引入业务"。诚实 + 学习意愿是加分项。
    Q95 Spring AI 了解过吗? ★★☆☆☆ +
    标准答案(2 分钟)

    Spring AI:Spring 官方的 AI 集成框架,2024 年发布,对标 LangChain4j。

    核心能力:

    • 1. Chat Client:统一接口调用各种 LLM(OpenAI、Azure、Anthropic、Ollama、国产模型)。
    • 2. Prompt 模板PromptTemplate 管理 prompt,支持多轮对话
    • 3. RAG 支持:和 Spring 生态集成(PG / ES / Redis 做向量库)。
    • 4. Function Calling核心,让 LLM 调用 Java 方法。
    • 5. 国产模型:支持通义千问 / DeepSeek / 智谱等。

    和 LangChain4j 对比:

    • Spring AI:Spring 原生,和 Spring Boot 集成最自然,学习曲线低。
    • LangChain4j:LangChain 的 Java 版,社区更活跃支持更多模型
    为什么这么回答

    Spring AI 是 Java 程序员做 AI 的"原生选择"。JD 提了,即使没做项目也要了解。

    面试官会怎么追问
    • "Spring AI 适合什么场景?" → Java 后端 + AI 集成,企业内部 AI 应用和 Spring Cloud 无缝对接
    • "怎么调用国产模型?" → 配置 spring.ai.qwen.api-key + base-url和 OpenAI 一样
    • "Function Calling 怎么用?" → 用 @Tool 注解 Java 方法,LLM 自主决定调用
    怎么回答能拿高分
    提一个想法:"我准备在下个项目里用 Spring AI + 通义千问 + PG 向量设备操作智能问答用户问怎么配置设备,AI 检索文档 + 调 API 给出步骤。"展示规划能力。