面试题
以下结合投资研究系统,针对SHEIN Java开发工程师社招一面问题,逐一给出回答思路与示例,突出业务场景与技术结合:
1. 说下项目的亮点难点
回答思路:聚焦投资研究系统,选「多源研报智能整合 + 高并发证券数据查询」场景,讲清业务价值、技术挑战、创新解法。
示例:
在投资研究系统中,多源研报智能整合是核心亮点与难点:需从外部券商(如Wind、东方财富 )、内部投研团队同步研报,支持全文检索、关键信息抽取(如目标价、核心逻辑 ),还要保障证券信息管理模块的高并发查询(投研人员实时查股票基本面 )。
-
亮点:
- 用 RabbitMQ 异步化:外部研报接入时,通过 MQ 解耦,接入服务只负责接收,消费端异步解析、入库,峰值时每秒处理 200+ 份研报,吞吐量提升 3 倍;
- Elasticsearch 全文检索:构建研报索引,支持按标题、关键词、作者检索,结合 Redis 缓存热门研报,查询响应从 500ms 压降到 80ms 。
-
难点:
- 多源数据格式异构:外部研报有 PDF、Word、HTML 等,解析规则复杂(如 PDF 表格提取 )。通过 策略 + 模板模式,定义
ReportParser接口,实现不同格式解析类,Spring 动态加载,新增格式只需扩展类; - 证券数据高并发冲突:投研人员同时查询、修改证券信息(如财务指标 ),用 Redis 乐观锁(
watch + multi)做并发控制,结合 Oracle 行锁,解决数据不一致问题。
- 多源数据格式异构:外部研报有 PDF、Word、HTML 等,解析规则复杂(如 PDF 表格提取 )。通过 策略 + 模板模式,定义
2. 知道哪些限流算法,分别说一下底层原理,滑动窗口和令牌桶的区别
回答思路:列举常用算法,结合投资系统场景(如研报上传、证券查询限流 )讲原理、差异。
示例:
常用限流算法有 固定窗口、滑动窗口、令牌桶、漏桶,在投资系统中,证券查询接口用滑动窗口,研报上传用令牌桶。
-
固定窗口:
原理:将时间划分为固定窗口(如 1 分钟 ),统计窗口内请求数,超阈值则限流。
场景:证券信息管理模块的低频配置接口,简单易实现,但临界时间(如窗口切换 )可能突增流量。 -
滑动窗口:
原理:把时间窗口拆成小格子(如 1 分钟拆 6 个 10 秒格子 ),实时滑动,统计最近窗口内请求数。
场景:证券实时行情查询(高并发 ),比固定窗口更平滑,避免临界突刺。 -
令牌桶:
原理:令牌桶以固定速率生成令牌,请求需获取令牌才能执行,桶满则暂停生成。
场景:研报上传接口(需限制带宽 ),允许一定突发流量(桶内积累令牌 ),又能控制长期速率。 -
滑动窗口 vs 令牌桶:
- 滑动窗口控频更精准,适合纯请求数限流;
- 令牌桶支持流量突发,兼顾速率与突发,适合有带宽/资源限制的场景(如投资系统的文件上传 )。
3. 线程的状态有哪几个?
回答思路:基于 Java 线程状态(Thread.State ),结合投资系统线程池、MQ 消费场景说明。
示例:
Java 线程有 6 种状态:
| 状态 | 场景说明(投资系统) |
|---|---|
| NEW | 初始化证券数据同步线程,未调用 start() |
| RUNNABLE | 研报解析线程执行中(CPU 运行/等待资源 ) |
| BLOCKED | 线程竞争 Oracle 行锁,等待进入同步块 |
| WAITING | 线程池线程调用 LockSupport.park() ,等待 MQ 消息触发 |
| TIMED_WAITING | 证券缓存预热线程 Thread.sleep(1000) ,定时刷新 |
| TERMINATED | 历史数据归档线程执行完毕,资源释放 |
4. Java线程和操作系统线程的区别
回答思路:从映射关系、调度、生命周期管理切入,结合投资系统性能优化场景说明。
示例:
-
映射关系:
Java 线程早期(JDK 1.2 前 )是用户级线程,现在(JDK 8+ )与 OS 线程 1:1 映射(通过pthread实现 )。投资系统中,证券查询线程对应 Linux 内核线程,由 OS 调度。 -
调度机制:
Java 线程调度由 JVM 负责(如线程优先级 ),但最终依赖 OS 调度器;OS 线程直接由内核调度。投资系统的策略回测线程池,通过设置线程优先级(setPriority),让关键策略计算线程优先执行。 -
生命周期管理:
Java 线程由 JVM 管理(创建、销毁 ),OS 线程由内核管理。投资系统中,Executors.newFixedThreadPool创建的线程,JVM 统一回收,避免 OS 线程泄漏。
5. 线程和线程池的生命周期
回答思路:拆分线程、线程池的状态流转,结合投资系统线程池(如证券数据更新池 )说明。
示例:
(1)线程生命周期(简化版 ):
NEW → RUNNABLE → TERMINATED(正常结束 ),或中途进入 BLOCKED/WAITING 等状态。
(2)线程池生命周期(以 ThreadPoolExecutor 为例 ):
- 运行状态(RUNNING ):接收任务,证券数据更新池初始化后,持续处理
updateStockData任务; - 关闭中(SHUTDOWN ):调用
shutdown(),不再接收新任务,处理队列剩余任务(如系统优雅停机 ); - 停止(STOP ):调用
shutdownNow(),中断正在执行的任务,清空队列(紧急故障恢复 ); - 终止(TERMINATED ):所有任务处理完毕,线程池彻底关闭,资源释放。
6. MQ熟悉哪几个?rabbitmq和其他mq的区别?他们都是怎么保证消息的顺序?
回答思路:对比 RabbitMQ、Kafka、RocketMQ ,结合投资系统消息场景(研报同步、证券数据变更 )说区别与顺序保证。
示例:
(1)熟悉 MQ:
投资系统用 RabbitMQ(研报异步解析、证券信息变更通知 )、Kafka(日志采集、大数据流处理 )。
(2)RabbitMQ vs 其他 MQ(以 Kafka 为例 ):
| 维度 | RabbitMQ | Kafka |
|---|---|---|
| 定位 | 通用消息中间件,侧重可靠投递 | 大数据流处理,侧重高吞吐 |
| 消息顺序 | 单队列 + 单消费者保证顺序 | 分区内有序,多分区需全局排序 |
| 适用场景 | 研报解析(低延迟、高可靠 ) | 行情数据实时流(高吞吐 ) |
| 生态 | 插件丰富(如延迟队列 ) | 生态完善,适合大数据链路 |
(3)消息顺序保证:
- RabbitMQ:发送到同一队列的消息,由单消费者顺序消费,投资系统中「证券评级变更通知」用单队列,保证投研人员收到顺序消息;
- Kafka:分区内消息有序,投资系统的行情数据(按股票代码分区 ),同一股票的行情变更消息发同一分区,消费时顺序处理。
7. 你知道java有哪几种锁?cas原理是什么?aba问题怎么解决呢
回答思路:分类锁(乐观/悲观、公平/非公平等 ),结合投资系统并发场景(证券数据修改 )讲 CAS 与 ABA 。
示例:
(1)Java 锁分类:
- 悲观锁:如
synchronized(证券信息管理的 Oracle 数据更新 ),认为并发冲突概率高,直接加锁; - 乐观锁:如
AtomicInteger(基于 CAS ),证券缓存计数用AtomicLong,先操作再校验; - 公平锁/非公平锁:ReentrantLock 可配置,投资系统的策略回测线程池用非公平锁,提升吞吐量;
- 分段锁:ConcurrentHashMap 的分段机制,证券代码哈希分段,减少锁竞争。
(2)CAS 原理:
Compare-And-Swap(比较并交换 ),3 个核心值:内存值(V )、预期值(A )、新值(B )。
投资系统中,证券缓存版本号更新用 CAS:
// 伪代码:更新证券缓存版本
AtomicReference<StockCache> ref = ...;
StockCache old = ref.get();
StockCache newCache = old.updateVersion();
// 比较并交换,版本号一致才更新
ref.compareAndSet(old, newCache);
(3)ABA 问题解决:
加 版本号/时间戳 ,投资系统中证券数据变更消息带版本号:
// 证券数据对象
class StockData {
private int version; // 版本号,每次变更 +1
private String data;
}
// CAS 时,对比版本号 + 数据
ref.compareAndSet(oldVersion, newVersion);
8. 你们怎么做的分库分表?为什么要这样做分库分表?
回答思路:结合投资系统数据规模(证券数据、研报、模拟组合 ),说分库分表策略(垂直/水平 )、路由规则、业务价值。
示例:
(1)分库分表原因:
投资系统初期用单 Oracle 库,证券表超 5000 万条、研报表超 2000 万条,查询慢(如按股票代码查历史数据 ),DML 操作锁表严重,因此分库分表。
(2)分库分表策略:
- 垂直分库:按业务拆分,将「证券信息管理库」「研报库」分离,减少跨业务干扰;
- 水平分表:证券表按 股票代码哈希分表(
stock_info_0~stock_info_31),研报表按 上传时间 + 机构 ID 哈希分表,路由规则:// 证券表路由:股票代码哈希取模 String tableName = "stock_info_" + (stockCode.hashCode() & 31);
(3)价值:
- 查询性能:证券查询从 500ms 降到 80ms ,研报检索从 1s 降到 300ms;
- 扩展性:支持后续证券数据量增长,新增分表只需扩展路由规则。
9. 数据库索引了解吗?B+树有什么特征吗?
回答思路:讲清索引类型(聚簇、非聚簇 )、B+ 树结构,结合投资系统 Oracle 表设计说明。
示例:
(1)数据库索引:
投资系统中,证券表建 聚簇索引(按股票代码 ),研报表建 非聚簇索引(按上传时间 + 机构 ID ):
- 聚簇索引:数据物理存储与索引顺序一致,适合范围查询(如查某行业股票 );
- 非聚簇索引:索引与数据分开存储,适合高频点查(如查某机构最新研报 )。
(2)B+ 树特征:
- 结构:叶子节点有序且连成链表,非叶子节点存索引键,叶子节点存数据地址(或数据 );
- 优势:
- 投资系统中,证券代码查询走 B+ 树,范围查询(如查代码 600000~600100 的股票 ),可通过叶子节点链表快速遍历;
- 高度低(3 层可存千万级数据 ),证券表 5000 万条数据,B+ 树高度 3 ,查询只需 3 次 IO 。
10. 你觉得这样做能解决什么问题?又会带来什么问题?
回答思路:承接分库分表、MQ 等技术决策,说解决的业务问题(性能、扩展性 )与引入的新挑战(分布式事务、运维复杂度 )。
示例(以分库分表为例 ):
(1)解决的问题:
- 性能:证券、研报查询从「全表扫描」变「索引 + 分表查询」,响应时间从秒级→毫秒级;
- 扩展性:支持未来 3 年数据增长(证券新增 10 万+ 、研报新增 500 万+ );
- 隔离性:垂直分库后,证券业务与研报业务故障互不影响。
(2)带来的问题:
- 分布式事务:跨分表更新(如证券代码变更需同步更新关联研报 ),用 Seata 补偿机制,增加复杂度;
- 运维成本:分库分表后,需维护多库多表,监控、备份、恢复难度提升;
- 跨表查询:如查「某机构发布的所有证券研报」需跨分表聚合,用 Elasticsearch 辅助解决。
11. 项目里面说有解决慢sql、cpu飙高、数据库死锁等经验,说下怎么解决的
回答思路:分场景(慢 SQL、CPU 高、死锁 ),结合投资系统实际案例,说排查流程与解法。
示例:
(1)慢 SQL 解决(证券历史数据查询 ):
- 现象:投研人员查「某股票近 5 年财务数据」,SQL 执行 10s+;
- 排查:
- 用 Oracle AWR 报告,发现全表扫描(未走索引 );
- 分析 SQL:
SELECT * FROM stock_finance WHERE stock_code = '600000' AND year BETWEEN 2018 AND 2023,year字段无索引;
- 优化:
建复合索引idx_stock_year (stock_code, year),SQL 执行时间降到 80ms 。
(2)CPU 飙高解决(研报解析线程池 ):
- 现象:研报解析服务 CPU 长期 90%+ ,系统卡顿;
- 排查:
top命令发现 Java 进程 CPU 高,top -Hp定位到解析线程;jstack分析,发现正则表达式匹配(研报正文提取 )存在回溯爆炸;
- 优化:
替换正则表达式(如用非贪婪匹配.*?替代.*),优化后 CPU 降到 30% 以内。
(3)数据库死锁解决(证券信息更新 ):
- 现象:证券代码变更与研报关联更新,出现死锁;
- 排查:
- 查 Oracle 死锁视图
v$lock,发现两个事务互相等待对方锁; - 分析代码,证券更新与研报更新的 SQL 顺序不一致(事务 A:先更证券 → 再更研报;事务 B:先更研报 → 再更证券 );
- 查 Oracle 死锁视图
- 优化:
统一 SQL 执行顺序(先更证券主表 → 再更关联研报 ),加锁超时重试(select ... for update wait 3),死锁率从 5%→0 。
12. 有什么想问我的吗?
回答思路:问岗位技术栈、团队业务方向、成长路径,体现对岗位的关注。
示例:
想了解 SHEIN 目前 Java 后端在高并发场景(如全球业务、海量订单 )的技术挑战,以及团队在云原生、微服务治理方面的实践规划。另外,想知道团队内技术分享与新人成长支持(如有没有定期的技术 workshop ),方便我提前准备融入~
以上回答紧扣投资研究系统业务,将技术问题融入真实场景,既体现技术深度,又展示业务思考,适配SHEIN Java开发岗位需求。可根据实际项目细节调整,让回答更贴合自身经历!

浙公网安备 33010602011771号