☰
MySQL数据库基础——事务
2026/10/14 21:22:02 网站建设 项目流程

文章目录

  • 什么是事务?
  • 事务的ACID特性(重点)
  • 为什么要使用事务
  • 事务的使用
    • 事务语法
    • 保存点
    • 自动/手动提交事务
  • 隔离级别
  • 查看和设置隔离级别

什么是事务?

事务把一组SQL语句打包成为一个整体,在这组SQL的执行过程中,执行结果只有两种:要么全部成功,要么全部失败。这组SQL语句可以是一条也可以是多条。来看一个转账的例子,如图:

这个过程包含两个步骤(两个SQL语句),张三的余额减100、李四的余额加100,两个SQL语句执行完之后不能出现张三扣了100,但李四+100的SQL语句却执行失败了这样的情况,所以我们把这两个SQL语句打包为一个事务,使业务保持数据的完整性和安全性。

事务的ACID特性(重点)

事务的 ACID 特性指的是 Atomicity(原子性)、Consistency(一致性)、Isolation(隔离性)、Durability(持久性)

  1. Atomicity(原子性)
  • 是支持事务的数据库的最基本的一个特性。
  • 一组 SQL 要么全都执行成功,要么全都不执行,不会出现只执行一半的情况。
  • 如果事务执行过程中出现错误,事务就会回滚( rollback )到事务开始之前的状态,就像这个事务从来没有执行过一样。

依靠 undo log (回滚日志)实现

  1. Consistency(一致性)
  • 事务执行前后,数据库从一个合法一致状态,转变为另外一个合法一致的状态,保证数据正确,满足业务约束。

例:转账前后,两个人账户总金额保持不变。一致性是事务最终要达成的目标

  • 也就是事务结束之后,数据库不能出现违背业务规则的非法数据。

再比如:
数据库设置了约束,余额不能是负数。

  • 如果你执行一个事务,让账户余额变成负数,就算这条 SQL 完整跑完(满足原子性),数据库也会拒绝,保证不会出现非法数据,这就是一致性。
  • 其实一致性就是事务的最终目标,一致性就是由原子性、隔离性、持久性这三者来实现的。
  1. Isolation(隔离性)

多个事务并发执行时,事务之间相互隔离,互不干扰。

比如,如果事务的隔离性没有得到保证的话:

事务 A 修改数据,还没提交(commit);另一个事务 B 读到了这个还没提交的数据。若 A 回滚,B 读到的数据就是无效脏数据,事务之间发生干扰。

  1. Durability(持久性)

事务一旦提交,对数据库的修改就永久保存在存储介质中,就算数据库发生故障、宕机,已经提交的数据也不会丢失。

依靠 redo log (重做日志)实现

为什么要使用事务

  • 事务拥有 ACID 四大特性,用来满足业务上的数据一致性需求。
  • 数据库实现事务,简化了应用层的编程模型,不需要我们去考虑各种各样的潜在错误和并发问题(不用我们自己再在代码底层实现一遍ACID了,只要用好事务就可以实现业务的ACID特性),因此事务本质上是数据库对 ACID 模型的一个实现,是为应用层服务的。
  • 业务逻辑中一组操作,要么全部提交成功,要么全部回滚。保证业务数据不会卡在中间状态,不会出现一半成功、一半失败的脏数据。
  • 一旦 commit 完成,数据库依靠 redo log 持久化保存,所以数据库内部保证提交成功的数据即使宕机也不会丢失(不过实际上应用仍然需要处理网络异常等业务层面问题)。

查看当前MySQL支持事务的存储引擎:

  • 可以看到只有InnoDB支持事务(transaction)

事务的使用

事务语法

-- 手动开启一个事务,方式 1:starttransaction;-- 方式2:begin;-- 这里写你的多条业务SQL...-- 接下来有两种选择:-- ①:commit;-- SQL全部执行成功,提交(落盘),数据这才永久保存在储存介质中-- ②:rollback;-- 如果中间出问题,就执行回滚,所有SQL执行的修改全部撤销-- 无论执行的是 commit,还是 rollback,当前事务都会关闭,无法再对该事务进行任何操作了

保存点

在开启一个事务之后,在事务执行的过程中设置保存点,回滚时指定回滚到保存点,可以把数据恢复到保存点的状态

-- 设置保存点,名字随便取(比如 sp1)savepointsp1;
-- 回滚到保存点 sp1,而不是回滚整个事务rollbacktosp1;
  • 保存点只能在同一个事务里面生效
  • rollback to 保存点 :只撤销保存点之后的修改,事务继续保持打开,你还能继续写 SQL、继续回滚别的保存点,最后依旧需要 commit 或者完整 rollback

自动/手动提交事务

  • 默认情况下事务是自动开启、提交或回滚的,也就是说只要你写了一条SQL语句,InnoDB就会在底层隐式开启一个事务,此时该事务中就只包含这一条SQL语句,SQL执行成功就自动 commit,执行失败就自动 rollback

可以查看一下“自动提交设置”的系统变量:

  • 可以看到autocommit为on

可以手动设置该系统变量:

  • 0或off都是关闭autocommit,1或on都是开启autocommit
  • 如果关闭了自动提交,那每次写SQL语句就必须要自己commit或rollback,但是只要使用start transaction;手动开启了一个事务,无论autocommit是否开启,都要自己手动提交

注意:

  • set autocommit = 0这样的设置属于会话(session)级别,只在当前这一条数据库连接里面生效。
  • 关闭当前这个 MySQL 窗口(断开连接)直接失效​。当然重启电脑 / 重启 MySQL 服务也失效,新连接恢复默认 autocommit = 1
  • 每打开一个新的 mysql 黑窗口(也就是一个新的mysql客户端连接),就是一个全新会话,默认自动提交是打开的

为什么失效?

会话变量只是保存在内存里面,不会写入数据库的配置文件

  • 只要连接断开,这块内存直接释放,这个设置就消失​
  • 重启 MySQL 服务,服务器重新读取配置文件,全部新连接恢复默认值

所以想要永久关闭自动提交,要手动去修改 my.ini 配置文件。
(不过自动提交本来就挺好的,一般不会去关闭它)

再总结一下事务的相关注意:

  • 在一个事务A中进行数据的修改,虽然其他会话中的事务B可以正常访问到此次修改后的数据,但因为事务A的修改并没有落盘,所以不知道数据会不会继续修改或者是rollback,所以事务B拿到的很可能是错误的数据
  • 接下来介绍如何通过设置隔离级别来解决这类问题

隔离级别

  • 事务具有隔离性,那么如何实现事务之间的隔离?隔离到什么程度?如何保证数据安全的同时也要兼顾性能?

  • 事务间不同程度的隔离,称为事务的隔离级别。不同的隔离级别在性能和安全方面做了取舍,有的隔离级别注重并发性,有的注重安全性,有的则是并发和安全适中

在MySQL的InnoDB引擎中事务的隔离级别有四种,分别是(按隔离级别由低到高排序):

①:read uncommitted :读未提交
②:read committed :读已提交
③:repeatable read :可重复读 (InnoDB 默认的隔离级别)
④:serializable :串行化

  • 隔离级别越高,并发性能越差,但是越安全

在各隔离级别中存在的三个并发读问题:

①:脏读 :dirty read
②:不可重复读:non-repeatable read
③:幻读: phantom read

分别在各隔离级别中分析:

  1. read uncommitted 读未提交

别的事务还没提交,你就能读到它的修改,可能你拿到的数据还会修改或者rollback,那你拿到的数据就可能是错误的,也就是脏读。

这种隔离级别生产中几乎不会使用。

  1. read committed 读已提交(Oracle 默认)

只能读到别人已经提交的数据,不会脏读。

但是同一个事务内,进行了两次查询,中途别的事务对该数据进行了修改并且提交,这样的话对这一行数据的两次查询结果就会不一样,出现不可重复读。

  1. repeatable read 可重复读(MySQL InnoDB 默认)

使用该隔离级别,在同一个事务,多次读取同一行时,读到的永远是事务启动那一刻的数据,不会出现不可重复读。

但如果此时我们进行了前后两次范围查询,得到了两次的结果集,但是在两次范围查询中间,别的事务插入了满足条件的新数据,导致两次查询结果行数不一样,这就是幻读(可以和不可重复读区分,不可重复读是同一行数据查询结果不同,幻读是两次的查询结果集的数据条数不一样)

不过MySQL 的 InnoDB 在 repeatable read (它的默认隔离级别)中,依靠 mvcc(多版本) + 间隙锁(gap lock)来尝试解决幻读,但是它并没有从所有场景完全杜绝幻读,属于部分解决了幻读问题

  1. serializable 串行化

事务只能排队串行执行,完全没有并发问题,但是性能最差,也是几乎不用。

几点注意总结:

  1. 不要以为 repeatable read 所有数据库都能解决幻读,只有 InnoDB 依靠间隙锁做到,别的数据库 repeatable read 仍然存在幻读。
    ​

  2. 隔离级别只是定义标准,不同存储引擎对隔离级别的底层实现不一样,也就可能有不同的效果

  3. 隔离级别越高,锁越严格,并发越差

  4. 脏读一定是读到了未提交的数据。

查看和设置隔离级别

事务的隔离级别分为全局作用域和会话作用域,查看不同作用域事务的隔离级别,可以使用以下的方式:

-- 查看当前会话隔离级别select@@session.transaction_isolation;-- 查看全局隔离级别select@@global.transaction_isolation;

设置隔离级别:

①只修改当前会话:

setsessiontransactionisolationlevel隔离级别;

②修改全局(新连接才生效):

setglobaltransactionisolationlevel隔离级别;
  • 如果上面的session和global都不写的话,隔离级别的设置就只对下一个事务生效

还有其他写法:

图片中写法能看得懂就行,还是推荐最上面的写法

  • 图片中写法是直接给系统变量赋值的写法,隔离级别用字符串形式传入参数,而字符串里面不能写空格,所以用短横线代替

  • 这几种写法是等价的,都只是修改了内存参数

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询