常见Java后端面试(八股文)
NO 1. spring的事物,事物失效的场景
Spring 事务
核心概念
Spring 事务本质是 对数据库连接的提交/回滚做统一管理,常用 @Transactional 声明式事务,底层靠 AOP 动态代理 实现。
调用方 → 代理对象 → 开启事务 → 执行业务 → 提交/回滚
传播行为(Propagation)
| 传播行为 | 含义 |
|---|---|
|
REQUIRED(默认) |
有事务就加入,没有就新建 |
|
REQUIRES_NEW |
总是新建事务,挂起当前事务 |
|
NESTED |
嵌套事务,外层回滚会带动内层 |
|
SUPPORTS |
有事务就加入,没有就非事务执行 |
|
NOT_SUPPORTED |
以非事务执行,挂起当前事务 |
|
MANDATORY |
必须在事务中,否则抛异常 |
|
NEVER |
不能在事务中,否则抛异常 |
隔离级别(Isolation)
对应数据库隔离级别:DEFAULT、READ_UNCOMMITTED、READ_COMMITTED、REPEATABLE_READ、SERIALIZABLE。
回滚规则
- 默认:RuntimeException 和 Error 回滚
- Checked Exception 默认 不回滚
- 可配置:
rollbackFor = Exception.class、noRollbackFor = ...
@Transactional(rollbackFor = Exception.class)
public void transfer() throws Exception {
// ...
}
事务失效的常见场景(面试重点)
1. 方法不是 public
Spring AOP 默认只代理 public 方法,private/protected 事务不生效。
@Transactional
private void update() { } // 失效
2. 同类内部自调用(最常见)
代理只拦 外部调用,类内部 this.method() 不走代理。
@Service
public class OrderService {
public void createOrder() {
this.saveOrder(); // 失效,没走代理
}
@Transactional
public void saveOrder() { }
}
解决:
- 注入自身(
@Lazy @Autowired private OrderService self) - 拆到另一个 Service
- 使用
AopContext.currentProxy()
3. 异常被吞掉或 catch 后没再抛出
@Transactional
public void update() {
try {
// ...
} catch (Exception e) {
log.error("error", e); // 吞掉异常,事务不会回滚
}
}
解决: catch 后 throw e,或 TransactionAspectSupport.currentTransactionStatus().setRollbackOnly()
4. 抛出的异常类型不对
默认 Checked Exception 不回滚:
@Transactional
public void update() throws Exception {
throw new Exception("业务异常"); // 默认不回滚
}
解决: rollbackFor = Exception.class
5. 数据库引擎不支持事务
如 MySQL 用 MyISAM 不支持事务,要用 InnoDB。
6. 多数据源时数据源或事务管理器配置错误
@Transactional 没指定 transactionManager,或动态数据源路由错误,可能导致事务绑错连接。
7. 没有被 Spring 管理
类没加 @Service / @Component,或 new 出来的对象,代理不会生效。
OrderService service = new OrderService(); // 失效
8. 传播行为设置不当
例如:
@Transactional(propagation = Propagation.NOT_SUPPORTED)
public void method() { } // 故意非事务执行
或外层 REQUIRES_NEW、内层逻辑理解错误,导致“以为有事务、实际没有”。
9. 只读事务误用
@Transactional(readOnly = true)
public void update() { } // 可能只读优化,写操作异常或行为不符合预期
10. 异步执行导致事务上下文丢失
@Async、新线程、CompletableFuture 里执行数据库操作,新线程拿不到原线程事务。
@Transactional
public void process() {
executor.execute(() -> {
dao.update(); // 不在同一事务里
});
}
11. 分布式场景误以为是本地事务
跨服务、跨库调用,本地 @Transactional 管不了远程,需要 Seata、消息最终一致性等。
如何排查事务是否生效
- 日志开
org.springframework.transactionDEBUG - 看是否有
Creating new transaction/Committing/Rolling back - 打断点看调用的是 代理对象 还是 原始对象
- 故意抛 RuntimeException,看数据是否回滚
面试口述模板(30 秒版)
NOSpring 事务通过 AOP 代理实现,
@Transactional在方法前后做开启、提交或回滚。
失效最常见三类:同类自调用不走代理、异常被 catch 没抛出、抛了 Checked Exception 默认不回滚。
另外还有方法非 public、Bean 没被 Spring 管理、MyISAM 不支持事务、异步线程丢失事务上下文等。
实际项目里我一般避免同类自调用,统一rollbackFor = Exception.class,关键业务用集成测试验证回滚。
NO2 . 老项目没日志,怎么让它有日志
分层次、渐进式改造:
方案一:引入日志框架(最小改动)
- 加依赖:
logback或log4j2(Spring Boot 默认 logback) - 配置
logback-spring.xml:控制台 + 滚动文件、按天归档 - 全局替换
System.out.println→log.info/debug/error - 用 IDE 批量重构 + Code Review
方案二:AOP 统一打日志(侵入小)
@Aspect
@Component
public class LogAspect {
@Around("@annotation(org.springframework.web.bind.annotation.RequestMapping)")
public Object log(ProceedingJoinPoint pjp) throws Throwable {
log.info("请求: {} 参数: {}", pjp.getSignature(), Arrays.toString(pjp.getArgs()));
long start = System.currentTimeMillis();
try {
Object result = pjp.proceed();
log.info("耗时: {}ms", System.currentTimeMillis() - start);
return result;
} catch (Exception e) {
log.error("异常", e);
throw e;
}
}
}
方案三:过滤器 / 拦截器
- Servlet
Filter或 SpringHandlerInterceptor记录 HTTP 请求 URL、IP、耗时
方案四:网关 / 代理层
- 在 Nginx、API Gateway 记录访问日志,业务代码零改动
方案五:链路追踪
- 接入 SkyWalking、Zipkin,自动采集调用链
实施建议
| 优先级 | 内容 |
|---|---|
|
P0 |
异常必须 |
|
P1 |
关键业务节点(下单、支付、对账) |
|
P2 |
入参出参、SQL 慢查询 |
|
P3 |
全链路 traceId(MDC) |
企业场景可提:
- 日志集中收集(ELK、Loki)
- 敏感信息脱敏(证件号、手机号)
- 与现有运维监控平台对接
NO3 . 业务Redis有一个大Value,如何不影响业务正常使用,把它删掉?
生产删 Redis 大 Value,核心是 避免 DEL 阻塞单线程。
首选 UNLINK 异步删除;若 Key 仍在使用,先 业务切到新 Key 或做双写,确认无流量后再删。
Hash/Set 等超大集合用 SCAN 分批删除。
操作选低峰、开监控、集群注意分片,必要时先备份。
根本治理是 避免大 Value:控制缓存粒度、压缩、拆分 Key、设置合理 TTL
NO4 . 做项目如何正确的做技术选型
设计流程(结构化回答):
1. 需求分析 → 功能、非功能(QPS、可用性、安全)
2. 领域建模 → 模块划分、边界
3. 架构选型 → 单体 / 微服务 / 分层
4. 技术选型 → 语言、框架、DB、中间件
5. 接口设计 → REST/API 规范、文档
6. 数据设计 → ER、索引、分库分表
7. 非功能 → 日志、监控、部署、灾备
8. 迭代计划 → MVP、里程碑
技术选型考虑因素:
| 因素 | 示例 |
|---|---|
|
团队熟悉度 |
东航 Oracle 存量多,可延续 Oracle 或信创达梦 |
|
业务规模 |
小项目单体 Spring Boot + MySQL 够用 |
|
性能要求 |
高并发加 Redis、消息队列、读写分离 |
|
合规 |
信创、等保、数据出境 |
|
运维成本 |
云原生 vs 传统机房 |
|
生态 |
Spring 生态成熟,文档和社区好 |
示例:
Spring Boot + Spring Cloud + Oracle/达梦 + Redis + Kafka + Nacos + Gateway;
日志 ELK,监控 Prometheus;容器 K8s 部署
NO5. Spring IOC 和 AOP 原理
IOC(控制反转)
- 传统:对象自己
new依赖 - IOC:容器负责创建对象、注入依赖(依赖注入 DI)
原理:
- 扫描
@Component、@Service等 - 解析 Bean 定义(BeanDefinition)
- 通过反射创建 Bean,处理
@Autowired/@Resource注入 - 单例默认缓存在
Singleton容器
相关点: @Configuration、@Bean、@Scope(singleton/prototype)、循环依赖(三级缓存)、ApplicationContext vs BeanFactory
AOP(面向切面编程)
- 目的:把日志、事务、权限、性能监控等横切逻辑从业务代码中剥离
原理:
- 动态代理:接口用 JDK 动态代理;无接口用 CGLIB 子类代理
- 核心概念:切点(Pointcut)、通知(Advice:Before/After/Around)、切面(Aspect)、连接点(JoinPoint)
常见应用:
@Transactional事务管理@Cacheable缓存- 自定义
@Log注解记录操作日志
口述示例:
Spring 在 Bean 初始化后,对需要代理的 Bean 包装一层代理对象;调用方法时先走切面逻辑,再调用目标方法。
NO6 . 微服务架构
定义: 将单体应用拆成多个独立部署、独立扩展、松耦合的小服务,每个服务负责单一业务能力。
核心要点:
客户端 → API 网关 → 微服务集群
↓
注册中心(Nacos/Eureka)
↓
配置中心、链路追踪、熔断限流
| 维度 | 常见技术 |
|---|---|
|
通信 |
REST、Feign、gRPC、消息队列(Kafka/RocketMQ) |
|
注册发现 |
Nacos、Eureka、Consul |
|
网关 |
Spring Cloud Gateway、Kong |
|
配置 |
Nacos Config、Apollo |
|
熔断限流 |
Sentinel、Hystrix |
|
分布式事务 |
Seata(AT/TCC/Saga)、最终一致性 + 消息 |
|
链路追踪 |
SkyWalking、Zipkin |
|
容器化 |
Docker + K8s |
优缺点(要能讲):
- 优点:独立部署、技术栈灵活、故障隔离、团队自治
- 缺点:分布式复杂度、运维成本高、数据一致性难、调用链变长
核心系统与外围系统解耦;与 Oracle 存量系统通过 API/消息集成;信创环境下中间件国产化适配。
NO7. Oracle 存储过程及相关技术点
存储过程(Procedure):预编译的 PL/SQL 块,封装业务逻辑,减少网络往返,便于权限控制和复用。
相关技术点:
| 技术 | 作用 |
|---|---|
|
PL/SQL |
过程化扩展:变量、游标、异常处理、循环 |
|
Package(包) |
规范 + 体,封装过程/函数,支持重载、私有成员 |
|
Function |
有返回值,可在 SQL 中调用 |
|
Trigger |
行级/语句级触发器,审计、同步、校验 |
|
Cursor |
显式/隐式游标,批量处理 |
|
Exception |
|
|
事务控制 |
|
|
动态 SQL |
|
|
DBMS_JOB / DBMS_SCHEDULER |
定时任务 |
|
性能 |
绑定变量、避免硬解析;批量 |
扩展问题:擅长的数据库?
我主要熟悉 Oracle,日常涉及表设计、SQL 调优、索引、执行计划、存储过程/包、分区表、物化视图等。对 SQL Server / MySQL 也有一定使用经验。
若项目以 Oracle 为主,我会重点关注:高可用(RAC/Data Guard)、大批量数据处理、存储过程维护、性能与锁问题排查。
NO8. 大表查询优化思路(Oracle & MySQL 通用 + 差异)
大表查询慢,通常卡在:全表扫描、回表多、排序/分组重、锁等待、网络传输大。优化顺序建议:
先确认慢在哪 → 再改 SQL/索引 → 再改表结构 → 最后分库分表/归档
一、优化前必做:定位瓶颈
1. 看执行计划(最重要)
| 数据库 | 命令 |
|---|---|
|
Oracle |
|
|
MySQL |
|
关注:
- 访问方式:Full Table Scan vs Index Range Scan vs Index Skip Scan
- Rows / Cardinality:预估行数是否离谱(统计信息过期)
- Extra / Operation:Using filesort、Using temporary、TABLE ACCESS FULL
- Cost / Actual Time:哪一步最耗时
2. 看 SQL 本身
- 是否
SELECT * - 是否对索引列做函数/运算:
WHERE YEAR(create_time)=2024 - 是否
%xxx%左模糊 - 是否隐式类型转换导致索引失效
- 是否 OR、NOT IN、<> 导致优化器放弃索引
- 是否深分页:
LIMIT 1000000, 20
二、通用优化手段(Oracle & MySQL 都适用)
1. 索引优化
原则:让查询走“索引范围扫描 + 尽量少回表”。
-- 差:函数导致索引失效
WHERE TO_CHAR(create_time, 'YYYY-MM') = '2024-06'
-- 好:范围查询
WHERE create_time >= TO_DATE('2024-06-01')
AND create_time < TO_DATE('2024-07-01')
联合索引(最左前缀):
-- 查询:WHERE user_id = ? AND status = ? ORDER BY create_time
-- 索引:(user_id, status, create_time)
覆盖索引(减少回表):
-- 只查索引里有的列
SELECT user_id, status, create_time
FROM orders
WHERE user_id = 100 AND status = 1;
2. 减少返回数据量
- 只查需要的列,避免
SELECT * - 加分页 / 限制条数
- 深分页改 游标分页(基于上次最大 ID):
-- MySQL 差
LIMIT 1000000, 20
-- MySQL 好
WHERE id > 1000000 ORDER BY id LIMIT 20
-- Oracle 好
WHERE id > :last_id AND ROWNUM <= 20 -- 或 OFFSET/FETCH 配合索引
3. SQL 改写
| 场景 | 优化 |
|---|---|
|
|
改 |
|
|
改 |
|
大 IN 列表 |
拆批、临时表、JOIN |
|
子查询 |
改 JOIN(视执行计划而定) |
|
先排序再过滤 |
调整 WHERE 顺序,让优化器先过滤 |
4. 统计信息及时更新
优化器靠统计信息选计划,大表变更后计划可能变差。
| 数据库 | 做法 |
|---|---|
|
Oracle |
|
|
MySQL |
|
5. 表结构层面
- 合适的数据类型(避免过大字段拖慢扫描)
- 大字段(TEXT/BLOB/CLOB)拆到附表,主表只留常用列
- 历史数据 归档/分区,查询只扫热数据
6. 架构层面
- 读写分离:报表走从库
- 汇总表 / 物化视图 / 定时任务预聚合
- 搜索引擎(ES)做复杂检索
- 分库分表(真正亿级且单表优化到头)
三、Oracle 大表优化特有手段
1. 分区表(Partitioning)
按时间、地区等分区,查询带分区键时 Partition Pruning,只扫相关分区。
-- 按月分区,查 6 月只扫 6 月分区
WHERE order_date >= DATE '2024-06-01'
AND order_date < DATE '2024-07-01'
常见:Range(时间)、List(地区)、Hash(均匀打散)。
2. 并行查询(Parallel Query)
大表全表扫描、大聚合可开并行(适合报表,OLTP 慎用):
SELECT /*+ PARALLEL(t, 4) */ COUNT(*) FROM big_table t;
3. 物化视图(Materialized View)
复杂统计预计算,定时刷新:
CREATE MATERIALIZED VIEW mv_daily_sales
REFRESH FAST ON COMMIT -- 或 ON DEMAND
AS
SELECT trunc(order_date), region, SUM(amount)
FROM orders
GROUP BY trunc(order_date), region;
4. 索引类型
| 类型 | 场景 |
|---|---|
|
B-Tree |
常规范围/等值 |
|
Bitmap Index |
低基数列(性别、状态),DW 场景 |
|
Function-Based Index |
对表达式建索引 |
|
Index Organized Table (IOT) |
主键查询为主的大表 |
5. HINT 干预(谨慎)
SELECT /*+ INDEX(t idx_order_date) */ ...
6. AWR / ASH 分析
用 AWR 报告看 Top SQL、等待事件,定位全表扫描、direct path read 等。
四、MySQL 大表优化特有手段
1. 分区表(MySQL 5.7+ / 8.0)
类似 Oracle,按 RANGE/LIST/HASH/KEY 分区,注意:
- 分区键必须在 WHERE 里才能剪枝
- 单表分区数不宜过多
- 很多场景 归档拆表 比分区更简单
2. InnoDB 引擎要点
- 必须用 InnoDB(支持事务、行锁、聚簇索引)
- 主键尽量短且递增(避免 UUID 随机主键导致页分裂)
- 二级索引叶子节点存主键,回表成本高 → 覆盖索引更重要
3. 索引下推(ICP,5.6+)
联合索引中,存储引擎层先过滤,减少回表:
Extra: Using index condition
4. MRR(Multi-Range Read,5.6+)
优化 WHERE col IN (...) 的回表顺序,减少随机 IO。
5. 索引合并 / Skip Scan
优化器可能合并多个索引;8.0 对某些场景有 Index Skip Scan。
6. 执行计划管理
- Optimizer Hints:
/*+ INDEX(t idx_xx) */ - 8.0 Index Statistics:直方图
ANALYZE TABLE生成
7. 大表 DDL 注意
- 加索引用 Online DDL(
ALGORITHM=INPLACE, LOCK=NONE)或 pt-osc/gh-ost - 避免高峰期
ALTER TABLE锁表
五、典型场景对照
场景 1:按时间查最近 7 天订单(亿级)
✓ 时间字段建索引或分区(按月/按天)
✓ WHERE create_time >= ? AND create_time < ?
✓ 只查必要列
✓ 历史数据归档到历史表
场景 2:深分页列表
✗ LIMIT 500000, 20
✓ WHERE id > :lastId ORDER BY id LIMIT 20
✓ 或搜索引擎 / 游标方案
场景 3:多条件 + 排序
✓ 联合索引 (status, create_time) 或 (user_id, create_time)
✓ 避免 filesort:让 ORDER BY 列在索引里且顺序一致
场景 4:COUNT(*) 大表
✗ 实时 COUNT 全表
✓ 汇总表 / 缓存计数
✓ 分区分别 COUNT 再汇总
✓ 近似值(Redis、表统计行数仅参考)
场景 4:报表类复杂聚合
✓ 离线/定时预聚合
✓ Oracle 物化视图 / MySQL 汇总表
✓ 走从库,必要时 Oracle Parallel
六、Oracle vs MySQL 快速对比
| 维度 | Oracle | MySQL |
|---|---|---|
|
执行计划 |
DBMS_XPLAN、AWR |
EXPLAIN / EXPLAIN ANALYZE |
|
分区 |
成熟,企业常用 |
有,但用得相对少 |
|
预计算 |
物化视图很强 |
主要靠汇总表/定时任务 |
|
并行查询 |
Parallel Query 成熟 |
8.0 有限支持 |
|
索引类型 |
B-Tree、Bitmap、函数索引 |
主要是 B-Tree(8.0 有函数索引) |
|
优化器 |
CBO 很强,统计信息关键 |
8.0 优化器明显改善 |
|
大表 DDL |
在线重定义等工具多 |
Online DDL / pt-osc |
七、面试 1 分钟口述版
大表查询优化我先看 执行计划,确认是全表扫描、回表多还是排序临时表。
然后 改 SQL:避免SELECT *、函数破坏索引、深分页;建立合适的 联合索引/覆盖索引;必要时 改写 OR、NOT IN。
表层面做 分区、归档、冷热分离;统计信息要 及时更新。
报表类用 汇总表/物化视图,亿级再考虑 分库分表。
Oracle 侧重 分区 + 物化视图 + Parallel;MySQL 侧重 InnoDB 索引设计、覆盖索引、ICP、游标分页。
更多推荐

所有评论(0)