事务

事务是什么?

事务是应用程序中一系列严密的操作,所有操作必须完成,否则在所有操作中所做的所有更改都会撤消。也就是说事务具有原子性,一个事务中的一系列操作要么全部成功,要么一个都不做。


ACID

数据库事务正确执行的 4 个基本要素。ACID:原子性(Atomicity),一致性(Correspondence),隔离性(Isolation),持久性(Durability)。

  1. 原子性:整个事务中的全部操作,要么全部完成,要么全部不完成,不可能停滞在中间某个环节。事务在中间某个过程中发生错误,会被回滚到事务开始前的状态,就像这个事务从来没有被执行过一样。
  2. 一致性:在事务开始之前,和事务结束之后,数据库的完整性约束没有没破坏。
  3. 隔离性:隔离状态执行事务,使它们好像是系统在给定时间内执行的唯一操作。如果有两个事务,运行在相同时间内,执行相同的功能,事务的隔离性将确保每一事务在系统中认为只有该事务在使用系统。这种属性有时称为串行化,为了防止事务操作间的混淆,   必须串行化或序列化请 求,使得在同一时间仅有一个请求用于同一数据。
  4. 持久性:在事务完成之后,该事务对数据所做的更改便持久的保存在数据库之中,并不会被回滚。

在 mysql 通过日志来保证 ACID 特性:

  1. A 原子性由 undo log 保证,它记录了需要会滚的日志信息,事务回滚时,撤销已经执行成功的 sql。
  2. C 一致性一般由代码层面来保证。
  3. I 隔离性由 MVCC 来保证。
  4. D 持久性由内存+redo log 保证,mysql 修改数据同时在内存和 redo log 记录这次操作,事务提交时通过 redo log 刷盘,宕机时可以从 redo log 恢复。

log 日志


并发产生的问题

总共会产生 5 种问题:第一类丢失更新,第二类丢失更新,脏读,不可重复读,幻读。

  1. 第一类丢失更新:回滚丢失,撤销一个事务的时候,把另一个事务已提交的数据覆盖了。
  2. 第二类丢失更新:覆盖丢失,当两个或多个事务查询数据,每个基于自己的查询结果做修改时,其中一个会被另一个覆盖。
  3. 脏读:指事务 A 读到了事务 B 还没有提交的数据。
  4. 不可重复读:事务 A 多次读取同一数据,事务 B 在事务 A 多次读取的过程中,对数据作了更新并提交,导致事务 A 多次读取同一数据时,结果不一致。
  5. 幻读:系统管理员 A 将数据库中所有学生的成绩从具体分数改为 ABCDE 等级,但是系统管理员 B 就在这个时候插入了一条具体分数的记录,当系统管理员 A 改结束后发现还有一条记录没有改过来,就好像发生了幻觉一样,这就叫幻读。

隔离级别

事务的隔离级别就是为了解决上面的问题诞生的,隔离级别越高,并发下产生的问题就越少,但同时付出的性能消耗就越大,很多时候必须在并发性和性能之间做一个权衡。隔离级别有 4 种,但 spring 会提供 5 种:

  1. DEFAULT:默认隔离级别,每种数据库支持的事务隔离级别不一样,如果 Spring 配置事务时将 isolation 设置为这个值的话,那么将使用底层数据库的默认事务隔离级别。顺便说一句,如果使用的 MySQL,可以使用”select @@tx_isolation“来查看默认的事务隔离级别。这里的隔离级别是 spring 的隔离级别。
  2. READ_UNCOMMITTED:读未提交,即能够读取到没有被提交的数据,所以很明显这个级别的隔离机制无法解决脏读、不可重复读、幻读中的任何一种,因此很少使用。
  3. READ_COMMITED:读已提交,即能够读到那些已经提交的数据,自然能够防止脏读,但是无法限制不可重复读和幻读。
  4. REPEATABLE_READ:重复读取,即在数据读出来之后加锁,类似”select * from XXX for update”,明确数据读取出来就是为了更新用的,所以要加一把锁,防止别人修改它。REPEATABLE_READ 的意思也类似,读取了一条数据,这个事务不结束,别的事务就不可以改这条记录,这样就解决了脏读、不可重复读的问题,但是幻读的问题还是无法解决。mysql 中默认就是可重复读的隔离级别。
  5. SERLALIZABLE:串行化,最高的事务隔离级别,不管多少事务,挨个运行完一个事务的所有子事务之后才可以执行另外一个事务里面的所有子事务,这样就解决了脏读、不可重复读和幻读的问题了。

隔离级别对并发问题的解决(是表示会发生)

隔离级别脏读不可重复读幻读加锁读
READ_UNCOMMITTED
READ_COMMITED
REPEATABLE_READ
SERLALIZABLE

一般使用 READ_COMMITED,解决脏读,再通过其他办法解决不可重复读与幻读。

查看与修改

查看事务隔离级别使用 select @@tx_isolation。
修改当前会话事务隔离级别使用 SET session TRANSACTION ISOLATION LEVEL Serializable;
修改当前全局事务隔离级别使用 SET global TRANSACTION ISOLATION LEVEL Serializable;,修改全局的对所有会话有效,当前已经存在的会话不受影响。