数据库sql&&事务
1、核心原则:这件事的成功与否,不应该影响主事务;而且要等数据库数据落库成功之后再做。
private static void runAfterCommit(Runnable action) { // 判断:当前线程是否**处于事务中、事务同步器可用** if (TransactionSynchronizationManager.isSynchronizationActive()) { // 注册一个事务回调:事务提交成功之后执行 TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronization() { @Override public void afterCommit() { action.run(); } }); return; } // 如果不在事务里:直接立刻执行 action.run(); }
isSynchronizationActive():当前事务上下文是否开启了事务同步。
普通 @Transactional 默认是开启的;有些传播行为、或者手动事务、只读事务场景下可能不一样。
afterCommit() 只有事务正常提交成功才会走;如果事务回滚了,这个回调不会执行。
回调里异常不会影响主事务 :action.run () 抛异常,外层已经提交完了,不会回滚数据库!这是设计目的,但很多人会忽略。
2、事务执行
@Transactional public void test() { sql1(); sql2(); runAfterCommit(()->{ // 提交后执行 }); sql3(); }
执行sql1\2\3,但是执行是这些 SQL 只是发给数据库执行,不等于落盘
- 谁身上有
@Transactional,谁就是事务的 “围墙”;这里是test需正常return - Spring 此时才发送 commit 指令给数据库 → 事务才算完成、数据正式提交、锁释放
开启事务时,执行sql是单事务内起作用:
- 执行 SQL(execute):数据库收到语句,做完修改,写到内存的 undo/redo 日志 + 缓冲页,当前这个事务里能查到改后的数据;但是别的会话看不到,断电了这个修改还可能丢。
- commit:告诉数据库:这个事务结束,把这次所有变更正式持久化、对外可见、锁释放。
不在事务里,单独一条 update(等DML,DDL,数据库操作语言):mysql 自动帮你 commit。
自动提交模式 autocommit=1(MySQL 默认会话是打开的),如果直接commit了,就无法rollback。如果走spring的事务,commit之前抛异常了,就能回滚。
@Transactional public void test() { // 就单独这一条update userMapper.updateById(user); // 方法正常结束 → Spring commit }
## 到 MySQL InnoDB 原理简单说 InnoDB 默认是 **事务隔离 + MVCC**: - 当你在事务里执行 `update set name='xxx' where id=1` - 数据库找到了这条记录,生成新版本数据,放到内存缓冲池 - 同时写 undo log(用来回滚)、redo log(崩溃恢复) - **在你这个事务里面 SELECT,能读到新值** - **其他连接(别的请求)去查同一条,看到的还是老版本数据** - 直到 `commit` 命令到达: - 标记事务为已提交 - 释放行锁 - MVCC 可见性变更,其他会话可以读到新版本 - 后续数据库会异步把数据刷到磁盘文件
但是DDL需注意(执行DDL,会将之前的更改一并提交):
(alter table、truncate、drop)
@Transactional public void test(){ userMapper.insert(...); // ⚠️ DDL一执行,前面insert直接提交!事务提前结束 jdbcTemplate.execute("alter table user add column ..."); // 后面再写SQL,是新事务了 }

浙公网安备 33010602011771号