常见Java后端面试(八股文)

NO 1. spring的事物,事物失效的场景

Spring 事务

核心概念

Spring 事务本质是 对数据库连接的提交/回滚做统一管理,常用 @Transactional 声明式事务,底层靠 AOP 动态代理 实现。

调用方 → 代理对象 → 开启事务 → 执行业务 → 提交/回滚

传播行为(Propagation)

传播行为 含义
REQUIRED(默认) 有事务就加入,没有就新建
REQUIRES_NEW 总是新建事务,挂起当前事务
NESTED 嵌套事务,外层回滚会带动内层
SUPPORTS 有事务就加入,没有就非事务执行
NOT_SUPPORTED 以非事务执行,挂起当前事务
MANDATORY 必须在事务中,否则抛异常
NEVER 不能在事务中,否则抛异常

隔离级别(Isolation)

对应数据库隔离级别:DEFAULTREAD_UNCOMMITTEDREAD_COMMITTEDREPEATABLE_READSERIALIZABLE

回滚规则

  • 默认:RuntimeException 和 Error 回滚

  • Checked Exception 默认 不回滚

  • 可配置:rollbackFor = Exception.classnoRollbackFor = ...

    @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、消息最终一致性等。


如何排查事务是否生效

  1. 日志开 org.springframework.transaction DEBUG
  2. 看是否有 Creating new transaction / Committing / Rolling back
  3. 打断点看调用的是 代理对象 还是 原始对象
  4. 故意抛 RuntimeException,看数据是否回滚

面试口述模板(30 秒版)

NOSpring 事务通过 AOP 代理实现,@Transactional 在方法前后做开启、提交或回滚。

失效最常见三类:同类自调用不走代理、异常被 catch 没抛出、抛了 Checked Exception 默认不回滚。

另外还有方法非 public、Bean 没被 Spring 管理、MyISAM 不支持事务、异步线程丢失事务上下文等。

实际项目里我一般避免同类自调用,统一 rollbackFor = Exception.class,关键业务用集成测试验证回滚。

NO2 . 老项目没日志,怎么让它有日志

分层次、渐进式改造:

方案一:引入日志框架(最小改动)

  1. 加依赖:logbacklog4j2(Spring Boot 默认 logback)
  2. 配置 logback-spring.xml:控制台 + 滚动文件、按天归档
  3. 全局替换 System.out.printlnlog.info/debug/error
  4. 用 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 或 Spring HandlerInterceptor 记录 HTTP 请求 URL、IP、耗时

方案四:网关 / 代理层

  • 在 Nginx、API Gateway 记录访问日志,业务代码零改动

方案五:链路追踪

  • 接入 SkyWalking、Zipkin,自动采集调用链

实施建议

优先级 内容
P0 异常必须 log.error + 堆栈
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)

原理:

  1. 扫描 @Component@Service
  2. 解析 Bean 定义(BeanDefinition)
  3. 通过反射创建 Bean,处理 @Autowired/@Resource 注入
  4. 单例默认缓存在 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 WHEN OTHERSRAISE_APPLICATION_ERROR
事务控制 COMMIT/ROLLBACK,注意自治事务 PRAGMA AUTONOMOUS_TRANSACTION
动态 SQL EXECUTE IMMEDIATE、DBMS_SQL
DBMS_JOB / DBMS_SCHEDULER 定时任务
性能 绑定变量、避免硬解析;批量 FORALL/BULK COLLECT

扩展问题:擅长的数据库?

我主要熟悉 Oracle,日常涉及表设计、SQL 调优、索引、执行计划、存储过程/包、分区表、物化视图等。对 SQL Server / MySQL 也有一定使用经验。

若项目以 Oracle 为主,我会重点关注:高可用(RAC/Data Guard)、大批量数据处理、存储过程维护、性能与锁问题排查。

NO8. 大表查询优化思路(Oracle & MySQL 通用 + 差异)

大表查询慢,通常卡在:全表扫描、回表多、排序/分组重、锁等待、网络传输大。优化顺序建议:

先确认慢在哪 → 再改 SQL/索引 → 再改表结构 → 最后分库分表/归档


一、优化前必做:定位瓶颈

1. 看执行计划(最重要)

数据库 命令
Oracle EXPLAIN PLAN FOR ... + DBMS_XPLAN.DISPLAY;或 DBMS_SQLTUNE
MySQL EXPLAIN / EXPLAIN ANALYZE(8.0.18+)

关注:

  • 访问方式: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 改写

场景 优化
OR 多条件 UNION ALL 分别走索引
NOT IN NOT EXISTSLEFT JOIN ... IS NULL
大 IN 列表 拆批、临时表、JOIN
子查询 改 JOIN(视执行计划而定)
先排序再过滤 调整 WHERE 顺序,让优化器先过滤

4. 统计信息及时更新

优化器靠统计信息选计划,大表变更后计划可能变差。

数据库 做法
Oracle DBMS_STATS.GATHER_TABLE_STATS
MySQL ANALYZE TABLE t

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、游标分页。