网易后端开发面经 · 2024 秋招
岗位:后端开发工程师(网易云音乐社交社区)
背景:双非本科 + 985 硕士,Java 技术栈,2 段实习
结果:OC,年薪 35w+(Base + 年终 + 补贴)
时间线:8.20 投递 → 8.28 一面 → 9.8 三面 → 9.12 OC
网易面试风格
网易面试整体节奏比较温和,注重基础扎实和逻辑思维:
- 算法难度中等:以 medium 为主,hard 题出现较少
- 基础比重高:Java 并发、JVM、Spring、MySQL 是重点
- 业务场景务实:偏向社交、音乐、游戏等网易核心业务场景
- 面试官态度友好:更像技术交流,压力面较少
一面(8.28):基础 + 算法
面试官:网易云音乐评论服务组
时长:55 分钟
Java 基础(15 分钟)
“HashMap 的 put 过程详细说说。”
- hash → 数组索引 → 链表/红黑树处理冲突 → 扩容(2 倍、rehash)
“ConcurrentHashMap 的线程安全是怎么实现的?”
- JDK 1.7:分段锁(Segment)
- JDK 1.8:CAS + synchronized(桶级别锁),扩容时多线程协助迁移
“volatile 和 synchronized 的区别?volatile 一定能保证可见性吗?”
- volatile:可见性 + 禁止指令重排,不保证原子性
- synchronized:可见性 + 原子性 + 有序性(Monitor 规则)
“线程池的核心参数有哪些?怎么配置?”
- corePoolSize、maximumPoolSize、keepAliveTime、workQueue、handler
- IO 密集型:core = CPU * 2;CPU 密集型:core = CPU + 1
算法题 1:Reorder List(LeetCode 143)
重排链表:L0→L1→…→Ln-1→Ln 变成 L0→Ln→L1→Ln-1→…
三步法:
def reorderList(head):
if not head or not head.next:
return
# 1. 找到中点(快慢指针)
slow, fast = head, head
while fast.next and fast.next.next:
slow = slow.next
fast = fast.next.next
# 2. 反转后半部分
prev, curr = None, slow.next
slow.next = None # 断开
while curr:
next_temp = curr.next
curr.next = prev
prev = curr
curr = next_temp
# 3. 交替合并
first, second = head, prev
while second:
tmp1, tmp2 = first.next, second.next
first.next = second
second.next = tmp1
first, second = tmp1, tmp2
算法题 2:Number of Islands(LeetCode 200)
岛屿数量,经典 DFS/BFS。我选了 DFS 写,因为代码更短。
def numIslands(grid):
if not grid:
return 0
rows, cols = len(grid), len(grid[0])
count = 0
def dfs(r, c):
if r < 0 or r >= rows or c < 0 or c >= cols or grid[r][c] != '1':
return
grid[r][c] = '0'
for dr, dc in [(-1, 0), (1, 0), (0, -1), (0, 1)]:
dfs(r + dr, c + dc)
for r in range(rows):
for c in range(cols):
if grid[r][c] == '1':
count += 1
dfs(r, c)
return count
追问:“如果用 BFS 怎么写?” → 用队列代替递归,其他逻辑一样。
二面(9.1):项目 + 场景
interviewer:云音乐社区推荐组
时长:65 分钟
项目深挖(20 分钟)
我实习做的是基于 Redis 的实时排行榜系统:
“排行榜用 Redis 的什么数据结构?”
- Sorted Set(ZSet),score 作为排序依据
“如果score 相同怎么排序?”
- 默认按 member 字典序排;如果需要自定义,把 score 放大 10^6 倍,低 6 位存时间戳
“用户量很大,单 ZSet 放不下怎么办?”
- 按时间段分片(日榜/周榜/月榜),或按用户分段(前 1000 名一个 ZSet,其余一个)
“如果排行榜数据丢了怎么恢复?”
- RDB 定期快照 + AOF 增量日志;核心榜单有数据库异步备份
算法题:Design LRU Cache(LeetCode 146)
哈希表 + 双向链表的经典题目。
class Node:
def __init__(self, key=0, val=0):
self.key = key
self.val = val
self.prev = None
self.next = None
class LRUCache:
def __init__(self, capacity):
self.capacity = capacity
self.cache = {} # key -> Node
# 伪头部和伪尾部
self.head = Node()
self.tail = Node()
self.head.next = self.tail
self.tail.prev = self.head
def _remove(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 _move_to_head(self, node):
self._remove(node)
self._add_to_head(node)
def _pop_tail(self):
node = self.tail.prev
self._remove(node)
return node
def get(self, key):
if key in self.cache:
self._move_to_head(self.cache[key])
return self.cache[key].val
return -1
def put(self, key, value):
if key in self.cache:
self.cache[key].val = value
self._move_to_head(self.cache[key])
else:
if len(self.cache) >= self.capacity:
tail = self._pop_tail()
del self.cache[tail.key]
node = Node(key, value)
self.cache[key] = node
self._add_to_head(node)
追问:
- “获取 API 时间复杂度?put 呢?” → 都是 O(1)
- “如果要支持缓存过期呢?” → 每个 node 加 expire_time,定时清理或在 get 时检查
- “Redis 的 LRU 怎么实现的?” → 近似 LRU,随机采样淘汰最久未访问的
MySQL 索引(15 分钟)
“InnoDB 的聚簇索引和非聚簇索引有什么区别?”
- 聚簇索引:数据行存在叶子节点(主键索引)
- 非聚簇索引:叶子节点存主键值,需要回表查数据
“联合索引 (a, b, c) 的命中场景?”
- 最左前缀:a、ab、abc 能命中;bc、c 不能
“有一个查询 WHERE a > 1 AND b = 2,索引怎么建?”
- (b, a):先精确匹配 b,再范围查 a,能走索引
- (a, b):a 范围查询后,b 的索引失效
三面(9.5):架构 + 综合
面试官:云音乐社区架构组
时长:60 分钟
场景设计:评论系统
要求设计网易云音乐歌曲评论系统:
我的方案:
用户评论 ──▶ API Gateway ──▶ 评论服务
│
├──▶ Redis:热门评论缓存(ZSet 按热度排序)
├──▶ MySQL:评论内容 + 评论关系(主从)
└──▶ ES:评论搜索(全文检索)
│
└──▶ Kafka:评论事件流(审核、通知)
- 热评算法:点赞数 × 0.7 + 回复数 × 0.2 + 时间衰减 × 0.1
- 分页策略:按热度排序用 ZSet,按时间排序用时间戳游标
- 敏感词过滤:Trie 树前缀匹配 + 命中即拦截
- 防刷:同一用户 1 分钟内最多 3 条,验证码触发
追问:
- “如果周杰伦发新歌,评论瞬间 100 万条怎么办?” → 写操作异步化(先写消息队列,消费者批量入库);读走 CDN + Redis 缓存
- “评论的点赞数怎么保证高并发下的正确性?” → Redis INCR 原子计数 + 定时刷盘到 MySQL
手撕代码:合并 K 个有序链表
LeetCode 23,经典题目。写了归并分治版本:
def mergeKLists(lists):
if not lists:
return None
def mergeTwo(l1, l2):
dummy = ListNode(0)
curr = dummy
while l1 and l2:
if l1.val < l2.val:
curr.next = l1
l1 = l1.next
else:
curr.next = l2
l2 = l2.next
curr = curr.next
curr.next = l1 or l2
return dummy.next
# 分治合并
def mergeRange(lists, left, right):
if left == right:
return lists[left]
mid = (left + right) // 2
l1 = mergeRange(lists, left, mid)
l2 = mergeRange(lists, mid + 1, right)
return mergeTwo(l1, l2)
return mergeRange(lists, 0, len(lists) - 1)
复杂度:时间复杂度 (O(N \log k)),空间复杂度 (O(1))(迭代版)或 (O(\log k))(递归版栈空间)。
四面(9.8):HR 面
时长:20 分钟
- 自我介绍和项目经历
- 为什么想来网易?对云音乐有什么了解?
- 薪资期望,其他 offer 情况
- 反问:团队技术栈、新人培养
复盘与建议
网易后端面试通关密码
| 维度 | 权重 | 准备要点 |
|---|---|---|
| 基础 | 35% | Java 并发(ConcurrentHashMap、线程池)、JVM、Spring |
| 算法 | 30% | LeetCode Hot 100 medium,链表/树/哈希表高频 |
| 项目 | 20% | 能讲清楚技术选型、复杂度、扩展方案 |
| 场景 | 15% | 社交场景(评论、点赞、关注)、排行榜、消息系统 |
高频考点
- 链表操作:反转、合并、重排、环检测(网易特别喜欢链表)
- Java 并发:volatile、synchronized、CAS、AQS、线程池
- MySQL 索引:聚簇/非聚簇、最左前缀、覆盖索引、回表
- Redis:ZSet 应用场景、持久化、过期策略、缓存一致性
- 场景设计:评论系统、点赞系统、用户关系(关注/粉丝)
网易 vs 其他公司对比
| 公司 | 算法难度 | 基础深度 | 面试氛围 |
|---|---|---|---|
| 网易 | ⭐⭐⭐ | ⭐⭐⭐⭐ | 友好 |
| 字节 | ⭐⭐⭐⭐⭐ | ⭐⭐⭐ | 紧凑 |
| 阿里 | ⭐⭐⭐⭐ | ⭐⭐⭐⭐⭐ | 深度追问 |
| 美团 | ⭐⭐⭐ | ⭐⭐⭐⭐ | 务实 |
网易面试整体难度适中,基础扎实、项目能讲清楚的同学通过率较高。建议重点准备 Java 基础和链表/树类算法题。
继续阅读
探索更多技术文章
浏览归档,发现更多关于系统设计、工具链和工程实践的内容。