MySQL事务

ACID

原子性(Atomicity)

事务包含的所有数据库操作,要么全部成功,要么全部失败回滚

一致性(Consistency)

一致性是指事务必须使数据库从一个一致性状态,变换到另一个一致性状态。一致性规定事务提交前后只存在两个状态,不会有中间状态

用通俗一点的话说,就是:一致性是指数据处于一种语义上的有意义且正确的状态。一致性是对数据可见性的约束,保证在一个事务中的多次操作的数据中间状态对其他事务不可见的。因为这些中间状态,是一个过渡状态,与事务的开始状态和事务的结束状态是不一致的

隔离性(Isolation)

当多个用户并发访问数据库时,比如同时操作一张表时,数据库为每一个用户开启的事务,不能被其他事务的操作所干扰,多个并发事务之间要相互隔离

从用户的角度,对于任意两个并发的事务,在用户看来,要么开始之前就结束了,要么结束之后才开始,这样每个事务都感觉不到有其他事务在并发地执行

但在实践中,隔离性并没有被严格遵守,具体的隔离性与隔离级别有关,这是并发与安全的trade off

持久性(Durability)

一个事务一旦被提交了,那么对数据库中的数据改变就是永久性的,即便是在数据库系统遇到故障的情况下,也不会丢失提交事务的操作

事务并发时存在的问题

脏读(Dirty Read)

脏数据,就是未提交的数据,而脏读是指在一个事务处理过程中,读取了另一个未提交的事务中的数据,比如:

(脏读触发者) (脏读受害者)
......(修改了A) ......
获取A
......(触发回滚,反悔了对A的修改) ......(使用了A)

脏读的危害:我拿到了一个数据,但这个数据在逻辑上本不应该存在

类似的,还有脏写:我写入了一个数据,但这次写入消失了

(脏写触发者) (脏写受害者)
......(修改了A: ......
...... ......(修改了A:
......(触发回滚,返回了对A的修改: ......(发现A变成了)

不可重复读(Non-repeatable Read)

一个事务先后读取同一条记录,而在事务两次读取之间,该数据被其他事务所修改,则两次读取的数据不同

...... ......
...... 获取A,值为
修改A,并commit ......
获取A,值为
......

幻读(Phantom Read)

一个事务按相同的查询条件重新读取以前检索过的数据,却发现其他事务插入了满足其查询条件的新数据

...... ......
...... 修改全部数据行
插入一行新数据 ......
...... 发现没有被修改

脏读(读取到未提交的数据)

不可重复读(单点查询操作,发现在自己未修改的情况下,两次查询结果不同)

幻读(集合查询操作,发现在自己未修改的情况下,加入了新元素)

四个隔离级别

隔离级别 脏读 不可重复读 幻读 应用
读未提交 F F F 没什么实际应用
读已提交 T F F
可重复读 T T F MySQL默认级别
串行化 T T T 通常不会使用

读未提交(Read Uncommitted)

所有事务都能看到其他未提交事务的执行结果

读已提交(Read Commited)

一个事务能看见已提交事务做出的改变,可以防止脏读问题

实现方式

  • 加锁:数据库通过加锁来实现读已提交,当一个事务开始读取数据时,数据库会对相应的数据行或者表进行锁定,以防止其他事务对该数据进行修改。只有在事务提交之后,其他事务才能访问并修改这些数据
  • 多版本并发控制(MVCC):MVCC为每个事务创建一个可见版本来实现隔离。当一个事务开始时,它只能看见已经提交的版本。其他事务对数据的修改会创建新版本,并且只有在事务提交后,其他事务才能看到这些修改

可重复读(Repeatable Read)

这是MySQL默认的事务隔离级别,它确保一个事务的多个实例在并发读取数据时,会看到同样的数据行

在一个事务中,多个实例指的是同一种事务在不同的情况下的多次发生。事务可以是指某种事件、活动、任务或过程。每当该事务发生一次,就会产生一个新的实例

例如,假设有一个名为"购买商品"的事务。每当一个人购买商品,就会创建一个新的实例。如果有多个人在同一时间内购买商品,就会有多个实例同时进行

每个实例都是独立的、具有自己的上下文和特定的属性,但它们都属于同一个事务。每个实例可以具有不同的参数、变量和结果,取决于特定的情况和上下文。多个实例的存在可以帮助我们跟踪和管理事务的不同状态,同时处理多个实例可以提高效率和并行性

实现方式

  • 快照隔离:事务开始时获取一个数据的快照,该快照代表了事务开始时表的状态。当其他事务对数据进行修改时,事务仍然使用最初的快照来读取数据,而不会看到其他事务的修改

串行化(Serializable)

这是最高的隔离级别,它通过强制事务排序,使之不可能相互冲突。它可以解决所有并发问题,但可能导致大量的超时现象和锁竞争,通常数据库不会用这个级别,我们需要其他的机制来解决这些问题:乐观锁和悲观锁

实践指南

生产环境下大多使用RC隔离级别

  • 在RR隔离级别下,存在间隙锁,导致出现死锁的几率比RC大得多
  • 在RR隔离级别下,条件列未命中索引会锁表,而在RC隔离级别下,只锁行

也就是说,RC的并发性高于RR

并且大部分情况下,不可重复读的问题是可以接受的,毕竟数据都已经提交了,读出来“落后于时代的值”,又读出来一个“新值”,是可以进行人为取舍的