mysql中的事务

一、什么是事务

事务(Transaction)是一组 SQL 操作的集合,这些操作要么全部成功执行 ,要么全部回滚,不允许只执行一半。经典例子就是银行转账------A 扣钱和 B 加钱必须同时成功或同时失败。

二、ACID 四大特性

这是事务最核心的概念,面试必考。

三、事务控制语句

sql

复制代码
-- 方式一:手动控制
START TRANSACTION;   -- 或者 BEGIN
UPDATE accounts SET balance = balance - 500 WHERE id = 1;
UPDATE accounts SET balance = balance + 500 WHERE id = 2;
COMMIT;              -- 提交,所有操作永久生效
-- ROLLBACK;         -- 回滚,所有操作撤销

-- 方式二:查看/关闭自动提交
SELECT @@autocommit;       -- 默认为 1(开启)
SET autocommit = 0;        -- 关闭自动提交,需手动 COMMIT

**保存点(SAVEPOINT)**允许部分回滚,不必回到事务起点:

复制代码
START TRANSACTION;
INSERT INTO orders VALUES (1, 'A');
SAVEPOINT sp1;                    -- 设置保存点
INSERT INTO orders VALUES (2, 'B');
ROLLBACK TO SAVEPOINT sp1;        -- 只回滚到 sp1,第一条 INSERT 保留
COMMIT;

回滚 = 撤销本次事务里已经执行过的所有 SQL,让数据回到事务开始前的样子。

就像你写了一半的字,发现写错了,直接橡皮擦全部擦掉,跟没写过一样。

四、并发问题

多个事务并发执行时,如果不加控制,会出现以下三类经典问题:

sql

不可重复读和幻读的区别:前者是同一行数据两次读到不同的 ,后者是同一查询条件两次读到不同数量的

问题 核心描述
脏读 读了别人没提交的(可能回滚的)数据
不可重复读 同一事务内,同一条数据前后读到的值不一样(被改并提交了)
幻读 同一事务内,同一查询条件前后查到的行数不一样(被插入或删除了)

1. 脏读(Dirty Read)

定义 :事务 A 读取了事务 B 已修改但尚未提交的数据。随后事务 B 发生回滚,导致事务 A 读取到的数据是"无效的"或"从未真实存在过的"。

示例场景
时间 事务 A(查询) 事务 B(更新)
T1 开始事务 开始事务
T2 将账户余额从 100 改为 200
T3 读取余额:200
T4 回滚(余额恢复 100)
T5 提交事务(基于 200 做后续计算)

后果:事务 A 基于一个"脏数据" 200 进行了业务操作,导致数据逻辑错误。


2. 不可重复读(Non-Repeatable Read)

定义 :事务 A 内多次读取同一行数据 ,但在两次读取之间,该行数据被事务 B 修改并提交 ,导致事务 A 前后读取的值不一致

示例场景
时间 事务 A(两次读取) 事务 B(修改并提交)
T1 开始事务
T2 读取余额:100
T3 开始事务
T4 将余额改为 200
T5 提交事务
T6 再次读取余额:200
T7 提交事务

后果:事务 A 在同一个逻辑上下文中看到数据"变了",违反了事务内部的一致视图原则。


3. 幻读(Phantom Read)

定义 :事务 A 内多次查询符合某条件的记录集 (范围查询),在两次查询之间,事务 B 插入或删除了符合该条件的行 并提交,导致事务 A 前后看到的记录数量(或集合)不一致

示例场景
时间 事务 A(范围查询) 事务 B(插入)
T1 开始事务
T2 SELECT * FROM t WHERE age>18 → 10 条
T3 开始事务
T4 INSERT INTO t (age) VALUES (20)
T5 提交事务
T6 SELECT * FROM t WHERE age>1811 条
T7 提交事务

后果:就像出现了"幻觉"一样,多出了之前不存在的数据行。

五、隔离级别

SQL 标准定义了四个隔离级别,级别越高越安全,但并发性能越低:

MySQL InnoDB 默认隔离级别是可重复读(REPEATABLE READ) 。表中"基本无"幻读,是因为 InnoDB 通过 MVCC + 间隙锁(Gap Lock) 在这个级别下解决了大多数幻读场景,但在某些情况下(比如当前读)仍然可能出现。

查看和设置隔离级别的命令:

sql

复制代码
-- 查看当前隔离级别
SELECT @@transaction_isolation;  --mysql80 默认为repeatable readl

-- 设置当前会话的隔离级别
SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED;

set session transaction isolation level repeatable read;

-- 设置全局隔离级别
SET GLOBAL TRANSACTION ISOLATION LEVEL REPEATABLE READ;

六、完整的转账事务示例

把以上所有知识点串联起来:

sql

复制代码
-- 确保关闭自动提交
SET autocommit = 0;

START TRANSACTION;

-- 检查余额
SELECT balance FROM accounts WHERE id = 1 FOR UPDATE;  -- 加排他锁

-- 扣款
UPDATE accounts SET balance = balance - 500 WHERE id = 1;

-- 收款
UPDATE accounts SET balance = balance + 500 WHERE id = 2;

-- 检查余额是否合法(一致性由应用层保证)
-- 如果余额不够,回滚
-- ROLLBACK;

COMMIT;  -- 持久化到磁盘(redo log 保证)

知识点总结

维度 要点
ACID 原子/一致/隔离/持久,A靠undo log,D靠redo log
并发问题 脏读 > 不可重复读(行值变)> 幻读(行数变)
隔离级别 默认 REPEATABLE READ,InnoDB用MVCC解决幻读
控制语句 BEGIN → 操作 → COMMIT / ROLLBACK
保存点 SAVEPOINT 支持部分回滚
自动提交 autocommit=1 默认开启,每条语句都是独立事务

测试过程:

问题:

我执行完select* from account,在这之前没有commit的结果是第二张图,但是我在datagrid后面真实查看的时候是第三张图,为什么会这样?

回答:

你在客户端(DataGrip 的 SQL 编辑器)里执行的 select * from account 看到的是 1900/2100,因为那条 SELECT 和前面的两条 UPDATE 在**同一个会话(session)**里,读到了同一个事务内还未提交的修改。

而 DataGrip 的 Data 面板 (第三张图那种表格视图)用的是另一个独立的数据库连接去查询数据,它看到的是已提交到数据库的数据。由于你没有执行 COMMIT,两条 UPDATE 的修改对其他连接是不可见的,所以它看到的仍然是初始值 2000/2000。

用一张图来理解:

这正是 MySQL **隔离性(Isolation)**在起作用:一个事务未提交的数据,对其他事务不可见(MySQL 默认隔离级别是可重复读)。

解决方法

执行完两条 UPDATE 之后,加上 COMMIT 即可:

复制代码
insert into account (id, name, money) values (null, '张三', 2000), (null, '李四', 2000);

start transaction;

update account set money = money - 100 where name = '张三';
update account set money = money + 100 where name = '李四';

commit;  -- 加上这行,Data 面板就能看到 1900/2100 了

select * from account;

提交之后,DataGrip 的 Data 面板刷新就会和编辑器里查到的结果一致了。

总结:

相关推荐
身如柳絮随风扬13 小时前
Redis 主从复制与哨兵机制详解:从原理到高可用实战
数据库·redis·缓存
冰小忆13 小时前
类变量在继承场景下的初始化规则是怎样的?
java·前端·数据库
YL2004042613 小时前
MySQL-运维篇-主从复制
运维·数据库·mysql
Wzx19801213 小时前
MySQL什么时候索引失效反而提升效率?
数据库·mysql
AI人工智能+电脑小能手13 小时前
【大白话说Java面试题 第71题】【Mysql篇】第1题:索引是什么?
java·开发语言·b树·mysql·面试
AC赳赳老秦13 小时前
OpenClaw碎片时间利用:设置轻量化自动化任务,高效利用职场碎片时间
java·大数据·运维·服务器·数据库·自动化·openclaw
5201-13 小时前
向量数据库在 NPU 上的加速
数据库·pytorch·python
sukioe14 小时前
Linux RPM 方式安装 MySQL 8.0
linux·mysql·adb
arbitrary1914 小时前
自动化业务通报系统实现
大数据·数据库·python·jupyter
米饭不加菜14 小时前
Mermaid 流程图语法参考二
数据库·流程图