美团后端开发面经(2024 秋招)

美团 2024 年秋季校招后端开发岗位面试复盘,涵盖到店事业群完整技术面试流程,包含算法题、MySQL 深度、Redis、分布式锁与实战项目。

美团后端开发面经(2024 秋招)

岗位:后端开发工程师(到店事业群 - 美食业务部)
背景:985 本科 + 硕士,主语言 Java,一段美团暑期实习(日常)
结果: offer,评级 L4
时间线:8.15 投递 → 8.22 一面 → 8.29 二面 → 9.5 三面(HR 面同步进行)→ 9.12 offer


一面(技术基础,1h)

一面面试官是组内的技术骨干,风格务实,问题环环相扣。

算法题(25min)

LeetCode 76. 最小覆盖子串

给定一个字符串 s 和一个字符串 t,返回 s 中涵盖 t 所有字符的最小子串。

from collections import Counter

def minWindow(s: str, t: str) -> str:
    need = Counter(t)
    window = Counter()
    valid = 0

    left, right = 0, 0
    start, length = 0, float('inf')

    while right < len(s):
        c = s[right]
        right += 1

        if c in need:
            window[c] += 1
            if window[c] == need[c]:
                valid += 1

        while valid == len(need):
            if right - left < length:
                start = left
                length = right - left

            d = s[left]
            left += 1

            if d in need:
                if window[d] == need[d]:
                    valid -= 1
                window[d] -= 1

    return "" if length == float('inf') else s[start:start + length]

追问:

  1. 时间/空间复杂度? $O(|s| + |t|)$ 时间,$O(|\Sigma|)$ 空间(字符集大小)
  2. 如果 t 中有重复字符怎么办? Counter 天然处理,不需要额外逻辑
  3. 如果字符串 s 是流式输入的,如何改进? 可以用不定长滑动窗口,维护 need 的满足状态,每次新字符到来时尝试收缩左边界

Java 基础(20min)

  1. HashMap 的 put 流程完整说一下?

    • 计算 hash → 定位 bucket → 链表/红黑树插入 → 检查扩容(size > threshold = capacity * load_factor)
  2. ConcurrentHashMap 在 JDK 1.7 和 1.8 的区别?

    • 1.7:Segment 分段锁,每段一个 HashEntry 数组
    • 1.8:取消 Segment,使用 CAS + synchronized(桶级别),红黑树优化长链表
  3. 线程池的核心参数有哪些?

    • corePoolSize、maximumPoolSize、keepAliveTime、workQueue、threadFactory、rejectedExecutionHandler
  4. 拒绝策略有哪几种?

    • AbortPolicy(默认抛异常)、CallerRunsPolicy(调用者线程执行)、DiscardPolicy(静默丢弃)、DiscardOldestPolicy(丢弃最老任务)

MySQL(10min)

  1. InnoDB 的 RR(可重复读)隔离级别如何解决幻读?
    • 答:MVCC + Next-Key Lock(记录锁 + 间隙锁),锁住范围防止插入
  2. 聚簇索引和非聚簇索引的区别?
    • 聚簇索引:叶子节点存储完整行数据(InnoDB 主键索引)
    • 非聚簇索引:叶子节点存储主键值,需要回表

二面(项目 + 分布式,1h)

二面面试官是到店事业群的技术经理,更关注工程思维和系统设计。

算法题(20min)

LeetCode 138. 复制带随机指针的链表

class Node:
    def __init__(self, x: int, next: 'Node' = None, random: 'Node' = None):
        self.val = x
        self.next = next
        self.random = random

def copyRandomList(head: 'Node') -> 'Node':
    if not head:
        return None

    # 第一轮:复制节点并插入到原节点后面
    curr = head
    while curr:
        new_node = Node(curr.val, curr.next, None)
        curr.next = new_node
        curr = new_node.next

    # 第二轮:设置 random 指针
    curr = head
    while curr:
        if curr.random:
            curr.next.random = curr.random.next
        curr = curr.next.next

    # 第三轮:拆分链表
    curr = head
    new_head = head.next
    while curr:
        copy = curr.next
        curr.next = copy.next
        if copy.next:
            copy.next = copy.next.next
        curr = curr.next

    return new_head

追问:

  1. 除了这种方式,还能怎么做? 可以用哈希表存储原节点到新节点的映射,空间换时间
  2. 时间/空间复杂度? $O(n)$ 时间,$O(1)$ 额外空间(迭代解法)
  3. 如果链表有环怎么办? 需要先检测环并处理(不过这道题一般来说没有环)

分布式锁(15min)

如何用 Redis 实现分布式锁?

我回答了 Redlock 方案:

import redis
import uuid
import time

class RedisDistributedLock:
    def __init__(self, redis_client, lock_name, expire_time=30):
        self.r = redis_client
        self.lock_name = f"lock:{lock_name}"
        self.expire_time = expire_time
        self.identifier = str(uuid.uuid4())

    def acquire(self):
        # SET lock_name identifier NX EX expire_time
        result = self.r.set(
            self.lock_name,
            self.identifier,
            nx=True,
            ex=self.expire_time
        )
        return result is not None

    def release(self):
        # 使用 Lua 脚本保证原子性
        lua_script = """
        if redis.call('get', KEYS[1]) == ARGV[1] then
            return redis.call('del', KEYS[1])
        else
            return 0
        end
        """
        return self.r.eval(lua_script, 1, self.lock_name, self.identifier)

追问:

  1. 如果业务执行时间超过锁的过期时间怎么办? Redisson 的 Watch Dog 机制——启动守护线程定期续期
  2. Redis 是单节点的,如果挂了怎么办? Redlock 算法(在 N 个独立的 Redis 节点上加锁,多数成功才算获得锁)
  3. Zookeeper 实现分布式锁和 Redis 有什么区别? ZK 是顺序临时节点,利用 EPHEMERAL_SEQUENTIAL 实现公平锁,可靠性更高但性能略低

项目深挖(20min)

围绕实习中的优惠券系统:

  1. 优惠券如何防止超发?
    • 答:Redis Lua 原子脚本扣减库存,数据库异步同步
  2. 如果 Redis 扣减成功但数据库写入失败怎么办?
    • 答:先写数据库扣减,再删 Redis 缓存(Cache-Aside),异步对账补偿
  3. 如何防止用户重复领取?
    • 答:幂等键(user_id + coupon_id)加唯一索引
  4. 优惠券过期怎么自动回收?
    • 答:定时任务(xxl-job)扫描过期券 + Redis key 过期事件监听

开放性问题(5min)

如何设计一个餐厅排队系统?

  • 我回答了取号、排队、叫号、过号重排四个状态,用 Redis Sorted Set 维护队列,WebSocket 推送叫号通知。

三面(HR 面,30min)

美团的技术面和 HR 面有时合并,有时分开。我的三面就是纯 HR。

  1. 为什么选择美团?
    • 答:美团是「零售 + 科技」公司,到店业务直接连接线下实体商户,技术挑战大;同时美团的技术文化务实,重视工程能力。
  2. 你的职业规划?
    • 答:1-3 年深耕后端技术栈,3-5 年成长为能独立负责系统模块的技术骨干。
  3. 手里有其他 offer 吗?
    • 答:如实说有字节和京东,但美团更匹配我的业务兴趣。
  4. 期望薪资?
    • 说了一个 range,HR 说会在范围内尽量争取。

美团面试特色总结

维度特点
算法难度⭐⭐⭐⭐ Medium 为主,偶尔 Hard,重视代码质量
技术深度⭐⭐⭐⭐ 非常注重工程实践,Redis、MySQL、JVM 都会深挖
项目关注⭐⭐⭐⭐⭐ 会追问到每个技术细节的实现和异常处理
系统设计⭐⭐⭐ 二三面可能会问,但不像阿里那么重
面试氛围⭐⭐⭐⭐ 务实、直接、不拖沓

美团面试官的经典追问风格:

  • “如果这时候挂了怎么办?”
  • “如果数据量翻 100 倍呢?”
  • “你确定这个方案是最优的吗?有没有更好的?”

面试复盘与建议

美团面试的通关密码:

  1. Java 基础要扎实:HashMap、ConcurrentHashMap、线程池、JVM 内存模型是必考点。
  2. Redis 深入理解:不仅是缓存,分布式锁、限流、计数器、Sorted Set 的应用都要会。
  3. MySQL 不只八股文:索引优化、事务隔离级别、锁机制、分库分表策略要能结合实际场景讲。
  4. 项目要能扛住追问:每个项目准备"如果 XX 场景下出问题怎么解决"。

准备清单:

  • 《深入理解 Java 虚拟机》核心章节
  • 《Redis 设计与实现》数据结构 + 持久化 + 集群
  • 《MySQL 技术内幕》InnoDB 存储引擎
  • LeetCode Hot 100 + 剑指 Offer
  • 准备 2-3 个能扛住 20 分钟追问的项目

继续阅读

探索更多技术文章

浏览归档,发现更多关于系统设计、工具链和工程实践的内容。

全部文章 返回首页