网易旗下有网易游戏、网易云音乐、网易有道、网易严选等业务线,Java后端开发一直是核心岗位。本文基于真实面试经验整理,覆盖JVM、并发、Spring、Redis、Kafka、MySQL、分布式系统设计等高频考点,每道题都配有通俗易懂的解析和代码示例。
1. ConcurrentHashMap 1.7和1.8有什么区别?为什么1.8要改成CAS+synchronized?
这道题在网易面试中几乎必问,属于并发编程的经典题。
答案:
1.7版本用的是Segment分段锁,默认16个Segment,每个Segment继承ReentrantLock,put操作时对单个Segment加锁,相当于把大锁拆成16把小锁,并发度就是16。
1.8版本完全重写了,改成数组+链表+红黑树的结构,用CAS+synchronized来实现并发控制。put操作时先CAS尝试插入头节点,如果头节点为空直接CAS成功,否则对头节点加synchronized锁。
为什么这么改?核心原因有三个:
-
锁粒度更细:1.7要锁一个Segment(包含多个桶),1.8只锁一个桶,并发度从16提升到数组长度
-
synchronized优化了:JDK 1.6之后synchronized引入偏向锁、轻量级锁,性能已经不输ReentrantLock了
-
节省内存:Segment继承ReentrantLock是有额外内存开销的
java
// 1.8 put操作的简化逻辑
final V putVal(K key, V value, boolean onlyIfAbsent) {
// 计算hash
int hash = spread(key.hashCode());
for (Node<K,V>[] tab = table;;) {
Node<K,V> f; int n, i, fh;
if (tab == null || (n = tab.length) == 0)
tab = initTable(); // CAS初始化
else if ((f = tabAt(tab, i = (n - 1) & hash)) == null) {
// 头节点为空,CAS插入,无锁操作!
if (casTabAt(tab, i, null, new Node<K,V>(hash, key, value)))
break;
} else {
// 对头节点加synchronized锁
synchronized (f) {
// ... 链表或红黑树插入
}
}
}
}
2. JVM内存模型和垃圾回收机制,网易云音乐场景下怎么调优?
这道题考察你对JVM的理解深度,面试官会追问实际调优经验。
答案:
JVM内存分线程私有和线程共享两块。线程私有的有程序计数器、虚拟机栈(每个方法对应一个栈帧)、本地方法栈;线程共享的有堆(存对象实例)和方法区(存类信息、常量、静态变量)。
垃圾回收最常用的是分代收集:新生代用复制算法(Minor GC频繁,存活对象少,复制成本低),老年代用标记-整理或标记-清除(CMS/G1)。
网易云音乐这种场景,数据特征是一条歌曲有上亿次播放,用户听歌记录、歌单、评论都是热点数据。常见调优:
java
// 1.8 put操作的简化逻辑
final V putVal(K key, V value, boolean onlyIfAbsent) {
// 计算hash
int hash = spread(key.hashCode());
for (Node<K,V>[] tab = table;;) {
Node<K,V> f; int n, i, fh;
if (tab == null || (n = tab.length) == 0)
tab = initTable(); // CAS初始化
else if ((f = tabAt(tab, i = (n - 1) & hash)) == null) {
// 头节点为空,CAS插入,无锁操作!
if (casTabAt(tab, i, null, new Node<K,V>(hash, key, value)))
break;
} else {
// 对头节点加synchronized锁
synchronized (f) {
// ... 链表或红黑树插入
}
}
}
}
关键思路是:评论、歌单这类热点数据会频繁进入老年代,用G1可以设置目标停顿时间,避免Full GC导致接口超时。如果接口超时频繁,可以通过jstat -gcutil观察GC频率,适当调大新生代或者调整IHOP( Initiating Heap Occupancy Percent)。
3. 说一下Spring的IOC和AOP,AOP在项目里怎么用的?
Spring几乎是网易后端项目的标配,这道题看你有没有真正用过还是只会背八股。
答案:
IOC(控制反转)就是把对象创建和依赖管理的控制权从程序员手里交给Spring容器。原来我们new对象,现在把对象交给容器管理,通过构造器或setter注入依赖。Spring Boot里加个@Service或@Component,容器启动时就会扫描并创建Bean。
AOP(面向切面编程)底层分两种:有接口就用JDK动态代理(基于反射+InvocationHandler),没有接口就用CGLIB(生成子类)。
项目中真实场景:
java
@Aspect
@Component
public class LogAspect {
// 环绕通知,记录接口调用耗时
@Around("@annotation(LogAnnotation)")
public Object around(ProceedingJoinPoint point) throws Throwable {
long start = System.currentTimeMillis();
Object result = point.proceed();
long cost = System.currentTimeMillis() - start;
// 写入日志或监控系统
log.info("{} 耗时: {}ms", point.getSignature(), cost);
return result;
}
}
网易云音乐项目里,AOP常用于:记录用户操作日志(收藏/评论/分享)、接口耗时监控、权限校验、分布式锁等。判断一个人有没有真用过AOP,就看他说不谈什么"日志/事务/权限",而是能说清在这些场景里遇到了什么问题、怎么处理的。
4. Redis如何实现排行榜?网易云音乐歌单热度排序
网易云音乐的排行榜功能每天上亿用户访问,Redis的Sorted Set就是干这个的。
答案:
用Sorted Set(有序集合),每个成员关联一个score,Redis自动按score排序。
java
// 网易云音乐热歌榜实现思路
// 1. 每首歌曲的播放次数作为score
String key = "rank:hot_songs";
// 播放一次,score加1
redisTemplate.opsForZSet().incrementScore(key, "song_10086", 1);
// 2. 获取Top 100
Set<Object> top100 = redisTemplate.opsForZSet()
.reverseRange(key, 0, 99); // 从高到低取100个
// 3. 获取某首歌的排名
Long rank = redisTemplate.opsForZSet()
.reverseRank(key, "song_10086"); // 排名从0开始
真实场景的难点:
-
冷热数据分开:周榜和总榜分不同的key,周榜每周清空,总榜定期归档到MySQL/ES
-
降权处理:新歌需要扶持,比如新发布的歌7天内score乘以1.2的权重,防止老歌霸榜
-
防刷:一个用户短时间内同一首歌播放多次需要限频,一般加上时间窗口过滤(比如30秒内重复播放不计数)
-
实时与最终一致:排行榜可以容忍几秒的延迟,所以用Redis做主存储,后台定时刷到数据库做持久化
5. MySQL慢查询怎么优化?一条SQL从1秒优化到10ms
面试官会给你一条具体SQL让你分析为什么慢,怎么优化。
答案:
第一步,找到慢SQL。 MySQL的慢查询日志打开:
java
-- 开启慢查询日志,设置阈值1秒
SET GLOBAL slow_query_log = ON;
SET GLOBAL long_query_time = 1;
第二步,用EXPLAIN分析。 重点关注几个字段:type(至少要range以上,最好是ref或const)、Extra(如果有Using filesort或Using temporary就要优化了)。
第三步,常见的优化手段:
-
索引优化:联合索引遵循最左前缀原则。比如要查where status=1 and create_time>'2025-01-01',建(status, create_time)的联合索引,别单建
-
覆盖索引:查询的字段都在索引里,不用回表
-
分页优化:深分页用子查询或游标,别直接limit 1000000, 20
java
-- 深分页优化:用上一次的ID代替limit偏移
-- 慢写法
SELECT * FROM orders ORDER BY id LIMIT 1000000, 20;
-- 快写法
SELECT * FROM orders WHERE id > 1000000 ORDER BY id LIMIT 20;
-
读写分离:读多写少的场景,主库写、从库读
-
分库分表:单表超过500万行就得考虑分表,按用户ID取模或者按时间分区
6. 订单超时自动取消怎么实现?网易严选场景
网易严选是电商业务,订单30分钟未支付自动取消是经典问题。
答案:
方案一:定时任务扫描(最原始,不推荐)
缺点很明显,扫全表、延迟高、浪费数据库IO。
方案二:Redis过期事件
java
// 订单创建后设置缓存,过期时间30分钟
redisTemplate.opsForValue().set(
"order:timeout:" + orderId,
orderId,
30,
TimeUnit.MINUTES
);
但Redis过期事件不保证100%触发(key被删除也可能不回调),而且延迟不确定,只能当辅助方案。
方案三:时间轮算法 + 延迟消息
用Kafka或RocketMQ的延迟队列。RocketMQ支持18个级别的延迟(1s/5s/10s/30s/1m/2m...30m),发消息时指定延迟级别,到期自动投递。
java
// RocketMQ 延迟消息
Message msg = new Message("order_topic", orderId.getBytes());
msg.setDelayTimeLevel(16); // 30分钟
producer.send(msg);
方案四:Redisson DelayedQueue
如果不想引入MQ,用Redisson的RDelayedQueue,底层依赖Redis的Sorted Set,到期自动取出。
网易这种体量的公司,一般是RocketMQ延迟队列 + Redis过期兜底的双重方案,消息队列负责准时触发,Redis过期事件做异常情况的补偿。如果MQ挂了,还有定时任务做最终的兜底扫描。
7. 分布式锁有几种实现方式?网易游戏道具发放怎么保证不超发?
网易游戏在抢道具、充值、活动奖励时,分布式锁是关键。
答案:
实现方式有三种:
方案一:Redis SETNX
java
// 获取锁,key=道具ID,value=请求唯一ID,过期时间5秒
Boolean locked = redisTemplate.opsForValue()
.setIfAbsent("lock:item:10001", requestId, 5, TimeUnit.SECONDS);
if (locked) {
try {
// 发放道具的逻辑
} finally {
// 释放锁,用Lua脚本保证原子性
String lua = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";
redisTemplate.execute(new DefaultRedisScript<>(lua, Long.class),
Arrays.asList("lock:item:10001"), requestId);
}
}
方案二:Redisson
Redisson的RLock实现了可重入、自动续期(看门狗机制)、公平锁等高级功能。
方案三:Zookeeper
基于临时顺序节点+Watch机制,可靠性比Redis高,但性能差一点,适合对一致性要求极高的场景(比如道具发放)。
难点在于:
-
锁的超时时间怎么设?太短业务没执行完锁就自动释放了,太长影响并发
-
Redisson有看门狗,默认每10秒续期一次,业务不结束就不释放锁
-
Redis主从架构下,如果获取锁后主节点挂了,从节点还没同步,其他线程又能获取到锁,这就是RedLock要解决的问题
8. Kafka怎么保证消息不丢失和不重复消费?
网易有道、云音乐这些业务都有大量异步消息场景,消息可靠性是必考题。
答案:
消息不丢失,分三个阶段来看:
- 生产者端:用同步发送+回调确认
java
// 同步发送,确认leader和follower都写入
producer.send(record).get(); // 同步等待
// 或者用回调
producer.send(record, (metadata, exception) -> {
if (exception != null) {
// 重试或记录失败消息
}
});
配置acks=all(或acks=-1),等所有副本都写入了再返回成功。
-
Broker端:复制因子replication.factor >= 3,min.insync.replicas=2,保证至少2个副本同步。
-
消费者端:关闭自动提交,业务处理完才手动提交offset
java
// 手动提交offset
consumer.commitSync(); // 在业务处理成功后调用
消息不重复消费,核心是实现幂等性:
-
生产端幂等:enable.idempotence=true,Kafka会去重
-
消费端幂等:每条消息带全局唯一ID(或业务唯一键),消费前先查是否处理过
java
// 消费端幂等处理
@Override
public void onMessage(Message message) {
String bizId = message.getBizId(); // 业务唯一ID
// Redis SETNX,如果已经处理过就跳过
Boolean done = redisTemplate.opsForValue()
.setIfAbsent("msg:processed:" + bizId, "1", 1, TimeUnit.DAYS);
if (!Boolean.TRUE.equals(done)) {
return; // 已消费,跳过
}
// 真正的业务处理...
}
9. Elasticsearch的写入和查询流程,网易有道搜索怎么做?
网易有道的词典搜索、题库搜索大量使用ES。
答案:
写入流程: 客户端请求发给任意一个协调节点 → 协调节点计算该文档应该到哪个分片(shard = hash(_id) % number_of_shards) → 转发到主分片节点 → 写入buffer同时写translog → 每秒refresh生成一个segment(此时才可搜索) → 定期flush把segment刷到磁盘。
查询流程: 客户端发查询到协调节点 → 协调节点转发到所有分片(从分片和主分片都可以查) → 每个分片本地查询返回结果 → 协调节点合并排序返回。
面试的经典追问:
Q:ES的near real-time(近实时)是什么意思?
A:写入后默认1秒才可搜索,因为refresh默认1秒执行一次。如果要求强实时,可以调refresh=wait_for或手动refresh,但写入性能会下降。
Q:ES的深度分页怎么优化?
A:from+size不能超过10000条,超过要用search_after或scroll。search_after基于上一页最后一个文档的排序值继续分页,性能好且稳定。
java
// search_after分页
GET /products/_search
{
"size": 20,
"sort": [{"price": "asc"}, {"id": "asc"}],
"search_after": [99, "product_12345"] // 上一页最后一个商品的price和id
}
10. 设计一个带热度排序的Feed流系统(网易云音乐动态/歌单推荐场景)
这是系统设计题,网易很喜欢考这种结合业务场景的设计。
答案:
需求分析: 用户关注了歌手/好友后,能看到他们的最新动态(发歌、评论、收藏等),按热度排序。
推拉模式选择:
-
推模式:用户发动态后,写入所有粉丝的收件箱。适合明星大V(粉丝多时写入压力大)
-
拉模式:动态只存一份,用户刷Feed时拉取所有关注的人的动态再合并排序。适合粉丝少的普通用户(关注多时拉取压力大)
-
混合模式:大V用推(活跃粉丝直接推送),普通用户用拉。这是网易云音乐实际采用的方式
架构设计:
java
用户发动态 → API网关 → MQ(削峰) → Feed服务
├─ 大V发的 → 写入粉丝的Redis收件箱(Sorted Set,score=时间戳)
├─ 普通用户 → 只写入自己的时间线
└─ 同步到MySQL持久化
用户刷Feed → Feed服务
├─ 从Redis收件箱取(已push的)
├─ 从关注列表拉普通用户的动态
├─ 合并排序(按时间/热度加权)
└─ 返回给用户
热度排序实现:
java
// 热度的简单公式
double hotScore = baseScore * (1 + weight * factor);
// - baseScore:基础分(比如赞数*1 + 评论数*2 + 转发数*3)
// - weight:时间衰减权重,越新越高
// - factor:内容质量系数(通过用户举报率等调整)
// 用Redis Sorted Set存储,score就是热度值
String key = "feed:timeline:" + userId;
redisTemplate.opsForZSet()
.add(key, feedItemId, hotScore);
// 取出Top 50
redisTemplate.opsForZSet()
.reverseRange(key, 0, 49);
亿级用户的优化:
-
收件箱不存完整动态,只存动态ID,详情从Redis缓存或MySQL按ID批量查询
-
冷用户不预推送,等他登陆时实时聚合
-
缓存穿透问题:热点数据缓存+布隆过滤器拦截不存在的数据
总结
网易Java后端面试有个特点:既考基本功,又考场景设计。八股文答得好只是及格,能结合业务场景把知识"用起来"才是加分项。建议准备面试的时候,每个知识点都想一想"如果在网易云音乐/网易游戏/网易有道,这个技术会用在什么场景"。
祝大家面试顺利,早日上岸!