什么是事务

事务是访问并可能更新数据库中各种数据项的一个程序执行单元(unit)。

事务是逻辑上的一组操作,要么都执行,要么都不执行。

事务最经典也经常被拿出来说例子就是转账了。假如小明要给小红转账1000元,这个转账会涉及到两个关键操作就是:将小明的余额减少1000元,将小红的余额增加1000元。万一在这两个操作之间突然出现错误比如银行系统崩溃,导致小明余额减少而小红的余额没有增加,这样就不对了。事务就是保证这两个关键操作要么都成功,要么都要失败。

MySQL 事务主要用于处理操作量大,复杂度高的数据。

比如说,在人员管理系统中,你删除一个人员,你既需要删除人员的基本资料,也要删除和该人员相关的信息,如信箱,文章等等,这样,这些数据库操作语句就构成一个事务!

事务的特性

一般来说,事务是必须满足4个条件(ACID):

  • 原子性(Atomicity):

    • 事务是最小的执行单位,不允许分割

    • 一个事务(transaction)中的所有操作,要么全部完成,要么全部不完成,不会结束在中间某个环节。事务在执行过程中发生错误,会被回滚(Rollback)到事务开始前的状态,就像这个事务从来没有执行过一样。

  • 一致性(Consistency)

    • 在事务开始之前和事务结束以后,数据库的完整性没有被破坏。这表示写入的资料必须完全符合所有的预设规则,这包含资料的精确度、串联性以及后续数据库可以自发性地完成预定的工作。
  • 隔离性(Isolation)

    • 并发访问数据库时,一个用户的事务不被其他事务所干扰,各并发事务之间数据库是独立的;
  • 持久性(Durability)

    • 事务处理结束后,对数据的修改就是永久的,即便系统故障也不会丢失。

并发事务带来的问题

并发处理事务时,经常回操纵相同的数据处理不同的事务,可能会产生以下问题:

  • 脏读(dirty read):当一个事务访问数据并对其进行修改后,还未提交到数据库时另一个事务访问了改数据,读到的数据是未经更新的数据(脏数据),依据脏数据进行的操作可能有误。
  • 丢失修改(Lost to modify):两个事务同时访问一个数据时,都对数据做出了修改,第一个事务所修改的数据丢失。
  • 不可重复读(Unrepeatable read):在一个事务(A)内两次读取数据的过程中另一个事务(B)访问并修改了该数据,使得 A 读取到的两次数据不一致。
  • 幻读(Phantom read):幻读与不可重复读类似。它发生在一个事务(T1)读取了几行数据,接着另一个并发事务(T2)插入了一些数据时。在随后的查询中,第一个事务(T1)就会发现多了一些原本不存在的记录,就好像发生了幻觉一样,所以称为幻读。

不可重复读和幻读的区别:

  • 不可重复读重点在修改
  • 幻读重点在添加或删除

事务隔离级别

SQL标准定义了四个事务隔离级别

  • READ-UNCOMMITTED(读取未提交):

    最低的事务隔离级别,允许读取尚未提交的数据

  • READ-COMMITTED(读取已提交):

    允许读取并发已提交的事务

  • REPEATABLE-READ(可重复读):

    对同意字段读取的结果是一致的,除非是被本身事务所修改

  • SERIALIZABLE(可串行化):

    最高隔离级别,所有事务依次执行,事物之间不能互相干扰

隔离级别 脏读 不可重复读 幻读
READ-UNCOMMITTED
READ-COMMITTED ×
REPEATABLE-READ × ×
SERIALIZABLE × × ×

MySQL InnoDB 存储引擎的默认支持的隔离级别是 REPEATABLE-READ(可重读)

我们可以通过命令SELECT @@transaction_isolation;来查看当前的事务隔离级别。

这里需要注意的是:与 SQL 标准不同的地方在于InnoDB 存储引擎在 REPEATABLE-READ(可重读)事务隔离级别下使用的是Next-Key Lock 锁算法,因此可以避免幻读的产生,这与其他数据库系统(如 SQL Server)是不同的。所以说InnoDB 存储引擎的默认支持的隔离级别是 REPEATABLE-READ(可重读) 已经可以完全保证事务的隔离性要求,即达到了 SQL标准的**SERIALIZABLE(可串行化)**隔离级别。

因为隔离级别越低,事务请求的锁越少,所以大部分数据库系统的隔离级别都是**READ-COMMITTED(读取提交内容):**,但是你要知道的是InnoDB 存储引擎默认使用 REPEATABLE-READ(可重读)并不会有任何性能损失。

InnoDB 存储引擎在 分布式事务 的情况下一般会用到**SERIALIZABLE(可串行化)**隔离级别。

实际操作

我们可以通过下面的命令来设置隔离级别。

1
SET [SESSION|GLOBAL] TRANSACTION ISOLATION LEVEL [READ UNCOMMITTED|READ COMMITTED|REPEATABLE READ|SERIALIZABLE]

控制并发的语句:

  • START TARNSACTION |BEGIN:显式地开启一个事务。
  • COMMIT:提交事务,使得对数据库做的所有修改成为永久性。
  • ROLLBACK:回滚会结束用户的事务,并撤销正在进行的所有未提交的修改。

参考

javaGuide: https://snailclimb.gitee.io/javaguide/#/docs/database/

菜鸟教程: https://www.runoob.com/mysql/mysql-transaction.html