前言
MySQL 事务是比较重要且核心的一部分,在操作数据库 DML 语句时,以及开源框架基于 MySQL 进行事务操作时,保持事务的 ACID 特性是数据可靠的一大保障
事务特性
原子性(Atomicity)
一个事务必须被视为不可分割的最小单元,事务的所有操作要么全部提交成功、要么全部提交失败,对于一整个事务来说,不能只执行其中的一部分操作,例如:A 转账给 B,A 余额必须减少,B 余额必须增加
一致性(Consistency)
事务从一种状态扭转到另外一种状态,在事务开始前、结束后,数据的完整性没有被破坏
例如:A、B 事务操作前的总额是多少,转账后,事务操作后的总额也应该是一样的
隔离性(Isolation)
事务的执行不能被其他事务执行所干扰,即一个事务的执行应该与其他并发执行的事务是相互隔离的
持久性(Durability)
一旦事务被提交,对其所做的任何信息变更,都应该永久持久化到数据库中,即使系统瘫痪,已提交的数据也不会丢失
事务并发引发的问题
MySQL 基于客户端 / 服务器架构的软件,对于同一个服务器来说,可以有若干个客户端与之连接,每个客户端与服务器连接之后,就可以称之为一个会话 Session;每个客户端都可以在开启的会话中向 MySQL 服务器发出请求语句,一个请求语句可能是某个事务的一部分,也就是对于服务器来说,可以同时处理多个事务
MySQL 四大事务特性中 > 隔离性,理论上在某个事务在对数据进行访问或 DML 操作时,其他事务应该进行排队,当该事务提交之后,其他事务才可以继续访问此数据,这样的话,由并发事务执行就转变为了串行化执行;串行化执行方式对性能影响太大,既想保持事务的隔离性,又想让服务器在处理同一数据 > 多事务时
性能尽量高些,从而会为我们带来以下数据问题:脏读、不可重复读、幻读.
脏读
当一个事务读取到了另外一个事务修改但未提交的数据,称之为脏读
如上图,在事务 A 执行过程中,事务 A 对数据资源进行了修改,事务 B 读取到了事务 A 修改后的数据;可能由于某些原因,导致事务 A 没完成提交,发生了 Rollback 操作,则事务 B 读取到的数据就是脏数据
这种读取到另外一个事务未提交的数据的现象称为脏读(DD)
不可重复读
当在一个事务内的记录被检索过两次,若两次得到的结果不同,此现象称为不可重复读
事务 B 读取了两次数据,在第一次读取完准备读第二次期间,事务 A 修改了数据,导致事务 B 在第二次读出来的数据与第一次是不一致的.
幻读
在事务执行过程中,另外一个事务新增或删除了记录,正在读取记录的事务,会发生幻读.
事务 B 在前后两次读取同一个范围内的数据,在第一次读取完准备读第二次期间,事务 A 新增或删除了数据,导致事务 B 后一次读取到前一次未统计到的行数
幻读、不可重复读有些类似,但幻读重点强调了
读取到了之前没有获取到的记录
隔离级别
SQL 标准中规定了四种隔离级别:未提交读、已提交读、可重复读、可串行化读,但不同数据库厂商对 SQL 标准规定的四种隔离级别支持不一样,比如:Oracle 只支持已提交读、可串行化读两种隔离级别,MySQL 同 SQL 标准一样支持四种隔离级别,但与其不同的是,MySQL 在可重复读隔离级别下,是一大程度下是可以避免幻读问题发生的.
隔离级别 | 脏读 | 不可重复读 | 幻读 |
未提交读 READ UNCOMMITTED |
可能 | 可能 | 可能 |
已提交读 READ COMMITTED |
— | 可能 | 可能 |
可重复读 REPEATABLE READ |
— | — | SQL 标准可能 MySQL 少数场景会发生 |
可串行化 SERIALIZABLE |
— | — | — |
MySQL 默认隔离级别:REPEATABLE READ,可以手动修改事务的隔离级别
如何更改事务隔离级别
通过下面的语句可以更改事务的隔离级别:
SET [GLOBAL | SESSION] TRANSACTION ISOLATION LEVEL level;
level 可选值有四个:REPEATABLE READ、READ COMMITTED、READ UNCOMMITTED、SERIALIZABLE
设置事务的隔离级别语句中,在 SET 关键字后面可以放置 GLOBAL 关键字、SESSION 关键字,这样会对不同范围的事务产生不同的影响,具体如下:
- 使用 GLOBAL 关键字,在全局范围内生效
SET GLOBAL TRANSACTION ISOLATION LEVEL SERIALIZABLE;
对执行完该语句之后产生的会话起作用,当前已经存在的会话无效
- 使用 SESSION 关键字,在当前会话范围内生效
SET SESSION TRANSACTION ISOLATION LEVEL SERIALIZABLE;
对当前会话的所有后续事务生效,该语句可以在已经开启的事务中执行,但不会影响当前正在执行的事务
- GLOBAL、SESSION 两者都不使用,那么只会执行语句的下一个事务产生影响
SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;
对当前会话中下一个即将开启的事务有效,下一个事务执行完以后,将会恢复到默认的隔离级别,该语句不能在已经开启事务中间执行,会报错 Transaction characteristics can't be changed while a transaction is in progress
- 若想在服务器启动时想改变事务的默认隔离级别,可以修改启动参数
transaction-isolation
值,比如:在启动服务器时指定了--transaction-isolation=SERIABLIZABLE
,那么事务的默认隔离级别就会从原来的REPEATABLE READ -> SERIABLIZABLE
查看当前会话默认隔离级别可以通过查看系统变量 transaction_isolation
值或 SELECT @@transaction_isolation
来确定
mysql> SHOW VARIABLES LIKE 'transaction_isolation'; +-----------------------+-----------------+ | Variable_name | Value | +-----------------------+-----------------+ | transaction_isolation | REPEATABLE-READ | +-----------------------+-----------------+ mysql> SELECT @@transaction_isolation; +-------------------------+ | @@transaction_isolation | +-------------------------+ | REPEATABLE-READ | +-------------------------+
注意:transaction_isolation 系统变量是在MySQL 5.7.20 版本中引入来替换
tx_isolation
的,若你使用的还是之前版本,请将上述用到系统变量 transaction_isolation 地方替换为 tx_isolation
事务基本操作
事务开始:begin、start transaction(推荐)、begin work
事务回滚:rollback
事务提交:commit
事务保存点:savepoint
回滚保存点:ROLLBACK TO [SAVEPOINT] 保存点名称;
在我们进行事务操作时,操作了不同的 DML 语句,可以基于不同的语句作 savepoint
比如:插入 A 数据,执行 savepoint a、更新 B 数据,执行 savepoint b、删除 C 数据,执行 savepoint c
此时,若想取消删除 C 数据这个步骤,可以执行:
ROLLBACK TO c;
或RELEASE SAVEPOINT c;
隐式提交:是否开启隐式提交 > 取决于 autocommit ON 开或 OFF 关
当使用 START TRANSACTION 或 BEGIN 语句开启了一个事务,或者把系统变量 autocommit 值设置为 OFF 时,事务就不会进行自动提交,但是如果我们输入了某些语句之后就会悄悄的提交掉,就像我们输入了 COMMIT 语句了一样,这种因为某些特殊的语句而导致事务提交的情况称为隐式提交
会导致事务隐式提交的语句包括,如下:
- 执行 DDL 语句 > create、alter、drop,当执行这些语句时,就会隐式提交前面 SQL 语句所属的事务.
- 更新 MySQL 数据库表信息:使用 ALTER USER、CREATE USER、DROP USER、GRANT、RENAME USER、REVOKE、SET PASSWORD 等语句时也会隐式的提交前边语句所属于的事务
- 事务控制或关于锁定的语句:在一个会话中,一个事务还未提交或回滚,又使用 START TRANSACTION 或 BEGIN 语句开启了一个事务,会隐式提交上一个事务;或者使用了 LOCK TABLES、UNLOCK TABLES 等关于锁定的语句也会隐式提交前面语句所属的事务
- 加载数据语句:使用 LOAD DATA 语句来批量往数据库导入数据时,也会隐式提交前面语句所属的事务
- 其他语句:START SLAVE、STOP SLAVE、RESET SLAVE、ANALYZE TABLE、CACHE INDEX、CHECK TABLE、FLUSH 等语句也会隐式提交前面语句所属的事务