故事的开头:面试官的“老梗”
上个月我去面了一家还不错的互联网公司,坐标杭州。项目上刚好不那么忙,我心想,去试试水,也算是看看市场行情。
面试开始五分钟,面试官就抛出了这道题:
“你讲讲Spring事务是怎么实现的?”
我当时一听,心里偷笑:这题我熟,春招秋招社招必考题,面试圈的“Hello World”。
于是我立刻答道:
“Spring事务通过AOP实现的,在方法上加上 @Transactional 注解,Spring会用代理拦截方法调用,创建事务、提交事务、回滚事务。”
面试官听着点头,但脸上没有一丝波澜。他继续追问:
“那你知道数据库层的事务是怎么实现的吗?Spring怎么协调的?”
我一愣。数据库层?不是我说完 AOP 和代理就够了吗?
我顿了几秒,开始强行接住:
“数据库本身有事务支持,Spring只是通过事务管理器调用JDBC接口…”
话没说完,面试官突然说了一句话:
“其实你刚刚说的那些都只是表象,真正的事务发生在数据库内部,是靠 redo log 和 binlog 这样的机制实现的,Spring 只是配合你演了一出戏。 ”
那一刻,我突然像是被人点醒了武林盲点。
你以为你理解了Spring事务?其实你只是看懂了表象
我们很多人,尤其是工作 1~3 年的小伙伴,可能都能说出下面这些话:
- Spring 事务使用 @Transactional 实现。
- Spring 提供 PlatformTransactionManager 管理事务。
- Spring 使用 AOP 或 CGLIB 动态代理来包裹方法,实现前置开启事务、后置提交或回滚。
没错,这都对。
但是,这只是“Spring层面的事务包装”,而不是事务的本质。
让我们打个比方:
假如Spring是剧组,@Transactional 是舞台
我们写下:
就像我们写下一个剧本:
- 主角转账
- 扣钱 -> 加钱
Spring 会出动一个“导演”TransactionManager,它会:
- 在方法执行前: 调用 JDBC 的 connection.setAutoCommit(false),开启事务
- 在方法执行后: 如果没异常,connection.commit();否则,connection.rollback()
你看,这就是我们眼里的“事务”。但你有没有想过:
Spring这么做,能保证你的数据真的一致吗?
我们都知道,事务的4大特性:ACID。
这其中的原子性和持久性,不是靠Spring实现的,而是数据库底层通过 redo log 和 binlog 硬刚出来的。
数据库才是真正的“事务执行者”
让我们来看看数据库是怎么处理事务的。
我们以 MySQL 的 InnoDB 存储引擎为例。
1、redo log:持久化的保障者
当你开启一个事务后,在执行 update、insert、delete 等操作时,数据库其实并没有立即改动磁盘上的数据页。
它只是:
- 先在内存(buffer pool)里改数据
- 再写入一份 redo log(重做日志)
只有等你执行 commit 时:
- 数据库会将 redo log 标记为已提交
- 后台线程慢慢把脏页刷新到磁盘
也就是说,redo log 才是事务是否成功的关键。
哪怕宕机了,只要 redo log 在,数据库就能“重做”你的操作。
2、undo log:回滚的守护者
而在执行每一个数据修改时,数据库会记录对应的undo log,也就是“怎么把这一步撤销”。
如果事务执行中途抛出异常,Spring调用了rollback(),数据库就通过 undo log 让数据恢复原样。
3、binlog:MySQL 的官方记账本
除了 redo log 和 undo log,还有一个经常被提到的 binlog(归档日志),它是 MySQL Server 层记录的日志,用于主从同步、数据恢复等。
但你要记住一件事:
- redo log 是 InnoDB 引擎层的
- binlog 是 MySQL Server 层的
二者都写成功,事务才算真正“提交成功”。
Spring事务只是“导演”,不是演员
那我们回过头来说说,Spring 到底干了什么?
答案很简单:
Spring 是一个“事务协调器”。
它通过声明式或编程式事务管理,告诉数据库:
“老哥,接下来我要演一场戏,你看着点,该加锁就加锁,该提交就提交。”
Spring事务的实现流程:
- 拦截方法调用(AOP)
- 获取当前数据源连接
- 调用 connection.setAutoCommit(false) 开启事务
- 调用目标方法
- 若无异常,执行 commit();否则 rollback()
- 清理资源,释放连接
这一切,背后的关键类是:
- TransactionInterceptor(事务拦截器)
- DataSourceTransactionManager(事务管理器)
- TransactionSynchronizationManager(事务同步器)
而这些类本身,并不负责数据一致性。
它们只是告诉数据库该干什么,真正的数据安全,是数据库层做的。
为啥说“没有数据库事务支持,Spring啥也干不了”?
假设你用的是一个不支持事务的数据库(比如一些 NoSQL 数据库),你在方法上加再多的 @Transactional 都没用。
因为:
- Spring 调用 setAutoCommit(false) 报错
- 调用 rollback() 没效果
- 数据库根本没有 redo log,无法回滚!
所以说到底:
Spring 是事务的“外壳”,数据库才是事务的“核心”。
我们不能把“事务的一切”都归功于Spring。
面试官说的话,值得反复琢磨
回到我的面试现场,当我听完面试官的一句话之后,感觉就像是被打通了任督二脉。
他说:
“真正能保障事务一致性的,是数据库引擎内部对日志的使用。而 Spring,只是帮你把这些逻辑用优雅的方式封装了一下。”
听懂这句话,才算是“真正理解了Spring事务”。
总结一下,真正能打的知识点来了!
如果你也在准备 Java 后端社招,这些点一定要掌握:
1. Spring事务的三种方式:
- 编程式事务(繁琐,灵活)
- 声明式事务(常用,基于注解)
- XML配置事务(古早风格)
2. 核心类和接口:
- PlatformTransactionManager
- DataSourceTransactionManager
- TransactionDefinition
- TransactionStatus
- TransactionInterceptor
3. 数据库事务的实现:
- redo log:持久化保证
- undo log:回滚保证
- binlog:主从/恢复记录
4. 常见面试深入问题:
- @Transactional 为啥加在 private 方法上不生效?
- 多线程中事务怎么保证一致性?
- Spring事务传播机制有哪几种?
- Spring事务和数据库隔离级别如何协同?
写在最后:别再被“表象”骗了
现在回头看,很多技术我们以为懂了,其实只是“听懂了名词,没理解机制”。
就像 Spring 事务。
它的本质,其实就是你和数据库的一场“双人舞”。
Spring负责指挥,数据库负责执行。没有 redo log、undo log、binlog 的支持,Spring只是“空喊口号”。
END
希望这篇文章,能帮你真正理解事务背后的那些机制,也希望你下次面试能“打到核心”。
如果你喜欢这类“故事+技术”的风格,点个在看或者转发给你的技术伙伴吧,我们一起成长!
我是小米,一个喜欢分享技术的31岁程序员。如果你喜欢我的文章,欢迎关注我的微信公众号“软件求生”,获取更多技术干货!