中小厂数据库事务高频面试题

数据库事务面试题

1. 什么是数据库事务?

一句话:事务是一组要么全成功、要么全失败的操作。

展开:比如转账------A扣钱和B加钱必须同时完成,不能只扣不加。

加分点:事务保证了数据不会停留在中间状态。


2. 事务的 ACID 四大特性是什么?

A(原子性) :操作不可拆分,全做或全不做。
C(一致性) :事务前后数据约束不变(如总金额守恒)。
I(隔离性) :多个事务互不干扰。
D(持久性):提交后数据永久保存,宕机也不丢。

加分点:一致性是最终目的,AID 是为 C 服务的。


3. 什么是脏读?

一句话 :读到了别的事务还没提交的数据。

例子

事务A把余额从100改成200(未提交),事务B读到200。

如果A回滚,B读到的200就是脏数据。

加分点 :脏读是隔离级别最低时才出现的问题。


4. 什么是不可重复读?

一句话 :同一事务内,同一行数据前后读的结果不一样。

例子

事务A第一次查余额100,事务B改成200并提交,事务A再查变成200。

加分点 :侧重修改(UPDATE) ,幻读侧重新增/删除


5. 什么是幻读?

一句话 :同一事务内,两次查询返回的记录条数不一样。

例子

事务A查用户列表共10条,事务B插入一条新用户并提交,事务A再查变成11条。

加分点 :MySQL的RR级别通过间隙锁基本解决了幻读,但严格来说只在串行化下完全避免。


6. 数据库有哪几种隔离级别?

级别 脏读 不可重复读 幻读
读未提交
读已提交
可重复读 MySQL基本避免
串行化

加分点:隔离级别越强,并发性能越差。


7. MySQL 默认隔离级别是什么?解决了哪些问题?

一句话:默认可重复读(RR)。

解决了 :脏读、不可重复读。
幻读 :通过 MVCC + 间隙锁,大部分场景避免,但严格完全避免需要串行化。

加分点:面试官问"RR怎么解决幻读"时,可以说"快照读用MVCC,当前读用间隙锁"。


8. 什么是 MVCC?

一句话:多版本并发控制,让读不阻塞写,写不阻塞读。

原理:每条数据有隐藏的版本字段 + undo log 记录历史版本。

好处 :在 RR 和 RC 级别下,读操作读的是事务开始时的快照,不用加锁。

加分点:MVCC 是 MySQL InnoDB 高并发的核心机制。


9. Spring 事务管理有哪两种方式?

方式 说明 适用场景
编程式 手写 begin/commit/rollback 精细控制、复杂逻辑
声明式 @Transactional 简单易用,项目主流

加分点:实际开发几乎都用声明式,编程式很少用。


10. Spring 事务的 7 种传播机制,常用哪几个?

常用3个

机制 行为
REQUIRED 有事务就用,没有就新建(默认)
REQUIRES_NEW 挂起当前事务,新建一个独立事务
NESTED 嵌套事务,外层回滚内层也回滚,内层异常外层可选回滚

加分点:REQUIRES_NEW 和 NESTED 区别常考------前者独立提交/回滚,后者依赖于外层。


11. @Transactional 注解常用属性有哪些?

属性 作用
propagation 传播机制
isolation 隔离级别
rollbackFor 指定哪些异常回滚
noRollbackFor 指定哪些异常不回滚
timeout 超时时间(秒)
readOnly 是否只读事务(优化性能)

加分点:readOnly = true 可以走只读优化,适合查询场景。


12. @Transactional 默认对哪些异常回滚?

一句话 :只对 RuntimeException 和 Error 回滚。

受检异常 (如 IOException、SQLException)默认不回滚

解决方案 :手动指定 @Transactional(rollbackFor = Exception.class)

加分点 :这是一个高频踩坑点,面试时主动说出来很加分。


13. @Transactional 失效场景有哪些?(高频)

场景 原因
方法不是 public Spring 代理只能拦截 public
类没被 Spring 管理 没加 @Service 等
自身调用(this.方法) 没走代理
异常被 try-catch 吞掉 没抛出异常
数据库引擎不支持事务 如 MyISAM
传播机制设置错误 如改成 NOT_SUPPORTED

加分点:可以说"我在项目里遇到过自身调用失效,后来通过注入自身或放到另一个Service解决"。


14. 编程式事务怎么用?

两种方式

Java 复制代码
// 方式1:TransactionTemplate
transactionTemplate.execute(status -> {
    // 业务代码
    return result;
});

// 方式2:PlatformTransactionManager
TransactionStatus status = transactionManager.getTransaction(def);
try {
    // 业务代码
    transactionManager.commit(status);
} catch (Exception e) {
    transactionManager.rollback(status);
}

加分点 :适合一个方法中有多个事务边界复杂条件回滚的场景。


15. 分布式事务了解吗?简单说下

一句话:跨多个数据库/服务的事务,不能靠本地事务解决。

常见方案

方案 特点
2PC 强一致,但性能差、有阻塞风险
TCC 业务侵入强,性能较好
可靠消息最终一致性 实用主流,如 RocketMQ 事务消息
Seata 框架简化开发,AT模式无侵入

加分点:小厂问到这个,能说出"最终一致性 + 补偿机制"就足够,不用太深。

场景题

场景题1:下单扣库存,事务怎么加?

题目

用户下单:需要「扣库存 + 创建订单 + 扣余额」,三个操作。请问 @Transactional 应该加在哪里?有什么注意事项?

追问

  • 如果库存够、余额够,但创建订单失败了,库存和余额会回滚吗?
  • 如果库存扣成功,余额扣失败了,怎么保证一致性?

标准答案

1. 事务加在Service层方法上:

java 复制代码
@Service
public class OrderService {
    
    @Transactional(rollbackFor = Exception.class)
    public void createOrder(OrderDTO dto) {
        // 1. 扣库存
        stockService.deductStock(dto.getProductId(), dto.getQuantity());
        
        // 2. 扣余额
        accountService.deductBalance(dto.getUserId(), dto.getAmount());
        
        // 3. 创建订单
        orderMapper.insert(order);
    }
}

2. 回答追问:

  • 只要三个操作在同一个事务方法里,任何一个失败,全部回滚
  • 余额扣失败 → 库存回滚、订单不回创建 → 保证最终一致

3. 注意事项(加分点):

  • 必须指定 rollbackFor = Exception.class,防止受检异常不回滚
  • 扣库存和扣余额操作要先扣后创建订单(防止超卖)
  • 高并发场景:扣库存要用乐观锁Redis预扣 ,不能用简单update stock set num = num - 1(会超卖)

面试官想听:

  • 知道事务加在Service层
  • 知道要指定rollbackFor
  • 能说出高并发下事务的局限性(锁时间长、需要结合其他方案)

场景题2:批量导入Excel,一条失败全部回滚吗?

题目

需要批量导入1万条用户数据,要求:要么全部成功,要么全部失败。@Transactional 直接加在循环方法上可以吗?有什么问题?

追问

  • 如果改成「一条失败只跳过继续执行下一条」,怎么做?
  • 怎么避免大事务问题?

标准答案

1. 直接加@Transactional的问题:

java

java 复制代码
// ❌ 错误写法
@Transactional
public void importUsers(List<User> list) {
    for (User user : list) {
        userMapper.insert(user); // 1万次插入都在一个事务里
    }
}

问题:

  • 事务持续时间太长(可能几十秒甚至几分钟)
  • 数据库连接一直被占用
  • 锁持有时间长,容易死锁
  • 回滚日志巨大,回滚慢

2. 全部成功或全部失败的方案:

  • 可以先校验所有数据(格式、重复等)
  • 校验通过后再开启事务批量插入(用batch insert
java 复制代码
@Transactional(rollbackFor = Exception.class)
public void importUsers(List<User> list) {
    // 前置校验全部通过后
    userMapper.batchInsert(list); // 一次插入,不是循环
}

3. 一条失败只跳过继续执行的方案:

  • 不能在一个事务里
  • 编程式事务 + 记录失败日志
java 复制代码
public void importUsersWithSkip(List<User> list) {
    List<String> errorMessages = new ArrayList<>();
    for (User user : list) {
        try {
            userService.insertWithNewTransaction(user);
        } catch (Exception e) {
            errorMessages.add(user.getId() + "失败:" + e.getMessage());
        }
    }
    // 最后返回或保存错误日志
}

@Transactional(propagation = Propagation.REQUIRES_NEW)
public void insertWithNewTransaction(User user) {
    userMapper.insert(user);
}

面试官想听:

  • 知道大事务的危害
  • 知道两种需求的不同实现方式
  • 知道REQUIRES_NEW可以拆成小事务

场景题3:方法内部调用 @Transactional 为什么失效?怎么解决?

题目

下面代码,调用 orderService.create() 时,事务生效吗?调用 orderService.save() 时呢?

java 复制代码
@Service
public class OrderService {
    
    public void create(Order order) {
        this.save(order);  // 内部调用
    }
    
    @Transactional
    public void save(Order order) {
        orderMapper.insert(order);
        // 可能抛异常
    }
}

追问

  • 为什么this.save()事务会失效?
  • 给出3种解决方案

标准答案

1. 结论:

  • 调用 orderService.create() → 事务不生效
  • 直接调用 orderService.save() → 事务生效

2. 原因:

  • Spring事务通过代理实现
  • this.save() 走的是真实对象,不是代理对象,所以事务切面不执行
  • 只有通过代理对象调用方法,事务才会生效

3. 三种解决方案:

java 复制代码
// 方案1:注入自己(推荐)
@Service
public class OrderService {
    @Autowired
    private OrderService self; // 注入代理对象
    
    public void create(Order order) {
        self.save(order); // 走代理,事务生效
    }
}

// 方案2:从Spring上下文获取
public void create(Order order) {
    OrderService proxy = applicationContext.getBean(OrderService.class);
    proxy.save(order);
}

// 方案3:把事务方法放到另一个Service
@Service
public class OrderService {
    @Autowired
    private SaveService saveService;
    
    public void create(Order order) {
        saveService.save(order); // 跨Service调用
    }
}

面试官想听:

  • 知道事务失效的根本原因(代理 vs 真实对象)
  • 能给出至少2种解决方案
  • 最好能说出"项目中遇到过,用方案1解决的"

总结:小厂面试官的心理预期

场景题 能过的最低标准 加分项
下单扣库存 知道事务加在Service、会回滚 高并发优化(乐观锁)、rollbackFor
批量导入 知道大事务有问题 能说两种需求的不同方案、REQUIRES_NEW
内部调用失效 知道this.调用不生效 给出3种解法、能说原理(代理)
相关推荐
Yyyyyy~1 分钟前
【java】类和对象
java·开发语言
Allen_LVyingbo6 分钟前
医疗人工智能项目全生命周期管理系统:监管知识建模、工程实现与实证评估(下)
大数据·数据库·人工智能·python·自然语言处理·自动化
草莓熊Lotso14 分钟前
【Redis 进阶】主从复制深度解析:从配置落地到 PSYNC 同步原理
linux·开发语言·网络·数据库·redis·缓存·php
YangYang9YangYan30 分钟前
2026 校招市场数据分析 JD 拆解,SQL 要求、工具与面试考点
数据库·人工智能·数据分析
重生之小比特32 分钟前
【Java SE】抽象类和接口
java·开发语言
Lyyaoo.36 分钟前
【设计模型】策略模式
java
风哥2号39 分钟前
数据库教程FGMT40‑MySQL性能优化之性能基准测试
数据库·mysql
譕痕1 小时前
Idea 集成 智能体开发工具“千问(qwen)”
java·ide·spring·语言模型
苍梧问道1 小时前
SQL Server阻塞与死锁排查实战:从DMV到根因分析
数据库
用户8181870627461 小时前
第27章 消息丢失/重复消费的全链路排查(生产者→Broker→消费者)
java·后端