拼多多后端开发面经 · 2024 秋招
岗位:后端开发工程师(多多买菜供应链)
背景:211 硕士,Java 技术栈,1 段实习
结果:OC,年薪 42w+(Base + 期权,11-11-6 工作制)
时间线:9.1 投递 → 9.5 一面 → 9.15 四面 → 9.20 OC
拼多多面试风格
拼多多以高强度、高薪资、长工时著称,面试也有其鲜明特点:
- 算法占比高:每轮 1–2 道算法,对编码要求严格
- 工程基础深:Redis 源码、JVM、MySQL InnoDB 底层是常客
- 场景题务实:偏向电商场景(库存、订单、物流),与实际业务结合紧密
- 压力面倾向:面试官会直接质疑你的方案,考察抗压能力
一面(9.5):算法 + 基础
面试官:多多买菜商品服务组
时长:60 分钟
算法题 1:Binary Tree Maximum Path Sum(LeetCode 124)
二叉树中的最大路径和,路径不必经过根节点。
思路:树形 DP,后序遍历,每个节点返回"以该节点为端点的最大路径和"。
class Solution:
def maxPathSum(self, root):
self.max_sum = float('-inf')
def dfs(node):
if not node:
return 0
left = max(0, dfs(node.left))
right = max(0, dfs(node.right))
# 经过当前节点的最大路径和
self.max_sum = max(self.max_sum, left + right + node.val)
# 返回以当前节点为端点的最大路径
return node.val + max(left, right)
dfs(root)
return self.max_sum
追问:
- “如果路径必须从一个叶子到另一个叶子呢?” → 不允许负数贡献,必须左右都选
- “时间复杂度为什么一定是 O(n)?” → 每个节点只访问一次
算法题 2:Longest Substring Without Repeating Characters(LeetCode 3 变体)
最长无重复子串。但可以允许最多替换 k 个字符。
实际上是 LeetCode 424 和 3 的结合题。我用滑动窗口 + 哈希表写了 O(n) 解法。
Redis 深度提问(15 分钟)
“Redis 的 SDS(Simple Dynamic String)相比 C 字符串有什么优势?”
- O(1) 获取长度、避免缓冲区溢出、减少内存重分配、二进制安全
“Redis 持久化 AOF 和 RDB 的区别?怎么选型?”
- RDB:快照,恢复快,可能丢数据
- AOF:日志,数据完整,恢复慢
- 混合模式(Redis 4.0+):RDB 全量 + AOF 增量
“Redis 的过期键删除策略?”
- 惰性删除:访问时检查过期
- 定期删除:每隔一段时间随机抽查一批
二面(9.8):项目 + 算法
面试官:多多买菜订单组技术 lead
时长:70 分钟
项目深挖(25 分钟)
我实习做的是电商订单系统:
“你的订单表数据量多大?怎么分表的?”
- 按
user_id取模分 128 张表,避免热点
- 按
“如果用户查历史订单需要跨分表聚合,怎么办?”
- 走 ES 做宽表查询,MySQL 只负责单分表精确查询
“订单超时自动取消怎么实现?”
- 我说用 RabbitMQ 延迟队列
- 追问"如果 MQ 挂了怎么办?" → 兜底:定时任务扫描 + 数据库状态机兜底
“大促时订单量翻 10 倍,哪里会先崩?怎么预防?”
- 数据库连接池打满 → 提前扩容 + 读写分离
- 消息队列堆积 → 增加消费者 + 批量消费
- 库存扣减热点 → Redis 分段锁 + 本地缓存预热
算法题:Account Merge(LeetCode 721)
根据邮箱关联合并账户。正好是我之前准备的并查集题目。
from collections import defaultdict
def accountsMerge(accounts):
n = len(accounts)
uf = UnionFind(n)
email_to_id = {}
# 构建并查集:同一邮箱关联的用户合并
for i, account in enumerate(accounts):
for email in account[1:]:
if email in email_to_id:
uf.union(i, email_to_id[email])
else:
email_to_id[email] = i
# 按 root 分组收集邮箱
id_to_emails = defaultdict(set)
for i, account in enumerate(accounts):
root = uf.find(i)
for email in account[1:]:
id_to_emails[root].add(email)
return [[accounts[i][0]] + sorted(emails) for i, emails in id_to_emails.items()]
三面(9.12):架构 + 算法
面试官:多多买菜仓储物流方向架构师
时长:75 分钟
算法题:Sliding Window Maximum(LeetCode 239)
滑动窗口最大值。
单调双端队列解法:
from collections import deque
def maxSlidingWindow(nums, k):
result = []
dq = deque() # 存储索引,保持递减
for i, num in enumerate(nums):
# 移除不在窗口内的
while dq and dq[0] <= i - k:
dq.popleft()
# 移除比当前小的(它们不可能成为最大值)
while dq and nums[dq[-1]] < num:
dq.pop()
dq.append(i)
# 窗口形成后开始输出
if i >= k - 1:
result.append(nums[dq[0]])
return result
追问:
- “为什么是单调递减而不是递增?” → 队首保存最大值
- “如果要求滑动窗口的中位数呢?” → 双堆(最大堆 + 最小堆)或 有序集合
场景设计:供应商库存同步
多多买菜的供应商众多,要求设计一个实时库存同步系统:
我的方案:
- 异步同步:供应商系统推送库存变更 → Kafka → 消费更新 Redis / MySQL
- 幂等性:库存变更带版本号或 timestamp,防止乱序覆盖
- 一致性校验:定时对账(供应商系统 vs 我方系统)
- 降级策略:供应商接口挂了,沿用上次同步数据,标记为"可能不准确"
追问:
- “如果两个供应商同时改了同一个商品的库存怎么办?” → 商品维度加分布式锁
- “供应商系统特别慢,拖垮我们的接口怎么办?” → 异步线程池隔离 + 熔断 + 超时控制
四面(9.15):HR 面
时长:25 分钟
- “为什么选拼多多?能接受我们的工作强度吗?”
- 我回答看重技术成长和业务规模,对 11-11-6 有心理准备
- “你现在的 offer 情况?”
- 如实说了有美团和字节在流程中
- “期望薪资?”
- 报了范围,HR 说会给到有竞争力的 package
拼多多 HR 面的特点:直接谈钱、谈强度,不搞虚的。
复盘与建议
拼多多后端面试通关密码
| 维度 | 权重 | 准备要点 |
|---|---|---|
| 算法 | 45% | LeetCode Hot 100 必须会,hard 题需要掌握 |
| 项目/场景 | 30% | 电商场景(库存、订单、物流)的技术方案 |
| 基础 | 20% | Redis 源码、MySQL 事务、JVM |
| 抗压 | 5% | 面试官质疑时保持冷静、有理有据 |
重点准备清单
- 树形 DP:二叉树最大路径和、打家劫舍 III、 House Robber III
- 并查集:省份数量、冗余连接、账户合并
- 滑动窗口:最大值、中位数、长度至少 K 的最大子数组和
- Redis 源码级理解:SDS、跳表、Dict、AOF/RDB 混合持久化
- MySQL:InnoDB 索引结构、MVCC、幻读解决方案、分库分表策略
- 电商场景:库存扣减、订单超时、幂等性、对账
拼多多面试算法难度较高,且面试节奏很快。建议提前把 hard 难度的树形 DP 和并查集题目练熟,工程基础要有源码级理解。
继续阅读
探索更多技术文章
浏览归档,发现更多关于系统设计、工具链和工程实践的内容。