1. 介绍你做过的一个最有挑战性的项目
回答思路 京东技术面非常注重工程能力的深度,要展示你对系统设计的全局思考,而非只会写业务代码。
- 项目背景:规模、业务场景、你的角色。
- 挑战定义:是技术挑战(高并发/数据一致性),还是工程挑战(跨团队协作/需求多变)?
- 你的解法:核心思路和具体技术方案。
- 结果与反思:量化数据 + 如果重来会有什么改变。
回答示例 我做过最有挑战性的项目是一个大学图书馆座位预约系统(面向全校3万名学生)。 挑战点:开放预约时(每天早上8点整),3万学生同时涌入,系统出现严重的接口超时和座位超订问题——同一个座位被多人预约成功。 我的解决方案: 第一,前端限流:预约按钮点击后立即置灰(防重复提交),同时加入前端队列(最多500人同时进入预约流程); 第二,Redis原子扣减:将每个座位的可预约状态存入Redis(seat:available:A101 = 1),用Lua脚本原子执行"查询+扣减",保证同一座位只有一次成功:
if redis.call('GET', KEYS[1]) == '1' then
redis.call('SET', KEYS[1], '0')
return 1
end
return 0
第三,异步落库:Redis扣减成功后,将预约消息写入RabbitMQ,消费者异步写入MySQL,避免大量同步DB写入压垮数据库; 第四,补偿机制:MQ消费失败时(死信队列)触发自动重试,重试3次后人工告警。 结果:8点开放时并发峰值约3000 QPS,系统稳定运行,零超订,数据库QPS下降75%。
2. MySQL优化:一条慢SQL如何定位和优化?
回答思路 京东仓储/物流数据量巨大,MySQL优化是必考题,要给出完整的排查 → 优化流程。
- 发现问题:慢查询日志、监控告警。
- 定位问题:EXPLAIN分析执行计划,重点看type/key/rows字段。
- 优化手段:索引优化、SQL改写、表结构调整、读写分离/分库分表。
回答示例
第一步:发现慢SQL
通过慢查询日志(long_query_time = 1)或数据库监控工具(如Prometheus + Grafana)发现执行时间超过阈值的SQL。
第二步:EXPLAIN分析
执行EXPLAIN SELECT ... 重点关注:
- type:ALL(全表扫描,最差)→ index → range → ref → const(最优),目标是达到range及以上;
- key:是否用到了预期的索引;
- rows:预估扫描行数,越小越好;
- Extra:是否出现Using filesort(文件排序,意味着无法利用索引排序)或Using temporary(使用临时表)。 第三步:常见优化手段
- 建/修正索引:根据WHERE/ORDER BY/JOIN条件设计联合索引,遵循最左前缀原则;
- SQL改写:避免SELECT *(减少数据传输)、用LIMIT限制返回行数、将子查询改为JOIN;
- 覆盖索引:在索引上直接包含查询所需字段,避免回表;
- 分页优化:深度分页LIMIT 10000, 10性能差,改为游标分页(WHERE id > last_id LIMIT 10);
- 读写分离:将读请求路由到从库,降低主库压力。
3. 请设计一个"京东购物车"的后端数据结构
回答思路 系统设计题,考察对实际业务的工程化思维,要考虑登录/未登录状态、数据存储选型、高并发写入。
- 需求分析:未登录用户(临时购物车)vs 登录用户(持久化购物车)。
- 数据结构设计:用户ID + 商品SKU + 数量 + 价格快照(下单时价格可能变动)。
- 存储选型:Redis(高频读写)+ MySQL(持久化)。
- 合并逻辑:未登录购物车在登录后如何合并。
回答示例 需求拆分:
- 未登录用户:购物车存在浏览器本地(localStorage)或后端临时存储(以device_id为key);
- 登录用户:购物车持久化存储,多端同步。 数据结构设计: Redis Hash结构:
key: cart:{user_id}
field: sku_id
value: {quantity, price_snapshot, add_time}
用Hash的原因:HSET/HGET/HDEL操作单个商品O(1),HGETALL获取全量购物车O(n),性能高。 MySQL作为持久化备份:
CREATE TABLE cart (
id BIGINT PRIMARY KEY,
user_id BIGINT NOT NULL,
sku_id BIGINT NOT NULL,
quantity INT NOT NULL,
price_snapshot DECIMAL(10,2),
created_at DATETIME,
updated_at DATETIME,
INDEX idx_user_id (user_id)
);
合并逻辑:用户登录后,将本地临时购物车中的商品逐一与服务端购物车合并(同一SKU数量相加),合并后删除临时购物车。 价格快照的意义:记录用户加入购物车时的价格,结算时与实时价格对比,展示"比加入时降价XX元"是促进转化的重要产品细节。
4. 算法题:合并K个升序链表
回答思路 京东笔试高频题,要掌握堆(优先队列)方案,时间复杂度O(nk log k)。
回答示例
public ListNode mergeKLists(ListNode[] lists) {
PriorityQueue<ListNode> heap = new PriorityQueue<>(
(a, b) -> a.val - b.val
);
for (ListNode node : lists) {
if (node != null) heap.offer(node);
}
ListNode dummy = new ListNode(-1);
ListNode cur = dummy;
while (!heap.isEmpty()) {
ListNode minNode = heap.poll();
cur.next = minNode;
cur = cur.next;
if (minNode.next != null) {
heap.offer(minNode.next);
}
}
return dummy.next;
}
// 时间复杂度 O(nk log k)
5. Redis的主从复制原理是什么?如何解决主从脑裂问题?
回答思路 京东Redis高可用必备知识,主从脑裂(split-brain)是生产环境的真实风险。
- 主从复制原理:全量同步(RDB)+ 增量同步(命令传播),断点续传(backlog)。
- 主从脑裂问题:网络分区导致从库被选为新主,多主同时写入造成数据分裂。
- 解决方案:合理配置min-slave-to-write/min-slave-max-lag,强制主库写入前确认从库数量。
回答示例 Redis主从复制原理: 分为三个阶段:
- 全量同步:
- 从库向主库发送PSYNC ? -1(请求全量同步);
- 主库执行BGSAVE生成RDB快照,同时记录后续命令到repl_backlog缓冲区;
- 将RDB文件发送给从库,从库接收后清空本地数据,加载RDB;
- 将缓冲区中的增量命令发送给从库执行。
- 增量同步(命令传播):
- 主库将每个写命令同时发送给所有从库(主库推送模式)。
- 断点续传(Redis 2.8+):
- 从库重连时发送PSYNC {runid} {offset},主库判断偏移量是否在repl_backlog中(默认1MB),如果在则只发送缺失的命令,避免全量同步。 主从脑裂问题: 网络分区时,主库和从库断开连接,如果从库被哨兵/Cluster自动选为新主,而原主库未感知(新主写入),网络恢复后会产生两个主库,导致数据分裂(旧主的数据覆盖新主,或两主数据不一致)。 解决方案:
min-slave-to-write 2 # 主库写入前至少等待2个从库确认
min-slave-max-lag 10 # 从库延迟超过10秒,主库拒绝写入
这样当网络分区时,如果只有1个从库存活,主库自动降级为只读,强制等待更多从库恢复,避免两个主库同时写入。
6. 算法题:求两个字符串的最长公共子序列(LCS)
回答思路 动态规划经典题,京东笔试高频,dp数组的维度设计是核心难点。
- dp[i][j]含义:s1前i个字符和s2前j个字符的LCS长度。
- 状态转移:如果s1[i-1]==s2[j-1],则dp[i][j]=dp[i-1][j-1]+1;否则dp[i][j]=max(dp[i-1][j], dp[i][j-1])。
- 复杂度:O(mn)时间,O(min(m,n))空间优化。
7. 什么是依赖注入?Spring中@Autowired和@Resource的区别?
回答思路:Spring框架基础题,京东技术面常见,要说清楚两者的实现原理和区别。
- 依赖注入(DI):Spring自动为Bean注入依赖,而非Bean自己new依赖。
- @Autowired:byType注入(按类型),配合@Qualifier可指定bean名称。
- @Resource:byName注入(默认按名称),找不到再按类型。
- 实际选择:如果同一个接口有多个实现,@Autowired+@Qualifier更灵活。
回答示例: 依赖注入(DI)是IOC的实现方式。传统写法中,ServiceA需要ServiceB,ServiceA自己new ServiceB(),造成强耦合;DI后,Spring容器在创建ServiceA时,自动把ServiceB的实例注入进去,ServiceA不需要关心ServiceB是如何创建的。
@Autowired(Spring原生):
- 默认按类型注入,从容器中找类型匹配的Bean;
- 如果容器中有多个相同类型的Bean,会抛出NoUniqueBeanDefinitionException,需要配合@Qualifier("beanName")指定具体Bean;
- 可以标注在字段、构造函数、setter方法上。
@Resource(JDK标准):
- 默认按名称(@Resource(name="xxx")),找不到再按类型;
- 如果不指定name,先按字段名去容器中找,找不到再按类型;
- 不支持按类型多个候选。
实战选择:同一接口有多个实现时,用@Autowired + @Qualifier("实现Bean名");同一接口只有一个实现时,两者差别不大,用哪个都行,但@Autowired更符合Spring生态的惯例。
8. 京东的库存系统如何设计?如何防止超卖?
回答思路:电商核心系统设计题,京东的库存管理是业内标杆,要展示完整的库存架构和防超卖机制。
- 库存分层设计:活动库存(秒杀)/ 可售库存 / 预占库存 / 实物库存。
- 防超卖核心:乐观锁(版本号/状态机)+ Redis原子扣减。
- 下单链路:锁库存(Redis预占)→ 支付(实际扣减)→ 取消/超时回滚。
回答示例: 库存分层架构: 活动库存(Redis,精准备)→ 预占库存(Redis,有限时间内有效)→ 实物库存(MySQL,持久化)
活动库存(如秒杀):在活动开始前,将秒杀商品的库存量(可能只有100件)预加载到Redis,所有扣减在Redis完成。
防超卖机制——Redis Lua原子扣减:
local stock = redis.call('GET', 'sku:stock:' .. KEYS[1])
if tonumber(stock) <= 0 then
return 0, 'STOCK_EMPTY'
end
redis.call('DECR', 'sku:stock:' .. KEYS[1])
return 1, 'SUCCESS'
Lua脚本保证判断和扣减的原子性,不会出现两个请求同时读到库存=1、同时扣减为0的超卖情况。
下单链路中的库存流转:
- 用户点击下单 → Redis原子扣减活动库存,返回成功 → 订单创建(状态=待支付);
- 用户完成支付 → MySQL实物库存扣减(乐观锁)→ 订单状态变更为已支付;
- 用户取消订单或支付超时(如30分钟)→ Redis活动库存回滚(INCR)+ 预占库存释放 → MySQL库存不变(实物还未真正扣减)。
这种设计保证了超高并发的秒杀场景下Redis承担所有压力,MySQL不会被直接击穿。
9. 什么是Spring事务的传播行为?@Transactional有哪些失效场景?
回答思路:Spring事务是京东Java岗必考题,要覆盖7种传播行为 + 常见失效场景。
- 7种传播行为:REQUIRED(默认)/ REQUIRES_NEW / NESTED 等。
- 失效场景:private方法(非代理)、同类内部调用、本地数据库无事务、外部事务未覆盖。
- 解决方案:编码式事务(TransactionTemplate)、AOP切面捕获异常。
回答示例: Spring事务传播行为(7种):
| 传播行为 | 说明 |
|---|---|
| REQUIRED(默认) | 有事务则加入,无则新建 |
| REQUIRES_NEW | 总是新建事务,挂起外部事务 |
| NESTED | 有事务则在嵌套点创建Savepoint,无则新建 |
| SUPPORTS | 有则加入,无则不以事务运行 |
| NOT_SUPPORTED | 以非事务运行,挂起外部事务 |
| MANDATORY | 必须在事务中运行,否则抛异常 |
| NEVER | 必须在非事务中运行,否则抛异常 |
@Transactional失效的常见场景:
- private方法:Spring AOP基于代理(Proxy),代理只能拦截public方法,private方法不受事务控制;
- 同类内部调用:在ServiceA的methodA()中直接调用this.methodB(),不走代理,走的是目标对象的直接调用,@Transactional不生效;
- 异常被catch吞掉:如果业务方法内部catch了异常但没有重新抛出,Spring事务管理器感知不到异常,不会回滚;
- 异常类型不匹配:默认只对RuntimeException和Error回滚,Checked Exception不会触发回滚(除非rollbackFor=Exception.class);
- 事务传播行为不当:PROPAGATION_REQUIRES_NEW会挂起外部事务,如果外部需要看到内部事务的提交结果就会出问题。
10. HR面:你对京东的"客户为先"价值观有什么理解?
回答思路:京东文化题,结合自己的实际经历来回答,展示真诚的思考而非背诵。
- 对"客户为先"的理解:不是"服务客户",而是"以客户视角做决策"。
- 结合经历:举一个你主动以用户视角改进工作的例子。
- 表达期待:说明你在京东能如何实践这个价值观。
回答示例: 我对"客户为先"的理解是:不是口号,而是每次做决策时的第一性问题。
在我做项目时,有一次我完成了商品详情页的改版,从技术角度看代码质量很高,页面加载速度也优化了40%,我很满意。但上线前让用户测试时,发现新的页面布局把用户最关心的"价格"和"评价"藏到了页面下方,需要滚动才能看到——用户在旧版上是直接能看到这些信息的。
这件事让我意识到,技术做得好,不等于用户用得好。从那之后,我在每个功能开发前,都会问自己:"如果我是用户,我会不会用?会不会觉得麻烦?"这个习惯后来帮我避免了好几次"技术自我感动"的设计。
京东"客户为先"对我来说,就是这种思维——在每一个细节上,都站在用户的角度去判断什么更重要,而不是以开发者的便利为优先。