【MySQL】事务

发布于:2025-03-23 ⋅ 阅读:(35) ⋅ 点赞:(0)

基本概念

在 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)执行,标志事务的开启

语法:beginstart 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

一个事务可以读取另一个未提交事务的数据,是最低隔离级别,任何情况都无法保证,会造成脏读

脏读是数据库事务中的异常现象,指一个事务读取了另一个未提交事务修改的数据

场景示例:

  1. 事务 A 操作:执行 UPDATE account SET balance = 900 WHERE id = 1;(将账户余额改为 900 元),但未提交事务
  2. 事务 B 读取:此时事务 B 执行 SELECT balance FROM account WHERE id = 1;,读取到事务 A 未提交的 “900 元”
  3. 事务 A 回滚:若事务 A 最终回滚,数据恢复原状(如余额仍是 1000 元),则事务 B 读取的 “900 元” 从未真正生效,属于无效的 “脏数据”

影响:脏读破坏了数据一致性,事务 B 基于未最终确定的数据执行后续操作,可能导致业务逻辑错误(如错误计算余额、触发错误流程)。通过提升事务隔离级别(如使用 “读已提交”),可避免脏读问题,确保事务仅读取已提交的数据

读已提交 read committed

一个事务需等另一个事务提交后才能读取数据,可避免脏读,但会造成不可重复读

不可重复读是数据库事务中的异常现象,指同一事务内多次读取相同数据,结果却不一致,根源是其他事务在期间提交了数据修改

场景示例:

  1. 事务 A 首次查询:事务 A 执行 SELECT price FROM product WHERE id = 1;,读取商品价格为 100 元
  2. 事务 B 修改提交:事务 B 执行 UPDATE product SET price = 120 WHERE id = 1; 并提交,价格更新为 120 元
  3. 事务 A 再次查询:事务 A 再次执行相同查询,此时读到价格变为 120 元,两次结果不同

影响:该现象破坏事务一致性。如事务 A 基于首次读取的价格计算库存价值,后续因价格变动导致计算错误,影响业务逻辑正确性。通过将事务隔离级别设为 “可重复读”,可避免此问题,确保事务内读取数据始终一致

可重复读 repeatable read

在开始读取数据(事务开启)时,不再允许修改操作,可避免脏读、不可重复读,但会造成幻读

幻读是数据库事务中的一种现象,指在一个事务内,相同的查询条件多次执行查询时,由于其他事务在该期间进行了插入、删除或修改符合查询条件的数据的操作 ,导致多次查询的结果集出现差异,就好像产生了幻觉一样

场景示例:

  1. 事务 A 首次查询:银行系统中,事务 A 要批量处理余额大于 10 万元的账户,执行 SELECT account_id FROM accounts WHERE balance > 100000;,查询到 10 个符合条件的账户(如 ID 为 1-10)
  2. 事务 B 插入新账户:此时,事务 B 向系统中插入一个新账户(ID=11,余额 15 万元),并执行 INSERT INTO accounts (account_id, balance) VALUES (11, 150000); 后提交
  3. 事务 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;

网站公告

今日签到

点亮在社区的每一天
去签到