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/7/24 19:27:48 阅读更多 →
MSPM0 DMA控制器:从原理到实战,打造高性能嵌入式数据搬运系统

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

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

2026/7/24 19:26:48 阅读更多 →
【AI大模型】万能公式:结构化提示词模板直接套用

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

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

2026/7/24 19:26:48 阅读更多 →

最新新闻

Kimi    LeetCode 3677. 统计二进制回文数字的数目 Rust实现

Kimi LeetCode 3677. 统计二进制回文数字的数目 Rust实现

这是可以直接提交到 LeetCode 的完整 Rust 实现:rust impl Solution {pub fn count_binary_palindromes(n: i64) -> i32 {if n 0 {return 1;}let mut count: i64 1; // 0 是回文数// 获取 n 的二进制位数(不含前导零)let m 64 - n.lead…

2026/7/24 19:35:50 阅读更多 →
WorkshopDL:无需Steam账号,跨平台畅享海量游戏模组的终极解决方案

WorkshopDL:无需Steam账号,跨平台畅享海量游戏模组的终极解决方案

WorkshopDL:无需Steam账号,跨平台畅享海量游戏模组的终极解决方案 【免费下载链接】WorkshopDL WorkshopDL - The Best Steam Workshop Downloader 项目地址: https://gitcode.com/gh_mirrors/wo/WorkshopDL 你是否曾经在Epic Games Store或GOG平…

2026/7/24 19:35:50 阅读更多 →
NCM文件转换:5分钟解锁网易云VIP音乐的终极指南

NCM文件转换:5分钟解锁网易云VIP音乐的终极指南

NCM文件转换:5分钟解锁网易云VIP音乐的终极指南 【免费下载链接】ncmToMp3 网易云vip的ncm文件转mp3/flac - ncm file to mp3 or flac 项目地址: https://gitcode.com/gh_mirrors/nc/ncmToMp3 你是否曾为网易云音乐的NCM加密文件而烦恼?那些只能在…

2026/7/24 19:35:50 阅读更多 →
HarmonyOS开发实战:小分享-列表性能优化——组件复用与异步渲染

HarmonyOS开发实战:小分享-列表性能优化——组件复用与异步渲染

前言 列表性能优化 是长列表场景的核心挑战,通过组件复用和异步渲染可以大幅提升滑动流畅度。小分享 App 的收藏列表、模板列表等场景需要性能优化。本篇讲解列表性能优化策略。详细 API 可参考 HarmonyOS 列表性能优化官方文档。 一、使用 LazyForEach 替代 ForE…

2026/7/24 19:35:50 阅读更多 →
开源AI模型实战:从Claude Code到语音克隆的完整部署指南

开源AI模型实战:从Claude Code到语音克隆的完整部署指南

最近在技术圈里,开源模型的热度持续攀升,但很多开发者面对众多选择时却陷入了困惑:到底该投入时间学习哪个开源模型?这些模型真的能替代商业方案吗?还是只是"看起来很美"的玩具?在与资深AI工程师…

2026/7/24 19:35:50 阅读更多 →
GraphRAG本地搜索:基于知识图谱的精准检索技术

GraphRAG本地搜索:基于知识图谱的精准检索技术

1. 项目概述:GraphRAG本地搜索的核心价值GraphRAG Local Search是一种基于知识图谱的精准检索技术,它通过构建实体为中心的索引结构,实现了传统全文检索无法达到的语义理解深度。我在金融领域问答系统项目中验证过,相比传统RAG方案…

2026/7/24 19:34:50 阅读更多 →

日新闻

用Highcharts 创建可拖拽三维散点立方体3D图表

用Highcharts 创建可拖拽三维散点立方体3D图表

该案例基于Highcharts scatter3d 三维散点图实现空间立方体散点可视化,核心特色:三维 X/Y/Z 三轴空间,所有散点分布在 0~10 立方体空间内;散点使用径向渐变实现立体 3D 圆球质感;支持鼠标 / 触屏拖拽画布,…

2026/7/24 0:00:29 阅读更多 →
AppCertDlls:进程创建路径上的 DLL 入口

AppCertDlls:进程创建路径上的 DLL 入口

AppCertDlls:进程创建路径上的 DLL 入口 AppCertDlls 位于 HKLM\System\CurrentControlSet\Control\Session Manager\AppCertDlls。本文的程序功能是只读列出这个键在 64 位和 32 位注册表视图中的全部值,并显示每条值的来源、名称、类型和可安全显示的数…

2026/7/24 0:00:29 阅读更多 →
我的编程之路:第一篇博客

我的编程之路:第一篇博客

大家好,我是一名编程初学者,同时这也是我编程学习之路上的第一篇博客。在这里,我想要向大家介绍我的一些想法和规划。a.自我介绍我是一个刚刚接触编程的新手,目前在学习c语言,我对编程世界充满了强烈的好奇。当然&…

2026/7/24 0:00:29 阅读更多 →

周新闻

Go语言静态资源打包方案对比与实践指南

Go语言静态资源打包方案对比与实践指南

1. 项目背景与核心需求在Go语言开发中,我们经常需要处理静态资源文件的打包问题。无论是Web应用的模板文件、前端资源,还是配置文件、证书等,都需要随程序一起分发。传统做法是将这些文件与编译后的二进制文件放在同一目录下,但这…

2026/7/24 3:59:20 阅读更多 →
Go语言实现高性能LDAP认证服务的架构与实践

Go语言实现高性能LDAP认证服务的架构与实践

1. 项目背景与核心价值LDAP(轻量级目录访问协议)作为企业级身份认证的黄金标准,已经服务了超过80%的财富500强公司。我在金融科技领域实施统一认证体系时,发现传统Java方案存在启动慢、内存占用高等痛点。而Go语言凭借其协程并发模…

2026/7/24 1:23:39 阅读更多 →
【AI面试官实战指南】:用ChatGPT模拟10类高频技术岗面试,3天提升应答精准度92%

【AI面试官实战指南】:用ChatGPT模拟10类高频技术岗面试,3天提升应答精准度92%

更多请点击: https://intelliparadigm.com 第一章:AI面试官实战指南的核心价值与适用场景 AI面试官并非替代人类HR的“黑箱工具”,而是以可解释、可审计、可迭代的方式,赋能招聘全链路的关键基础设施。其核心价值在于将主观经验沉…

2026/7/24 18:52:18 阅读更多 →

月新闻