Skip to content

拼多多测试开发面经(按一二三面拆分 · 可背诵标准回答版)


一、一面(45~60 分钟 · 项目 + 基础 + 手撕代码)

1. 自我介绍

面试官好,我叫 XXX,XX 学校 XX 专业硕士/本科在读。

我有 X 段测试相关经历:
- 在 XX 公司实习期间,负责 XX 模块的功能测试和接口自动化测试

技术栈方面,我熟悉 Python/Java、MySQL、Redis,能独立编写测试脚本。
拼多多的企业文化和发展速度我很认可,希望能加入拼多多团队。

2. 项目介绍(会被深挖细节)

背景:该系统主要解决 XX 问题,用户规模 XX。
任务:我负责 XX 模块的测试用例设计和执行。
行动:
  1. 用 Xmind 梳理测试点,设计测试用例 XX 条
  2. 搭建接口自动化框架,覆盖核心接口
  3. 接入 Jenkins,实现持续集成
结果:主流程覆盖率达到 XX%,回归时间从 X 小时缩短到 X 分钟。

常见追问:

  • "项目的技术难点是什么?怎么解决的?"
  • "超卖问题遇到过吗?怎么处理的?"
  • "缓存和数据库不一致怎么办?"

3. 缓存穿透、缓存击穿、缓存雪崩

缓存穿透:查询不存在的数据,每次都打到数据库
解决:布隆过滤器 / 缓存空值 / 参数校验

缓存击穿:热点 key 过期瞬间,大量请求打到数据库
解决:互斥锁 / 永不过期 + 异步更新 / 热点数据不过期

缓存雪崩:大量 key 同时过期,导致数据库压力过大
解决:过期时间加随机值 / 热点数据永不过期 / 服务熔断降级

4. HashMap 线程安全吗?有哪些线程安全的 Map?

HashMap 线程不安全,JDK 1.7 并发下可能产生环形链表死循环。

线程安全的 Map:
1. Hashtable:全局锁,效率低
2. Collections.synchronizedMap:同样全局锁
3. ConcurrentHashMap:
   - JDK 1.7:分段锁(Segment)
   - JDK 1.8:CAS + synchronized,锁细化到单个桶

5. ConcurrentHashMap 如何保证线程安全?

1. 使用 CAS(Compare-And-Swap)保证原子性操作
2. 对单个桶使用 synchronized 加锁,保证并发安全
3. 扩容时支持多线程并发扩容
4. 不允许 null 作为 key 或 value

6. JVM 内存结构

1. 堆(Heap):对象实例、数组,GC 主要管理区域
2. 栈(Stack):方法调用、局部变量,每个线程独立
3. 方法区(Method Area):类信息、常量、静态变量
4. 本地方法栈:Native 方法调用
5. 程序计数器:记录当前线程执行位置

堆的分区:
- 年轻代:Eden + Survivor(From + To)
- 老年代:存放长期存活对象

7. 数据库索引数据结构(B+ 树 vs Hash)

B+ 树:
- 多叉平衡树,树高一般 3~4 层,查找次数少
- 叶子节点存储所有数据,用双向链表连接,范围查询快
- 适合范围查询、排序查询

Hash 索引:
- 用 Hash 计算 key 的位置,直接定位
- 等值查询快,范围查询慢
- 不支持排序、不支持最左前缀匹配

8. 幻读是什么?如何解决?

幻读:同一个事务中,两次查询结果不一致(因为其他事务插入了新数据)。

解决:
1. MVCC(多版本并发控制) + 间隙锁(Gap Lock)
2. RR(可重复读)隔离级别下,幻读会被抑制
3. SELECT ... FOR UPDATE 加锁

9. 分布式锁的实现

Redis 实现:
SET key value NX PX timeout

- NX:只有 key 不存在时才设置(保证原子性)
- PX:设置过期时间,防止死锁

释放:用 Lua 脚本保证只有持有锁的人才能释放

为什么用 SETNX 而不是 SET?
SETNX 是原子操作,保证只有一个客户端能获取锁。

10. 手撕代码:合并两个升序数组

python
def merge(nums1, m, nums2, n):
    p1, p2, p = m - 1, n - 1, m + n - 1
    while p2 >= 0:
        if p1 >= 0 and nums1[p1] > nums2[p2]:
            nums1[p] = nums1[p1]
            p1 -= 1
        else:
            nums1[p] = nums2[p2]
            p2 -= 1
        p -= 1

11. 手撕代码:LRU 缓存

python
from collections import OrderedDict

class LRUCache:
    def __init__(self, capacity):
        self.capacity = capacity
        self.cache = OrderedDict()

    def get(self, key):
        if key not in self.cache:
            return -1
        self.cache.move_to_end(key)
        return self.cache[key]

    def put(self, key, value):
        if key in self.cache:
            self.cache.move_to_end(key)
        self.cache[key] = value
        if len(self.cache) > self.capacity:
            self.cache.popitem(last=False)

二、二面(45~60 分钟 · 项目深挖 + 场景题)

1. MySQL 索引如何建立?

1. 主键自动建立唯一索引
2. WHERE 条件字段、JOIN 连接字段建议建索引
3. 区分度高的字段优先建索引
4. 遵循最左前缀原则:WHERE a = AND b = AND c = 可以建联合索引 (a, b, c)

复合索引顺序:
- 区分度高的放前面
- 查询条件最频繁的放前面

2. 深分页问题如何优化?

问题:SELECT * FROM t ORDER BY id LIMIT 100000, 10
      会先扫描前 100010 条,再返回后 10 条,性能很差。

优化方案:
1. 延迟关联:先查 ID,再关联查数据
   SELECT t.* FROM t JOIN (SELECT id FROM t LIMIT 100000, 10) AS tmp ON t.id = tmp.id

2. 游标分页:记录上一页最后一条的 ID
   SELECT * FROM t WHERE id > last_id LIMIT 10

3. 禁止跳页查询:只支持首页 + 上一页 + 下一页

3. 手撕代码:打牌,求最少出牌次数(LeetCode 403)

python
# 贪心 + DFS
def minCardPoints(cardPoints, k):
    # 转化为求两端和最小的子数组
    n = len(cardPoints)
    total = sum(cardPoints)
    window_size = n - k
    window_sum = sum(cardPoints[:window_size])
    min_window = window_sum
    for i in range(window_size, n):
        window_sum += cardPoints[i] - cardPoints[i - window_size]
        min_window = min(min_window, window_sum)
    return total - min_window

4. 接口幂等性

幂等性:多次执行同一个操作,结果和执行一次是一样的。

需要幂等的场景:
1. 支付接口:重复支付
2. 下单接口:重复下单
3. 退款接口:重复退款
4. 扣库存接口:重复扣减

实现方案:
1. 数据库唯一索引
2. 分布式锁(SETNX)
3. Token 机制:提交前获取 Token,提交时验证并删除
4. 状态机:只有特定状态才能流转

5. 手撕代码:两集合交集(给定左闭右开集合)

python
def intersection(nums1, nums2):
    # 假设输入为已排序的左闭右开区间集合
    result = []
    i, j = 0, 0
    while i < len(nums1) and j < len(nums2):
        # 获取当前集合的范围
        start1, end1 = nums1[i]
        start2, end2 = nums2[j]

        # 计算交集
        overlap_start = max(start1, start2)
        overlap_end = min(end1, end2)

        if overlap_start < overlap_end:
            result.append([overlap_start, overlap_end])

        # 移动指针
        if end1 < end2:
            i += 1
        else:
            j += 1

    return result

三、三面(60 分钟 · 项目深挖 + 架构 + HR)

1. 实习转正情况

如实回答:
1. 目前有没有转正机会
2. 实习期间的主要成果
3. 团队氛围和导师评价

2. 能否接受单休?

回答策略:
1. 如果确实能接受 → 直接说能接受
2. 如果有顾虑 → 说可以接受,并表示愿意适应公司文化
3. 追问工作强度和节奏

拼多多普遍工作强度大,回答前要想清楚。

3. 秋招投递了哪些公司?

如实回答,可以说:
- 头部大厂:字节/腾讯/阿里等
- 同类型公司:快手/美团/京东等

不要说只投了拼多多,显得选择少。
不要说投了很多小公司,显得质量不够。

4. 选 offer 的依据

1. 业务前景:业务是否有发展空间
2. 技术成长:能学到什么
3. 薪资福利:base + 奖金 + 股票
4. 团队氛围:直属 leader 和同事
5. 地点:城市和通勤

5. 预期薪资

参考:
- 提前了解市场行情
- 说出期望值时给一个范围
- 不要说「根据公司标准」

可以说:「结合我的学历背景和实习经历,以及市场行情,期望月薪 XXK~XXK」

6. 对拼多多的了解

1. 拼多多是中国最大的电商平台之一
2. 以 C2M(消费者直连制造商)模式著称
3. 增长速度快,用户规模大
4. 技术驱动:分布式系统、高并发、大数据
5. 拼多多企业文化:结果导向、执行力强

四、拼多多高频手撕代码汇总

题目难度出现频率
LRU 缓存★★★⭐⭐⭐⭐⭐
合并两个有序数组★★⭐⭐⭐⭐
集合交集(左闭右开)★★★⭐⭐⭐⭐
深分页优化★★⭐⭐⭐
打牌最少次数★★★⭐⭐⭐

五、拼多多面试特点总结

拼多多一面:项目 + 缓存(穿透/击穿/雪崩)+ HashMap + JVM + 手撕代码
重点:基础知识要扎实,代码要能写出来

拼多多二面:项目深挖 + MySQL(索引/深分页)+ 架构 + 手撕代码
重点:项目细节要经得起追问,MySQL 要深入

拼多多三面:主管面(偏 HR)
重点:稳定性、动机、抗压能力

注意:拼多多工作强度大,面试时会问能否接受单休,要提前想好。

Powered by VitePress

🔒 需要口令解锁

关注微信公众号
回复关键词 「」 获取口令

解锁后本浏览器长期有效