Spring 事务保证数据一致性
Spring 事务保证数据一致性完整详解一、底层基础数据库原生事务 ACIDSpring 本身不具备事务能力只是对 JDBC 事务、JTA 分布式事务做高层封装与管理真正原子性、隔离性由数据库 InnoDB 引擎实现。ACID 四大特性原子性 Atomic一系列操作要么全成功、要么全回滚依托undo 日志实现回滚。一致性 Consistent事务执行前后业务数据约束完全合法主键、外键、唯一索引、余额非负等是最终目标。隔离性 Isolate多事务并发互不干扰依靠MVCC 锁4 种隔离级别。持久性 Durable提交后数据永久落地依靠redo 崩溃恢复日志。二、Spring 事务两大实现方式1. 编程式事务手动控制TransactionTemplate/PlatformTransactionManager手动开启、提交、回滚灵活但侵入代码。Autowired private TransactionTemplate transactionTemplate; public void biz() { transactionTemplate.execute(status - { // 数据库操作 if(异常条件){ status.setRollbackOnly(); //强制回滚 } return null; }); }2. 声明式事务Transactional主流基于AOP 动态代理实现无侵入分为 JDK 动态代理接口、CGLIB 代理普通类。核心执行流程调用带Transactional方法Spring 生成代理对象拦截方法。代理切面TransactionInterceptor触发事务管理器。获取数据库连接关闭自动提交conn.setAutoCommit(false)。执行业务 SQL。正常走完commit 提交抛出指定异常rollback 回滚。释放连接、恢复自动提交。三、Spring 如何保障一致性分层拆解3.1 原子性保障全部成功 / 全部失败1正常提交流程AOP 拦截方法从连接池拿到 Connection关闭自动提交。执行所有 SQL 写入 InnoDB 缓冲池。方法正常结束切面触发commit()。redo 日志刷盘事务持久化binlog 同步主从。2异常回滚机制最容易踩坑默认仅对RuntimeException、Error自动回滚Checked 受检异常不回滚。// 指定所有异常都回滚 Transactional(rollbackFor Exception.class)手动强制回滚TransactionStatus.setRollbackOnly()回滚原理InnoDB 根据 undo 日志把数据修改全部撤销。经典失效场景直接破坏原子性数据不一致方法内部try-catch吃掉异常切面感知不到异常不会自动回滚。同类内方法自调用AOP 代理失效注解完全无效。多线程异步操作子线程不属于主线程事务互不影响。传播行为配置错误REQUIRES_NEW 新开独立事务。3.2 隔离性保障Spring 对接数据库 4 种隔离级别通过Transactional(isolation Isolation.XXX)设置本质是调用 JDBC 底层设置表格隔离级别脏读不可重复读幻读底层实现READ_UNCOMMITTED允许允许允许无锁控制READ_COMMITTED (MySQL 默认)禁止允许允许MVCC 快照读REPEATABLE_READ (InnoDB 默认)禁止禁止允许MVCCSERIALIZABLE全部禁止全部禁止全部禁止全表行锁MVCC 多版本并发控制不加锁读写通过 undo 版本链 read-view 实现快照读取保证同一事务多次读取数据一致。3.3 持久性保障宕机不丢数据redo 日志事务修改先写 redo 缓冲区定时刷盘数据库崩溃重启后用 redo 重做未刷入磁盘的数据。binlog 二进制日志记录所有修改语句用于主从复制、数据恢复。两阶段提交2PC保证 redo 与 binlog 一致性阶段 1事务 prepare刷 redo 日志阶段 2全部成功提交 binlog正式 commit。 宕机在 prepare 阶段直接回滚在 commit 阶段完成持久化。3.4 一致性最终业务约束合法Spring 不直接管控业务约束依靠两层保证数据库层主键、唯一约束、外键、check 约束非法操作直接抛异常触发事务回滚。业务层事务包裹完整业务逻辑扣款 加钱、主表 子表同时在一个事务内不会出现一方成功一方失败。四、事务传播机制多事务嵌套一致性控制7 种传播属性解决多个事务方法互相调用时共用事务还是新建独立事务REQUIRED默认有事务就加入没有就新建。最常用整体一个事务一荣俱荣一损俱损。REQUIRES_NEW每次新建独立事务外层回滚不影响内层已提交事务。SUPPORTS有事务就用没有就非事务运行。NOT_SUPPORTED强制非事务执行。MANDATORY必须运行在已有事务中否则报错。NEVER禁止存在事务有事务直接抛异常。NESTED嵌套事务基于 savepoint 保存点子事务可单独回滚不影响父事务。五、本地事务 vs 分布式事务跨库一致性1单一库本地事务Spring 声明式即可完全保证单 MySQL 库一个 Connection依靠 InnoDBSpring AOP 事务完全满足 ACID。2分布式场景多库、多微服务、跨服务调用单纯Transactional完全失效会出现局部提交、局部失败数据不一致需要分布式方案2PC 两段提交Seata AT 模式强一致性性能差。TCC手动编写 Confirm/Cancel/Try侵入业务高性能。SAGA长事务补偿回滚最终一致性。本地消息表 / 可靠消息队列最终一致性主流业务选型。六、Spring 事务完整执行时序总结客户端调用业务方法 → 进入 Spring AOP 代理TransactionInterceptor 拦截向 TM 申请事务资源获取 JDBC 连接关闭自动提交执行业务所有 DML无异常执行 conn.commit ()持久化数据抛出指定异常conn.rollback ()undo 日志还原数据释放连接事务结束七、高频面试核心考点Transactional 为什么同类调用失效只有外部调用才经过代理对象内部 this 调用不走 AOP 切面。默认只回滚运行时异常必须手动指定 rollbackForException.class 才能捕获所有异常。Spring 事务只是代理管控连接提交回滚底层一致性完全依赖 InnoDB 日志机制。事务超时timeout 超时后直接抛出异常回滚防止长事务占用连接。只读事务 readonlytrue数据库优化禁止修改操作。Spring Transactional 事务失效场景 复现代码 修复方案环境SpringBoot 2.7/3.x MyBatis/MyBatis-Plus MySQL InnoDB前提数据库引擎必须是 InnoDBMyISAM 不支持事务实体简单示例用户表Data public class User { private Long id; private String name; }MapperMapper public interface UserMapper { int insert(User user); }场景 1同类内部调用this 自调用最常考错误代码Service public class UserService { Autowired private UserMapper userMapper; public void outer() { // this调用不走AOP代理Transactional失效 inner(); } Transactional(rollbackFor Exception.class) public void inner() { User u1 new User(); u1.setName(张三); userMapper.insert(u1); // 抛出异常期望回滚实际不会回滚 throw new RuntimeException(出错); } }测试调用userService.outer()数据库插入成功事务不回滚。原因AOP 事务依靠代理对象this.inner()是原生对象调用没有经过TransactionInterceptor切面拦截事务逻辑完全不执行。修复方案任选其一方案 1自己注入自身推荐Service public class UserService { Autowired private UserMapper userMapper; // 注入代理对象 Autowired private UserService self; public void outer() { self.inner(); // 使用代理调用 } Transactional(rollbackFor Exception.class) public void inner() { User u1 new User(); u1.setName(张三); userMapper.insert(u1); throw new RuntimeException(出错); } }方案 2拆分到不同 Service 方案 3开启expose-proxy使用((UserService) AopContext.currentProxy()).inner()启动类需要配置spring.aop.proxy-target-classtrueaop:aspectj-autoproxy expose-proxytrue/场景 2异常被 try-catch 捕获切面感知不到异常错误代码Service public class UserService { Autowired private UserMapper userMapper; Transactional(rollbackFor Exception.class) public void addUser() { try { User u1 new User(); u1.setName(李四); userMapper.insert(u1); int i 1 / 0; // 算术异常 } catch (Exception e) { e.printStackTrace(); // 吃掉异常没有向外抛出事务不会回滚 } } }现象插入成功数据持久化不会回滚。修复两种方式方式 1catch 后重新抛出catch (Exception e) { throw new RuntimeException(e); }方式 2手动标记回滚不抛出异常也能回滚catch (Exception e) { // 手动告知事务管理器需要回滚 TransactionAspectSupport.currentTransactionStatus().setRollbackOnly(); }场景 3只抛出受检异常Exception非 RuntimeException未配置 rollbackFor错误代码// ❌ 没有 rollbackFor Transactional public void test() throws Exception { User u new User(); u.setName(王五); userMapper.insert(u); throw new Exception(受检异常); }Spring 默认规则仅 RuntimeException / Error 触发自动回滚受检异常Exception、IOException不会回滚修复Transactional(rollbackFor Exception.class)场景 4传播行为配置错误 REQUIRES_NEW 理解误区Service public class UserService { Autowired private UserMapper userMapper; Transactional(rollbackFor Exception.class) public void outer() { User u1 new User(); u1.setName(外层数据); userMapper.insert(u1); try { inner(); } catch (Exception e) { // 捕获内层异常 } // 外层正常结束外层事务提交 } Transactional(propagation Propagation.REQUIRES_NEW, rollbackFor Exception.class) public void inner() { User u2 new User(); u2.setName(内层数据); userMapper.insert(u2); throw new RuntimeException(内层异常); } }现象inner 独立事务异常回滚内层数据无记录outer 事务不受影响外层数据成功入库很多人误以为内层异常会让外层回滚REQUIRES_NEW 互相隔离场景 5方法访问权限不是 public错误代码Service public class UserService { Autowired private UserMapper userMapper; // private / protected / default 包访问权限事务失效 Transactional(rollbackFor Exception.class) private void addUser() { User u new User(); u.setName(赵六); userMapper.insert(u); throw new RuntimeException(); } }原因Spring AOP 只能拦截public方法非 public 不会生成代理增强。 ✅ 修复方法改成 public。场景 6多线程异步场景拓展高频坑Transactional(rollbackFor Exception.class) public void testThread() { new Thread(() - { User u new User(); u.setName(线程数据); userMapper.insert(u); throw new RuntimeException(); }).start(); }现象子线程抛出异常主线程事务不会回滚子线程操作独立连接不属于当前事务。事务和数据库连接绑定 ThreadLocal不同线程连接不同天然无法共享事务。 解决分布式事务方案 Seata AT / 可靠消息不要指望本地事务跨线程。配套测试 ControllerRestController RequestMapping(/tx) public class TxController { Autowired private UserService userService; GetMapping(/test1) public String test1(){ userService.outer(); return ok; } }快速排查事务失效自查清单面试可直接背诵数据库表引擎是否 InnoDBTransactional 是否加在 public 方法是否同类内部 this 调用异常是否被 try-catch 吞掉没有外抛抛出受检异常有没有配置 rollbackFor Exception.class是否多线程操作数据库传播行为 REQUIRES_NEW / NOT_SUPPORTED 是否误用是否使用不同数据源多数据源没配置事务管理器

相关新闻

Fluidstack百GW算力平台:从资源到商品的算力调度革命

Fluidstack百GW算力平台:从资源到商品的算力调度革命

上周,一条消息在技术圈和投资圈同时激起波澜:一家名为 Fluidstack 的公司宣布获得 8.3 亿美元融资,目标是部署“百 GW”级别的算力。这个数字,无论是融资额还是算力目标,都足以让任何一个关注技术基础设施的人停下来思…

2026/10/26 8:26:06 阅读更多 →
MSPM0 DMA控制器:从原理到实战,打造高性能嵌入式数据搬运系统

MSPM0 DMA控制器:从原理到实战,打造高性能嵌入式数据搬运系统

1. DMA控制器:嵌入式系统的“数据搬运工”与性能倍增器在嵌入式系统开发中,CPU常常被各种琐碎的数据搬运任务所拖累。想象一下,你的主控芯片就像一个忙碌的厨师,不仅要炒菜(执行核心算法),还要不…

2026/10/25 15:43:18 阅读更多 →
【AI大模型】万能公式:结构化提示词模板直接套用

【AI大模型】万能公式:结构化提示词模板直接套用

【AI大模型】万能公式:结构化提示词模板直接套用(含实操代码) 在AI大模型日常应用中,绝大多数用户始终停留在“即兴提问”阶段,随意输入短句、模糊指令,导致大模型输出效果极不稳定:时而内容详实专业,时而空洞敷衍、答非所问、逻辑混乱、偏离需求。很多人误以为是模型…

2026/10/25 19:40:33 阅读更多 →

最新新闻

Electron桌面应用开发实战:从环境搭建到打包发布全流程

Electron桌面应用开发实战:从环境搭建到打包发布全流程

做前端这些年,我大部分时间都在跟浏览器标签页打交道,直到第一次把桌面应用跑起来,才发现原来自己一直被限制在地址栏和刷新按钮围起来的框框里。Electron这个词,最开始是在某段时间频繁出现在同事的讨论里,一个用HTML…

2026/10/27 17:33:32 阅读更多 →
滑盖智能锁T109深度解析:结构原理、安装调试与故障排查

滑盖智能锁T109深度解析:结构原理、安装调试与故障排查

简介:亚太天能T109滑盖智能锁产品说明书是一份PDF格式的官方技术文档,主要面向智能锁安装人员、终端用户及售后维护者,帮助快速掌握产品安装、配置与常见故障处理。全包仅1个PDF文件,大小4.37MB,内容结构清晰&#xff…

2026/10/27 4:06:49 阅读更多 →
PMSM弱磁控制原理与Simulink建模:高速AGV驱动系统设计指南

PMSM弱磁控制原理与Simulink建模:高速AGV驱动系统设计指南

上个月我在调试一台高速AGV的驱动系统,整车要求空载跑到8m/s,电机转速已经拉到额定点,驱动器的输出电压也顶到了母线电压极限附近。再往上踩速度指令,转速纹丝不动,三相电流反而开始震荡。那会儿我的第一反应就是&…

2026/10/27 7:21:53 阅读更多 →
C++核心知识点:从内存管理到STL的实战指南

C++核心知识点:从内存管理到STL的实战指南

C 这门语言我前前后后折腾了十几年,用它写过嵌入式驱动、做过游戏服务端,也搭过基础组件库。每次看到新人一头扎进"C 核心知识点"这个搜索词,我其实挺感慨的——这门语言的难点从来不是语法多,而是知识点之间的耦合太深…

2026/10/26 12:00:21 阅读更多 →
T109滑盖智能锁怎么装?安装调试与故障排查全攻略

T109滑盖智能锁怎么装?安装调试与故障排查全攻略

简介:亚太天能T109滑盖智能锁产品说明书以PDF格式发布,适合智能锁用户、安装人员与售后维修人员阅读。内容围绕T109系列指纹密码刷卡锁展开,先介绍前面板、后面板、锁体、机械钥匙、电池盒盖等部件名称与功能,再给出完整安装步骤&…

2026/10/27 6:20:47 阅读更多 →
LLM知识降熵:从信息垄断到理解权下放

LLM知识降熵:从信息垄断到理解权下放

你有没有过这种经历:翻开源码注释、论文摘要或者一份专业报告,每个字都认识,连在一起却像天书。想找人聊聊,身边要么没人懂,要么懂的懒得跟你解释。后来我试着把同一段内容丢给一个大语言模型,几秒钟之后&a…

2026/10/27 4:06:50 阅读更多 →

日新闻

周新闻

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/27 13:55:19 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/27 7:21:50 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/26 8:34:37 阅读更多 →