一、什么是事务?

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

事务最经典栗子也常常被拿进去的栗子就是银行转账了。比方小明要给小红转账1000元,这个转账会波及到两个要害操作:将小明的余额减1000元,将小红的余额减1000元。万一这两个操作之间忽然呈现谬误,导致小明余额缩小然而小红余额没有减少,这种状况是必定不容许的。事务就是保障这两个要害操作要么都胜利,要么都不胜利。

二、事务的个性(ACID)

  • 原子性:事务最小的执行单位,不容许宰割。事务的原子性确保动作要么全副执行,要么全副不执行。
  • 一致性:执行事务的前后,数据保持一致。例如转账的业务中,无论事务是否胜利,转账者和收款人的总额应该是不变的。
  • 隔离性:并发拜访数据库时,一个用户的事务不应该被其余事务所影响,各并发事务之间数据库是独立的。
  • 持久性:一个事务被提交后,它对数据库中数据的扭转是长久的,即便数据库产生故障也不应该对其有影响。

三、并发事务带来的问题

在典型的应用程序中,多个事务并发运行,常常会操作雷同的数据来实现各自的工作(多个用户对同一数据进行操作)。并发尽管是必须的,然而可能会带来以下的问题:

  • 脏读(Dirty read):当一个事务正在拜访数据并且对其进行了批改,然而还没提交事务,这时另外一个事务也拜访了这个数据,而后应用了这个数据,因为这个数据的批改还没提交到数据库,所以另外一个事务读取的数据就是“脏数据”,这种行为就是“脏读”,根据“脏数据”所做的操作可能是会呈现问题的。
  • 批改失落(Lost of modify):是指一个事务读取一个数据时,另外一个数据也拜访了该数据,那么在第一个事务批改了这个数据之后,第二个事务也批改了这个数据。这样第一个事务内的批改后果就被失落,这种状况就被称为批改失落。例如:事务1读取表中数据A=20,事务2也读取A=20,事务1批改A=A-1,事务2也批改A=A-1,最终后果都是19,然而事务1的批改记录失落了。
  • 不可反复读(Unrepeatableread):指在一个事务内屡次读取同一数据,在这个事务还没完结时,另外一个事务也拜访了这个数据并对这个数据进行了批改,那么就可能造成第一个事务两次读取的数据不统一,这种状况就被称为不可反复读。
  • 幻读(Phantom read):幻读与不可反复读相似,幻读是指一个事务读取了几行数据,这个事务还没完结,接着另外一个事务插入了一些数据,在随后的查问中,第一个事务读取到的数据就会比本来读取到的多,就如同产生了幻觉一样,所以称为幻读

不可反复读和幻读区别:

不可反复读的重点是批改,幻读的重点是新增或者删除。

栗子1(同样的条件,你读取过的数据,再次读取的时候不一样了):事务1中的A学生读取本人的工资是1000的操作还没完结,事务2的B学生就批改了A学生的工资为2000,A学生再次读取本人工资的时候就变成2000了,这就是不可反复读。

栗子2(同样的条件,第1次和第2次读取进去的记录条数不一样):如果某工资表中工资大于3000的有4人,事务1读取了所有工资大于3000的人,总共查问到4条记录,这是事务2又查问了一条工资大于3000的记录,事务1再次读取查问到的记录就是5条了,这就是幻读。

四、事务隔离级别

SQL规范定义了四个隔离级别:

  • 读取未提交(READ-UNCOMMITTED):最低的隔离级别,容许读取尚未提交的数据变更,可能造成脏读、不可反复读、幻读。
  • 读取已提交(READ-COMMITTED):容许读取并发事务曾经提交的数据,能够防止脏读,然而可能造成不可反复、幻读。
  • 可反复读(REPEATABLE-READ):对同一字段屡次读取的后果都是统一的,除非自身事务批改,能够防止脏读和不可反复读,然而可能造成幻读。
  • 可串行化(SERIALIZABLE):最高的隔离级别,齐全遵从ACID的隔离级别,所以的事务顺次执行,能够防止脏读、不可反复读、幻读。
隔离级别脏读不可反复读幻读
读取未提交
读取已提交×
可反复读××
可串行化×××

MySQL InnoDB存储引擎默认的事务隔离级别是可反复读(REPEATABLE-READ),能够通过命令select @@tx_isolation;语句来查看,MySQL 8.0 该语句改为SELECT @@transaction_isolation;

mysql> SELECT @@tx_isolation;+-----------------+| @@tx_isolation  |+-----------------+| REPEATABLE-READ |+-----------------+

MySQL InnoDB存储引擎的可反复读并不能防止幻读,须要利用应用加锁读来保障,这加锁读应用到的机制就是Next-Key Locks

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

InnoDB存储引擎在分布式事务的状况下个别会用到可串行化隔离级别。

拓展一下(以下内容摘自《MySQL 技术底细:InnoDB 存储引擎(第 2 版)》7.7 章):

InnoDB存储引擎提供了对XA事务的反对,并通过XA事务来反对分布式事务的实现。分布式事务指的是容许多个独立的事务资源参加到一个全局的事务中。事务资源通常是关系型数据库系统,但也能够是其余类型的资源。全局事务要求在其中的所有参加的事务要么都提交,要么都回滚,这对事务的原有ACID要求又有了进步。另外,在应用分布式事务时,InnoDB 存储引擎的事务隔离级别必须设置为 SERIALIZABLE。

四、理论状况演示

MySQL命令行的默认配置中事务都是主动提交的,即执行SQL语句就会马上执行COMMIT操作。能够用命令START TRANSACTION开始一个事务。

咱们能够通过上面命令设置事务隔离级别。

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

咱们再来看一下咱们在实际操作中应用到的一些并发管制语句:

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

(脏读)读取未提交

(防止脏读)读取已提交

不可反复读

还是方才下面的读已提交的图,尽管防止了读未提交,然而却呈现了,一个事务还没有完结,就产生了 不可反复读问题。

可反复读

幻读

演示幻读呈现的状况

sql 脚本 1 在第一次查问工资为 500 的记录时只有一条,sql 脚本 2 插入了一条工资为 500 的记录,提交之后;sql 脚本 1 在同一个事务中再次应用以后读查问发现呈现了两条工资为 500 的记录这种就是幻读。

幻读和不可反复读有些相似之处 ,然而不可反复读的重点是批改,幻读的重点在于新增或者删除。

解决幻读的办法

  • 将事务隔离级别调整为 SERIALIZABLE
  • 在可反复读的事务级别下,给事务操作的这张表增加表锁。
  • 在可反复读的事务级别下,给事务操作的这张表增加 Next-Key Locks
阐明:Next-Key Locks 相当于 行锁 + 间隙锁
参考:https://javaguide.cn/database...