Skip to content

IB · 后端面试题 ​

C 后端域已有 23 篇系统文章,本篇绝大部分题目直接链过去(现已全部覆盖), 只补充 C 域没有的(CAP/BASE、分布式 ID、分布式锁,以及"前端要懂后端到哪一层"这类跨域判断题)。

📌 服务对象是前端 / 转全栈:目标不是背后端八股,而是能对等地和后端对话、能把全栈面试题答出深度。 章序即知识顺序:并发模型 → API → 鉴权 → 数据与事务 → 异步 → 架构 → 性能 → 分布式 → AI 服务端 → Java 协作。


高频 Top 10(不会的话优先补这些) ​

#题目去哪补
1同步 / 异步 / 多进程 / 多线程,Node / Python / Go 分别怎么并发C01 / C07
2Session vs JWT:JWT 最大的代价是什么C03
3幂等怎么做,重复请求怎么不重复扣款C04
4事务隔离级别:脏读 / 不可重复读 / 幻读分别是什么C04
5缓存穿透 / 击穿 / 雪崩,以及缓存一致性怎么保证C13
6什么时候该用 MQ,消息会丢吗、重复消费怎么办C06
7限流 / 熔断 / 降级的区别C05
8连接池是什么,耗尽会出现什么症状C07
9分布式事务:SAGA / 本地消息表怎么选C04
10前端做全栈,后端要先学哪些、学多深C00 / C22

一、运行时与并发模型 ​

Q:Node 是单线程,为什么还能扛高并发?和 Python / Go 的差异?(内联要点)

A:Node 用单线程事件循环 + 非阻塞 I/O:一个线程处理所有连接,I/O 交给系统,完成再回调。 高并发来自"不阻塞",而不是多线程。差异:

运行时并发模型最大的坑
Node单线程事件循环CPU 密集任务卡死整个进程
Python线程 + GILCPU 密集多线程没用,要多进程;async 里不能调阻塞库
Gogoroutine(协程)起步成本高,生态比 Python/Node 小

→ 详见 C01 · 运行时选型

Q:什么是连接池?为什么要池化?连接池耗尽了会怎样?

A:连接池是复用一组已建立的连接,避免每次请求都走"建连 → 用 → 断开"(建连开销大)。 耗尽的症状通常是:接口大面积超时、数据库 max_connections 打满、日志报"获取连接超时"。 排查顺序:连接是否泄漏(没归还)→ 池大小 vs 数据库上限 → 慢 SQL 占着连接不放。

→ 详见 C07 · 性能与容量

Q:async def 路由里调了同步阻塞库,会发生什么?(内联要点)

A:会卡住整个事件循环——一个请求慢,其他所有请求跟着慢。 FastAPI 里普通 def 路由会自动进线程池,而 async def 里的同步调用直接阻塞循环。

→ 详见 C07 / C09 · 语言上手

Q:线程 / 进程 / 协程,分别该什么时候用?

A:进程隔离最彻底但最重;线程共享内存、适合 I/O 密集但有竞争;协程是用户态调度、最轻, 适合大量 I/O 并发。前端类比:浏览器 tab ≈ 进程,worker ≈ 线程,async/await ≈ 协程。 Node/Python 的真相是"一个线程 + 协程调度",Java/Go 则各自有线程/协程池模型。

→ 详见 C01 / IO · 计算机基础


二、API 设计与错误处理 ​

Q:REST / gRPC / GraphQL 怎么选?(内联要点)

A:对外公开 API 和浏览器直接调 → REST(生态最好);内部服务间、高吞吐 → gRPC(HTTP/2 + 二进制 + 强契约); 客户端要灵活裁剪字段且调用方可控 → GraphQL(代价是缓存和 N+1 难做)。默认 REST,别为"看起来高级"上另两个。

→ 详见 C02 · API 设计

Q:状态码怎么用才不混乱?

A:按语义分层:2xx 成功、4xx 客户端错误、5xx 服务端错误。常见坑: 404 到底是不存在还是没权限(很多人用 404 隐藏资源,会导致排查混乱);400 vs 422 vs 409 语义要固定; 5xx 不能乱用,服务端异常统一 500 + 错误体,别把业务校验错误也返回 500。

→ 详见 C02 / C12 · 错误处理日志与配置

Q:接口版本怎么演进,才能不破坏老客户端?

A:核心原则是响应字段只增不删、语义不偷换。版本策略:URL 版本(/v2/...,清晰但会扩散)、 Header 版本(干净但调试麻烦)。老客户端要兼容就并行提供新版本,不要原地改。

→ 详见 C02

Q:统一错误体怎么设计?

A:让前端能"机器可读地判断错误类型",而不是解析文案。常用结构:

json
{ "code": "ORDER_NOT_FOUND", "message": "订单不存在", "details": { "orderId": "123" } }

code 给程序判断、message 给人看、details 带上下文。日志里打 request_id,前端报错能一键关联。

→ 详见 C12


三、认证授权与安全 ​

Q:Session 和 JWT 怎么选?JWT 最大的代价是什么?(内联要点)

A:JWT 是无状态的,代价是签发后服务端无法即时作废——你要黑名单/白名单/改密钥, 一旦引入这些就是"服务端状态",无状态优势就没了。选型: 需要即时失效、强管控(封号、退出登录立即生效)→ Session;服务间调用、跨域 SSO、短期授权 → JWT;拿不准 → Session。

→ 详见 C03 · 认证与授权

Q:Token 该存哪?为什么不用 localStorage?(内联要点)

A:首选 HttpOnly Cookie + Secure + SameSite——JS 读不到,XSS 拿不走;代价是要防 CSRF。 localStorage 对 JS 完全开放,XSS 一次就泄露。这是前后端都要懂的安全共识。

→ 详见 C03 / A08 · 前端安全

Q:OAuth2 什么时候才需要?

A:OAuth2 是授权框架,解决"让第三方拿到有限权限"。自己的系统、自己的用户 → 自己发 Session/JWT 就够了; 只有做第三方登录、开放平台时才上。常见误解:"微信登录"其实是 OAuth2 + OIDC——OAuth2 只解决"授权", 不解决"你是谁",后者靠 OIDC 的 id_token。

→ 详见 C03

Q:越权(IDOR)怎么防?

A:只做"登录校验"不够——要校验当前用户对目标资源是否有权(order.user_id == current_user.id)。 权限判断必须在服务端数据层兜底,不能只靠前端隐藏按钮。多租户系统尤其要防"换个 id 就看别人数据"。

→ 详见 C03 / C16 · 安全纵深

Q:SQL 注入、SSRF、密钥管理各怎么防?(内联要点)

A:SQL 注入 → 永远用参数化查询/ORM,禁止字符串拼 SQL;SSRF → 服务端发起请求时校验目标 IP/域名白名单; 密钥 → 不进代码库、走环境变量/密钥管理服务,日志里脱敏。安全是"纵深"不是单个手段。

→ 详见 C16


四、数据访问、事务与缓存 ​

Q:ORM 还是手写 SQL?N+1 问题怎么避免?

A:常规 CRUD 用 ORM(安全、快),复杂查询用 SQL。N+1 是"查一次列表,再逐条查关联", 症状是接口慢、SQL 数量爆炸。解法:预加载/联查(JOIN 或一次 IN 查回关联数据)。

→ 详见 C10 · 数据建模与数据访问 / C07

Q:事务隔离级别:脏读 / 不可重复读 / 幻读?(内联要点)

A:脏读读到别人未提交的数据;不可重复读同一条记录两次读结果不同(别人改了并提交); 幻读同一条件两次查,行数变了(别人插入了)。级别从松到严:读未提交 → 读已提交 → 可重复读 → 串行化。 MySQL 默认可重复读,Postgres 默认读已提交。别一上来就升到串行化,先看业务是否需要。

→ 详见 C04 · 一致性与事务

Q:幂等怎么做?重复请求怎么不重复扣款?(内联要点)

A:幂等三件套:客户端传幂等键 + 数据库唯一约束 + 撞唯一约束时返回已存在的结果。 只"先查再写"是不够的——并发下两个请求都查不到,就都写了。必须用唯一约束兜底。

→ 详见 C04

Q:分布式事务:SAGA 和本地消息表怎么选?

A:跨服务不能靠数据库事务。SAGA(编排/协同 + 补偿)是默认答案:下单 → 扣款 → 减库存, 哪步失败就反向补偿。本地消息表:业务表 + 消息表同库一个事务,靠轮询/订阅发消息,配合消费幂等。 核心是重试 + 幂等 + 补偿三件套,最终一致不是"不保证",而是"失败了还能补到一致"。

→ 详见 C04

Q:缓存穿透 / 击穿 / 雪崩的区别和各自解法?

A:一句话记忆:穿透是"查了不存在的数据",击穿是"一个热点 key 过期瞬间",雪崩是"大量 key 同时过期"。 分别用空值缓存、互斥锁、随机过期时间防。缓存一致性默认先更新 DB 再删缓存,依赖 TTL 兜底。

→ 详见 C13 · 缓存与 Redis

Q:Redis 能做分布式锁吗?要注意什么?

A:能,SET key value NX EX 获取、设过期时间防持有方挂了死锁、释放用 Lua 保证"是自己的锁才删"。 分布式锁只解决跨实例互斥,锁必须有过期时间。

→ 详见 C13


五、异步、消息与定时任务 ​

Q:什么时候该用 MQ?怎么选型?

A:判据是"调用方不等待结果也能正确"且慢操作拖慢主流程:发通知、转码、算积分适合异步,下单成败必须同步返回。 好处是接口变快、削峰填谷、故障隔离。选型看吞吐、顺序、延迟和运维成本(Kafka 高吞吐 / RabbitMQ 灵活路由 / Redis Stream 轻量)。

→ 详见 C06 · 异步与消息

Q:消息会丢吗?重复消费怎么办?

A:消息丢失分三段:生产(确认机制)、Broker(持久化/副本)、消费(确认后消费)。 重复消费靠消费幂等:按业务键去重,而不是"保证只投一次"。

→ 详见 C06

Q:定时任务从单机 cron 到分布式调度怎么演进?

A:单机 cron 只在一台机器、无状态、不能集群。要分布式调度(如 XXL-Job):任务注册到调度中心、 分片执行、失败重试、执行日志可查。它解决的三个问题是"谁来触发、触发哪个实例、失败怎么重试"。

→ 详见 C15 · 定时任务


六、架构、部署与稳定性 ​

Q:什么时候该拆微服务?

A:默认不拆,先做模块化单体。拆的时机:团队多人分工边界清晰、部署/扩容出现瓶颈、模块要独立伸缩。 拆的代价是分布式事务、链路追踪、服务发现、环境复杂度全来了——微服务的成本在运维不在代码。

→ 详见 C05 · 服务架构

Q:限流 / 熔断 / 降级有什么区别?(内联要点)

A:限流:主动拒绝超出容量的请求(令牌桶/漏桶),保护自己;熔断:下游故障时快速失败, 不再发请求(类似前端"接口挂了先别打");降级:兜底返回,比如推荐服务挂了返回缓存/空列表。 三者的共同点是"系统扛不住时要有预案",而不是硬撑。

→ 详见 C05

Q:Docker 部署到底解决什么问题?

A:解决**"在我机器上能跑"**——把运行时、依赖、配置一起打包成镜像,环境一致、秒级启停、方便扩容。 前端类比:docker-compose 像把"node_modules + 环境变量 + 启动命令"打包,Dockerfile 像 package.json 的升级版。

→ 详见 C17 · 部署与 CI/CD

Q:CI/CD 里 CI 和 CD 分别做什么?

A:CI(持续集成):提交即自动构建 + 测试,问题尽早暴露;CD(持续交付/部署):构建产物自动发布到环境。 前端视角:CI ≈ 每次 push 自动跑 lint + 单测,CD ≈ 自动部署到预览/生产。

→ 详见 C17


七、性能、可观测性与测试 ​

Q:服务变慢了,先查哪里?

A:先测后优化,一次只改一个变量。按概率排序:数据库慢查询/缺索引 → N+1 → 热点无缓存 → 连接池耗尽 → 阻塞调用。 "语言慢"几乎从来不是瓶颈。没有压测数据支撑的"优化"是玄学。

→ 详见 C07

Q:日志 / 指标 / 链路追踪,分别解决什么问题?

A:日志回答"发生了什么"(报错、上下文);指标回答"整体健不健康"(QPS、延迟、错误率、CPU/内存); 链路追踪回答"一次请求跨了哪些服务、慢在哪一段"(trace_id 贯穿)。前端类比:console + 性能面板 + Network 瀑布图。

→ 详见 C08 · 可观测性

Q:后端测试怎么分层?

A:单元测试(函数/类,快)→ 接口测试(HTTP 层,验证契约)→ 集成测试(带真实 DB/MQ)。 核心是接口测试兜住"前后端契约"——这正好是前端最该盯的一层。

→ 详见 C11 · 后端测试实战


八、分布式高频补充(内联,C 域未展开) ​

Q:CAP 定理怎么理解?(内联)

A:分布式系统在 C 一致性、A 可用性、P 分区容忍 三者里最多满足两个,且网络分区(P)是必然发生的, 所以实际是在 C 与 A 之间选:选 C → 分区时拒绝服务(写失败也不给旧数据);选 A → 分区时继续服务但可能不一致。 CP vs AP 不是"哪个更好",而是看业务:账务/库存要 CP(宁可报错不可算错),推荐流、列表、计数可 AP(短暂不一致可接受)。 这正是 C 域"一致性光谱 + 最终一致"要讲的:90% 的问题先问"该用哪档"。

→ 落地参考 C04 · 一致性与事务

Q:BASE 是什么?(内联)

A:Basically Available(基本可用)+ Soft state(软状态)+ Eventually consistent(最终一致), 是"不用强一致也要交付"的工程约束。理解:系统基本可用、允许中间状态、靠补偿最终收敛。 它不是"随便"的代名词——必须有重试 + 幂等 + 补偿把状态补对。

→ 落地参考 C04

Q:分布式 ID 怎么做?(内联)

A:要求:全局唯一、尽量递增、高性能。常用方案:

方案优点缺点
数据库自增/号段简单、递增依赖 DB、有单点
雪花算法(Snowflake)时间戳+机器+序号,本地生成、趋势递增依赖时钟,时钟回拨会重复
UUID完全本地生成无顺序、存储大、索引进退
Redis INCR / 号段快、可控制引入 Redis 依赖

Q:分布式锁除了 Redis 还有什么坑?(内联)

A:核心坑是"锁的持有者挂了自己不会主动释放"——所以必须设过期;还要防"持锁线程卡顿,锁过期后别人拿到, 原持有者回来删了别人的锁"——所以删除前要校验 value 是不是自己的(Lua 保证原子)。

→ 详见 C13 · 缓存与 Redis


九、AI 服务端 ​

Q:AI 能力怎么做成后端服务?

A:把"调 LLM"封装成 API:输入校验 → 组装 Prompt → 调模型(流式/非流式)→ 结构化输出 → 缓存/限流/成本控制 → 日志。 重点不是"能调通",而是可观测、可限流、成本可控。

→ 详见 C19 · AI 服务端集成

Q:流式输出和普通请求有什么不同?

A:LLM 输出是逐个 token 生成的,流式(SSE)让用户边生成边看到,避免长时间白等。 服务端要处理:连接断开、重连、流式与日志/计费的对齐。

→ 详见 C19

Q:RAG 从"笔记本能跑"到"生产 API",难点在哪?

A:检索质量(分块/混合检索/重排)、数据更新与删除、引用溯源、缓存降级、评测回归—— 难点是把"demo"变成"可维护、可评测、不出错"的服务。

→ 详见 C20 · RAG 与 Agent 服务端落地

Q:AI 服务怎么控成本、保可靠?

A:限流(谁都能调就是失控)、缓存(相同问题不重复调模型)、模型路由(简单任务用小模型)、 输出 token 预算(输出单价高)、评测回归(改 Prompt 别改坏行为)。

→ 详见 C21 · AI 服务可靠性


十、Java/Spring 协作与前端转后端 ​

Q:前端怎么看懂 Java/Spring 项目?

A:用前端类比翻译:Maven ≈ npm(依赖管理)、Bean/IOC 容器 ≈ DI 容器(对象创建与注入)、 Spring Boot 启动器 ≈ 脚手架 + 约定优于配置、Controller/Service/Mapper 分层 ≈ 组件/业务层/Repository、 AOP ≈ 中间件(日志/事务/权限切面)。先看入口、再看分层、最后看配置,别陷进注解细节。

→ 详见 C18 · 看懂 Java 与 Spring

Q:前后端联调,最容易在哪里翻车?

A:契约——字段命名、类型、状态码语义、错误体结构、分页格式、时间格式。前端 DTO 类型服从后端契约, 不要在 UI 层改类型或加宽松转换器。契约定好,联调从"猜"变成"对表"。

→ 详见 C02 · API 设计 / C18

Q:前端做全栈,后端要先学哪些、学多深?

A:先分清三层深度:深入(一门语言 + HTTP 框架 + SQL/ORM 使用侧 + 鉴权 + 测试 + 部署 + LLM 服务端化, 能独立交付);掌握(事务与幂等、缓存、异步消息、定时任务、日志/链路、性能诊断,懂原理能排查); 了解(K8s、分布式事务深水区、大数据、服务网格,知道何时去学)。别一上来啃 K8s 和分布式中间件。

→ 详见 C00 · 后端学习全景 / C22 · 站外资源


自检清单 ​

  • [ ] 高频 Top 10 全部能讲清楚,并知道每题的 C 域出处
  • [ ] 能说清 Node/Python/Go 三种并发模型的差异与各自的坑
  • [ ] 能讲清 Session/JWT/OAuth2 三者的适用边界,以及 token 存储选型
  • [ ] 能说清四种事务隔离级别,以及"为什么跨服务不能靠数据库事务"
  • [ ] 能讲清幂等三件套、SAGA/本地消息表、最终一致与补偿
  • [ ] 能区分缓存穿透/击穿/雪崩并说出各自解法
  • [ ] 能区分限流/熔断/降级,并讲清连接池与分布式锁的坑
  • [ ] 能说出 CAP 的取舍逻辑与分布式 ID 的常用方案
  • [ ] 能把"AI 能力做成后端服务"讲出工程要点(限流/缓存/成本/评测)
  • [ ] 能用前端类比翻译一个 Java/Spring 项目的基本结构

相关文档 ​