MySQL_事务

事务的特性

ACID

  1. A原子性:一个事务中的所有操作,要么全部完成,要么全部不完成。undo log保证
  2. C一致性:事务操作前和操作后,数据满足完整性约束,数据库保持一致性状态。原子性、隔离性、持久性保证
  3. I隔离性:多个并发事务交叉执行,使用相同的数据时,互不干扰,每个事务都有一个完整的数据空间。MVCC或锁机制保证
  4. D持久性:事务处理结束后,对数据的修改是永久的,即便系统故障也不会丢失。redo log保证

并行事务会引发什么问题

  1. 脏读:一个事务读到了,另一个未提交事务修改过的数据
  2. 不可重复读:一个事务内多次读取同一个数据,前后两次读到的数据不一样
  3. 幻读:一个事务内多次查询某个符合查询条件的记录数量 ,前后两次查询到的记录数量不一致

事务的隔离级别有哪些

  1. 未提交读:
  2. 已提交读:MyISAM默认的隔离级别。彻底解决脏读
  3. 可重复读:InnoDB默认的隔离级别。彻底解决脏读、不可重复读(部分解决幻读)
  4. 串行化:彻底解决脏读、不可重复读、幻读

事务的隔离级别是如何实现的

  1. 未提交读:直接读取最新的数据。
  2. 串行化:加读写锁。
  3. 已提交读和可重复读:通过Read View来实现的。创建的时机不同。(已提交读:每个语句执行之前。 可重复读:启动事务时。

可重复读如何部分解决幻读

  1. 针对快照读SELECT:通过MVCC,事务执行过程中看到的数据,和事务启动时看到的数据时一致的。
  2. 针对当前读SELECT...FOR UPDATE:通过next-key lock(记录锁+间隙锁)阻塞其他事务的插入操作。

执行了"开始事务"就一定启动了事务吗?

不一定

  1. start/begin transaction 命令。之后执行第一条SELECE语句,才是事务真正启动的时机。
  2. start transaction with consistent snapshot命令。马上开启事务。

Read View 在MVCC里怎么工作?

Read View里的4个字段

creator_trx_id:创建该Read View的事务的id
m_ids:创建Read View时,当前数据库中活跃事务 (启动了但还没提交的事务)的事务id列表。
min_trx_id:m_ids的最小值。
max_trx_id:全局事务中最大的事务id值+1。

聚簇索引记录中的两个隐藏列

trx_id:事务id。
roll_pointer:指向undo log中的旧版本记录。

trx_id与记录对事务可见性的关系

创建Read View后,trx_id的分类:
已提交事务min_trx_id已启动但未提交的事务(m_ids) max_trx_id还没有开始的事务

  1. trx_id小于min_trx_id,该版本的记录对当前事务可见。
  2. trx_id大于max_trx_id,不可见。
  3. trx_id在之间,在m_ids列表中,不可见。
  4. trx_id在之间,不在m_ids列表中,可见。
  5. trx_id和creator_trx_id相等,可见。
什么是MVCC?

通过版本链事务的Read View里的字段记录中的两个隐藏列的对比)来控制并发事务访问同一个记录时的行为。

可重复读是如何工作的

启动事务时生成一个Read View,整个事务期间都在用这个Read View。

读记录时判断trx_id,如果小于min_trx_id ,或不在m_ids中,说明在生成Read View就已经提交修改了,可以读。否则,顺着roll_pointer在undo log中找,直到找到符合条件的trx_id。

已提交读是如何工作的

每次读取数据时,都会生成一个新的Read View。

MySQL可重复读隔离级别,完全解决幻读了吗

没有

幻读的2个例子:

  1. 对于快照读:事务A更新 了一条事务B插入的记录x,那么事务A前后两次查询的记录条目就不一样了,就发生了幻读。(记录x的trx_id变成了事务A,对事务A就可见了)
  2. 对于当前读:先快照读,其他事务插入了一条记录,再当前读,前后两次查询的记录条目不一样,就发生了幻读。(锁加晚了)

如何避免:

在开启事务后,立马执行SELECT...FOR UPDATE,对记录加锁,避免其他事务插入数据。