事务

事务的ACID的原则

一致性(Consistency)

一致性放在最前面因为其实ACID是有逻辑关系的。一致性是其他三个性质的结果,其他三个性质都是为了一致性服务。一致性指的事务操作前和操作后,数据满足完整性约束,数据库保持一致性状态。

隔离性(Isolation)

隔离性指的是事务和事务之间的关系。指的是每个事务之间都是独立的,可以防止多个事务并发执行时由于交叉执行而导致数据的不一致。比如一个用户在执行对商品的购买的同时不会影响到其他用户对于商品的操作。

原子性(Atomacity)

原子性指的是一个事务内部之间的关系。原子性指的是该单位是不可分割的,要么全部执行成功,要么全部执行失败。也就是防止了因为意外情况数据库数据中止在中间状态。一旦执行到中间遇到意外事故,该条执行语句会选择回滚。

持久性(Durability)

持久性指的是数据库的语句一旦发生修改之后,该值就会持久的保留下来。

  • 持久性是通过 redo log (重做日志)来保证的;
  • 原子性是通过 undo log(回滚日志) 来保证的;
  • 隔离性是通过 MVCC(多版本并发控制) 或锁机制来保证的;
  • 一致性则是通过持久性+原子性+隔离性来保证;

并行事务引发的问题

脏读

脏读指的是一个事务读取到了另一个事务正在修改但还未提交的数据。如果事务A在操作数据库的时候,执行成功了还未提交,这时突然发生错误了,数据进行回滚,但是事务B读取到了修改一半的数据。这个数据就不应该存在,那么我们说此时出现了脏读的情况。事务B读取到了一个不存在的数据。

不可重复读

指的是同一个事务两次读取的数据是不一样的。这个产生的原因主要在于读取的中间可能突然有另一个事务对于数据进行修改,那么这时候事务A读取的数据就不是原子性的了,前后读取的不一样就被称为不可重复读了。

幻读

幻读指的是同一个事务两次读取的数据条目数是不同的。这个和不可重复读产生的原因类似,都是两次读取操作没有保持原子性。但区别是不可重复读主要是针对于数据的内容,而幻读主要是针对于数据的条目数量。

事务的隔离级别

读未提交(read uncommitted)

指一个事务还没提交时,它做的变更就能被其他事务看到;

读提交(read committed)

指一个事务提交之后,它做的变更才能被其他事务看到;

可重复读(repeatable read)

指一个事务执行过程中看到的数据,一直跟这个事务启动时看到的数据是一致的,MySQL InnoDB 引擎的默认隔离级别;

串行化(serializable )

会对记录加上读写锁,在多个事务对这条记录进行读写操作时,如果发生了读写冲突的时候,后访问的事务必须等前一个事务执行完成,才能继续执行;

Read View 在 MVCC 里如何工作的?

  • 针对快照读(普通 select 语句),是通过 MVCC 方式解决了幻读,因为可重复读隔离级别下,事务执行过程中看到的数据,一直跟这个事务启动时看到的数据是一致的,即使中途有其他事务插入了一条数据,是查询不出来这条数据的,所以就很好了避免幻读问题。
  • 针对当前读(select … for update 等语句),是通过 next-key lock(记录锁+间隙锁)方式解决了幻读,因为当执行 select … for update 语句的时候,会加上 next-key lock,如果有其他事务在 next-key lock 锁范围内插入了一条记录,那么这个插入语句就会被阻塞,无法成功插入,所以就很好了避免幻读问题。

Read View 有四个重要的字段:

  • m_ids :指的是在创建 Read View 时,当前数据库中「活跃事务」的事务 id 列表,注意是一个列表,“活跃事务”指的就是,启动了但还没提交的事务。
  • min_trx_id :指的是在创建 Read View 时,当前数据库中「活跃事务」中事务 id 最小的事务,也就是 m_ids 的最小值。
  • max_trx_id :这个并不是 m_ids 的最大值,而是创建 Read View 时当前数据库中应该给下一个事务的 id 值,也就是全局事务中最大的事务 id 值 + 1;
  • creator_trx_id :指的是创建该 Read View 的事务的事务 id。img

一个事务去访问记录的时候,除了自己的更新记录总是可见之外,还有这几种情况:

  • 如果记录的 trx_id 值小于 Read View 中的 min_trx_id 值,表示这个版本的记录是在创建 Read View 前已经提交的事务生成的,所以该版本的记录对当前事务可见。

  • 如果记录的 trx_id 值大于等于 Read View 中的 max_trx_id 值,表示这个版本的记录是在创建 Read View 后才启动的事务生成的,所以该版本的记录对当前事务不可见。

  • 如果记录的 trx_id 值在 Read View 的 min_trx_id 和 max_trx_id 之间,需要判断 trx_id 是否在 m_ids 列表中:

    • 如果记录的 trx_id 在 m_ids 列表中,表示生成该版本记录的活跃事务依然活跃着(还没提交事务),所以该版本的记录对当前事务不可见。
    • 如果记录的 trx_id 不在 m_ids列表中,表示生成该版本记录的活跃事务已经被提交,所以该版本的记录对当前事务可见。