中级后端工程师面试题
中级后端工程师面试的考察重点、常见题目,以及如何用即时 AI 反馈练习。
中级阶段的考察重点
考察并发、缓存、表结构设计,以及独立负责一个服务。
后端工程师常见面试题示例
- 技术面什么时候你会选 SQL 数据库而不是 NoSQL?为什么?好回答应覆盖
- 强一致性与ACID事务
- 复杂查询与JOIN
- 结构化数据与固定schema
- 成熟工具与生态
- NoSQL的适用场景对比
查看范例答案
选择SQL数据库的场景包括:当应用需要严格的一致性和ACID事务时,如金融系统;当数据关系复杂,需要多表联合查询;当数据模式稳定,SQL的schema约束有助于数据完整性。SQL还提供成熟的备份、恢复和监控工具。而NoSQL更适合高并发、灵活schema、非结构化数据或需要水平扩展的场景。例如,社交网络中的用户动态用NoSQL更高效。但注意,NewSQL正在融合两者优点,选择应基于具体读写模式、一致性要求和扩展性目标。常见错误是盲目使用NoSQL导致数据不一致或JOIN性能问题。
- 技术面数据库索引是如何工作的?它有哪些取舍?好回答应覆盖
- B+树结构
- O(log N)查找复杂度
- 空间与写入性能代价
- 覆盖索引与回表
- 索引选择性
查看范例答案
数据库索引通常基于B+树(如InnoDB)或哈希表。B+树索引通过非叶子节点存储键指针,叶子节点存储数据行,查找时间为O(log N),适合范围查询。索引优化读性能,但会占用额外空间(约30%-50%),并降低写速度,因为每次INSERT/UPDATE/DELETE需维护索引。同时,低选择性的索引(如性别)几乎无用。合理做法:为高频查询列建索引,利用复合索引最左前缀,并设计覆盖索引避免回表。需监控慢查询和索引使用情况,避免过度索引。
- 技术面讲讲你会怎么把一个支付接口设计成幂等的。好回答应覆盖
- 幂等键(Idempotency Key)
- 去重表与唯一约束
- 状态机与终态判定
- 超时重试处理
- 分布式原子性保证
查看范例答案
设计幂等支付接口的关键是使用幂等键去重。客户端每次请求携带唯一ID(UUID),服务端先检查该键是否已处理:若存在则返回之前结果,否则执行业务并持久化结果。常用实现方式:数据库唯一约束(UNIQUE KEY)或Redis setNX命令。支付操作需确保扣款和幂等键写入原子性,可使用本地事务或分布式事务。对于超时重试,服务端需能识别并返回相同响应。还需注意幂等键的过期时间与防篡改。状态机有助于管理支付状态,避免重复执行终态操作。常见陷阱:不同接口共用幂等键空间,需加接口前缀区分。
- 编程实现一个 get 和 put 都是 O(1) 的 LRU 缓存。好回答应覆盖
- 双向链表+哈希表
- 节点移动到头部
- 删除尾部节点
- O(1)复杂度
- 线程安全问题
查看范例答案
实现O(1)的LRU缓存需要结合哈希表与双向链表。哈希表提供O(1)的节点访问,双向链表维护访问顺序。get时若key存在则移动节点到头部并返回值,否则返回-1。put时若key存在则更新值并移动到头部;若key不存在则创建新节点加入头部,若容量超出则删除尾部节点。时间复杂度均为O(1),空间复杂度为O(capacity)。需要注意并发安全,可加锁或使用collections.OrderedDict。下面给出Python实现。
参考代码python class ListNode: def __init__(self, key=0, value=0): self.key = key self.value = value self.prev = None self.next = None class LRUCache: def __init__(self, capacity: int): self.capacity = capacity self.cache = {} # key -> ListNode # 虚拟头尾节点 self.head = ListNode() self.tail = ListNode() self.head.next = self.tail self.tail.prev = self.head def _remove_node(self, node): node.prev.next = node.next node.next.prev = node.prev def _add_to_head(self, node): node.next = self.head.next node.prev = self.head self.head.next.prev = node self.head.next = node def get(self, key: int) -> int: if key in self.cache: node = self.cache[key] self._remove_node(node) self._add_to_head(node) return node.value return -1 def put(self, key: int, value: int) -> None: if key in self.cache: node = self.cache[key] node.value = value self._remove_node(node) self._add_to_head(node) else: if len(self.cache) >= self.capacity: # 删除尾部节点 tail_node = self.tail.prev self._remove_node(tail_node) del self.cache[tail_node.key] new_node = ListNode(key, value) self.cache[key] = new_node self._add_to_head(new_node) - 编程给定一个事件流,为 API 设计一个限流器。好回答应覆盖
- 令牌桶与漏桶算法
- 滑动窗口计数器
- Redis Sorted Set实现
- 分布式限流与一致性
- HTTP 429响应
查看范例答案
为API设计限流器常用令牌桶或滑动窗口算法。令牌桶允许突发,但需控制速率和容量;滑动窗口通过记录每个请求时间戳,窗口内请求数超阈值则拒绝。实现可基于Redis Sorted Set:key为用户/IP,成员为时间戳,每次请求前移除窗口外记录,统计窗口内数量。分布式环境下需考虑Redis性能,可采用本地缓存配合同步或使用Redis Cluster。限流响应返回429状态码和Retry-After头。常见陷阱是窗口边界突刺,滑动窗口能缓解。还应设置多级限流(网关+业务),并监控限流命中率。
- 系统设计设计一个能处理数十亿次跳转的短链服务。好回答应覆盖
- 短码生成策略(雪花算法+Base62)
- 分布式KV存储(Redis+MySQL)
- 302重定向与统计
- CDN缓存与预取
- 防攻击与高可用
查看范例答案
设计支持数十亿跳转的短链服务:首先,短码生成使用分布式唯一ID(如雪花算法)Base62编码得到6-8字符,或取哈希前缀并通过唯一约束解决碰撞。存储采用Redis缓存热数据,MySQL持久化映射,读多写少。跳转使用HTTP 302重定向,便于更新和统计。为提升性能,CDN缓存重定向响应,并预取热门短链到本地内存。高可用方面,短码生成服务无状态可水平扩展,存储分片(如一致性哈希)。防攻击措施:限制生成频率,过滤恶意长链。监控QPS和延迟,定期对账。陷阱:短码碰撞、存储热点、跳转延迟。容量估算:数十亿短链,每条约100字节,总存储约几十TB,需分布式数据库。
- 行为面讲一次你参与处理的线上故障,以及事后你做了哪些改进。好回答应覆盖
- 故障描述(支付回调超时)
- 根因分析(连接池过小)
- 修复措施(调整连接池大小)
- 改进(监控、熔断、压测)
- 预防复发(审批流程、演练)
查看范例答案
在一次线上故障中,支付回调接口频繁超时,导致订单状态不同步。通过APM追踪发现,原因是数据库连接池配置过小(20),高峰时期请求排队等待连接超时。我们立即将连接池大小调整到100并重启服务恢复。事后改进包括:添加数据库连接数监控与告警阈值(80%使用率触发);为关键接口设计熔断降级,当依赖异常时返回兜底响应;进行压力测试验证新配置;补充故障演练和变更审批流程。这次经历让我认识到容量规划与监控的重要性,后续再未出现类似问题。
- 行为面讲一次你不得不做出艰难的数据一致性取舍的经历。好回答应覆盖
- 最终一致性 vs 强一致性
- CAP权衡
- 异步消息与本地消息表
- 补偿机制与对账
- 业务容忍度分析
查看范例答案
在设计跨系统订单同步时,最初采用分布式事务(XA)追求强一致性,但导致吞吐量骤降且协调者单点。权衡后选择最终一致性:使用消息队列异步同步,结合本地消息表确保消息不丢。失败时通过补偿任务重试,并在展示层提示数据可能延迟。我们妥协了短期不一致(秒级),但换来了高性能和高可用。后续增加对账系统定期修复不一致。这个经历让我深刻理解CAP理论,一致性选择应基于业务容忍度,例如支付核心需强一致,而报表可接受最终一致。
面试官重点考察什么
数据建模
关系型 vs. NoSQL、索引、范式化与事务。
API 设计
REST/GraphQL/gRPC、幂等性、分页与版本管理。
并发
锁、竞态条件、队列与最终一致性。
系统设计
缓存、分片、复制,以及规模化下的故障处理。
算法
复杂度分析与实际的数据结构选型。
如何准备
- 动手设计前先说清假设和约束——面试官看重你对边界的界定。
- 编程题答案务必分析时间和空间复杂度。
- 做系统设计要主导节奏:需求、API、数据模型、规模、故障模式。
常见问题
后端面试会出哪些系统设计题?
常见的有设计短链服务、限流器、信息流或聊天系统,并讨论缓存、分片和一致性。
后端面试需要多少算法功底?
尽管日常工作更偏系统,但大多数公司仍会安排一到两轮聚焦数据结构和复杂度的编程面。
如何高效准备后端面试?
把刷题和口头的系统设计练习结合起来,再做模拟面试,让自己能开口为各种取舍辩护。
用即时 AI 反馈刷后端工程师面试题
Offersly 会根据你的简历和目标岗位定制一场模拟面试,并从相关性、深度、清晰度和正确性四个维度为每个回答打分。