目录
- 基本概念
- 事务操作
- 自动提交事务
- 开启事务
- 提交事务
- 回滚事务
- 代码示例
- 事务的特性 ACID
- 事务的隔离级别
- 读未提交 read uncommitted
- 读已提交 read committed
- 可重复读 repeatable read
- 序列化(串行) serializable
- 操作示例
基本概念
在 MySQL 中的事务(Transaction)是由存储引擎实现的,在 MySQL 中,只有 InnoDB 存储引擎才支持事务
事务处理可以用来维护数据库的完整性,保证成批的 SQL 语句要么全部执行,要么全部不执行
事务用来管理 DDL、DML、DCL 操作,比如 insert,update,delete 语句,默认是自动提交的
事务操作
自动提交事务
之前的所有 SQL 操作也有事务,只是 MySQL 自动完成了,没执行一条 SQL 语句,MySQL 就自动提交事务,因此如果想要手动控制事务,就必须关闭 MySQL 的自动提交事务
语法:
set autocommit=0; -- 关闭自动提交
set autocommit=1; -- 开启自动提交
开启事务
任何一条 DML 语句(insert、update、delete)执行,标志事务的开启
语法:begin
或 start transaction
提交事务
成功的结束,将所有的 DML 语句操作历史记录和底层硬盘数据来一次同步
语法:commit
回滚事务
失败的结束,将所有的 DML 语句操作历史记录全部清空
语法:rollback
代码示例
create table account(id int primary key ,name varchar(20) ,money double
);
insert into account values (1,'张三',1000),(2,'李四',1000);-- 关闭自动提交
set autocommit = 0;
-- 模拟账户转账
-- 开启事务
begin;
update account set money=money-200 where name = '张三';
update account set money=money+200 where name = '李四';
-- 提交事务
commit ;
结果如下:
而如果只开启没提交的话,数据不会彻底落盘,虽然查询的数据变了,但是原表中的数据还没发生改变
begin;
update account set money=money-200 where name = '张三';
update account set money=money+200 where name = '李四';
select * from account;
这个时候回滚事务再查询数据,就会发现之前修改的操作被删除了,查询出来的数据跟原表中的数据一致
-- 回滚事务
rollback ;
select * from account;
事务的特性 ACID
原子性(Atomicity)
事务是不可分割的最小操作单元,要么全部执行,要么全部不执行。例如银行转账,扣钱与加钱操作必须同时成功或失败,不能只执行一半。
一致性(Consistency)
事务执行前后,数据库从一个合法状态转换到另一个合法状态。如转账前后,双方账户总金额不变。
隔离性(Isolation)
多个并发事务之间相互隔离,避免彼此干扰。根据隔离级别(如读未提交、读已提交、可重复读、串行化),控制事务间的可见性和冲突。
持久性(Durability)
事务提交后,对数据的修改会永久保存到存储介质(如磁盘),即使数据库崩溃也不丢失。
事务的隔离级别
读未提交 read uncommitted
一个事务可以读取另一个未提交事务的数据,是最低隔离级别,任何情况都无法保证,会造成脏读
脏读是数据库事务中的异常现象,指一个事务读取了另一个未提交事务修改的数据
场景示例:
- 事务 A 操作:执行
UPDATE account SET balance = 900 WHERE id = 1;
(将账户余额改为 900 元),但未提交事务 - 事务 B 读取:此时事务 B 执行
SELECT balance FROM account WHERE id = 1;
,读取到事务 A 未提交的 “900 元” - 事务 A 回滚:若事务 A 最终回滚,数据恢复原状(如余额仍是 1000 元),则事务 B 读取的 “900 元” 从未真正生效,属于无效的 “脏数据”
影响:脏读破坏了数据一致性,事务 B 基于未最终确定的数据执行后续操作,可能导致业务逻辑错误(如错误计算余额、触发错误流程)。通过提升事务隔离级别(如使用 “读已提交”),可避免脏读问题,确保事务仅读取已提交的数据
读已提交 read committed
一个事务需等另一个事务提交后才能读取数据,可避免脏读,但会造成不可重复读
不可重复读是数据库事务中的异常现象,指同一事务内多次读取相同数据,结果却不一致,根源是其他事务在期间提交了数据修改
场景示例:
- 事务 A 首次查询:事务 A 执行
SELECT price FROM product WHERE id = 1;
,读取商品价格为 100 元 - 事务 B 修改提交:事务 B 执行
UPDATE product SET price = 120 WHERE id = 1;
并提交,价格更新为 120 元 - 事务 A 再次查询:事务 A 再次执行相同查询,此时读到价格变为 120 元,两次结果不同
影响:该现象破坏事务一致性。如事务 A 基于首次读取的价格计算库存价值,后续因价格变动导致计算错误,影响业务逻辑正确性。通过将事务隔离级别设为 “可重复读”,可避免此问题,确保事务内读取数据始终一致
可重复读 repeatable read
在开始读取数据(事务开启)时,不再允许修改操作,可避免脏读、不可重复读,但会造成幻读
幻读是数据库事务中的一种现象,指在一个事务内,相同的查询条件多次执行查询时,由于其他事务在该期间进行了插入、删除或修改符合查询条件的数据的操作 ,导致多次查询的结果集出现差异,就好像产生了幻觉一样
场景示例:
- 事务 A 首次查询:银行系统中,事务 A 要批量处理余额大于 10 万元的账户,执行
SELECT account_id FROM accounts WHERE balance > 100000;
,查询到 10 个符合条件的账户(如 ID 为 1-10) - 事务 B 插入新账户:此时,事务 B 向系统中插入一个新账户(ID=11,余额 15 万元),并执行
INSERT INTO accounts (account_id, balance) VALUES (11, 150000);
后提交 - 事务 A 再次查询:事务 A 继续处理时,再次执行相同查询,结果变为 11 个账户(新增了 ID=11)
影响:幻读导致同一事务内多次相同查询的结果集不一致,使事务失去对数据的统一认知。例如,在银行账户批量处理场景中,事务 A 原本计划处理 10 个高余额账户,但因幻读多出 1 个新账户,导致实际处理范围与预期不符,破坏了事务执行的一致性。基于幻读结果的业务操作可能出现偏差。例如,电商库存管理中,事务 A 统计库存大于 50 件的商品后,因幻读导致部分商品被误判为有库存,最终引发超卖或库存统计错误,直接影响订单处理和销售策略。为避免幻读,开发人员需手动引入锁机制(如 SELECT … FOR UPDATE)或编写复杂查询逻辑,这会增加代码复杂度和维护成本。例如,在统计类任务中,可能需要额外的条件或锁来确保结果集稳定
Mysql 的默认隔离级别是 repeatable read
序列化(串行) serializable
最高事务隔离级别,事务串行化顺序执行,可避免脏读、不可重复读与幻读。但该级别效率低下,耗数据库性能,一般不使用
事务隔离级别 | 脏读 | 不可重复读 | 幻读 |
---|---|---|---|
读未提交 read uncommitted | √ | √ | √ |
读已提交 read committed | × | √ | √ |
可重复读 repeatable read | × | × | √ |
序列化(串行) serializable | × | × | × |
操作示例
-- 查看隔离级别
show variables like '%isolation%'; -- 设置隔离级别
/*
set session transaction isolation level 级别字符串
级别字符串:read uncommitted、read committed、repeatable read、serializable
*/
-- 设置read uncommitted
set session transaction isolation level read uncommitted; -- 设置read committed
set session transaction isolation level read committed; -- 设置repeatable read
set session transaction isolation level repeatable read; -- 设置serializable
set session transaction isolation level serializable;