《MySQL》事务篇

事务特性

ACID

Atomicity原子性:事务中的操作要么全部完成,要么全部失败。

Consistency一致性:事务操作前后,数据满足完整性约束。

Isolation隔离性:允许并发执行事务,每个事务都有自己的数据空间,不会影响其他事务。

Durability持久性:事务操作完后,对数据的修改是永久的。

并行事务问题

1、更新丢失 – 两个事务同时对同一数据进行修改,导致某一个事务的修改的数据丢失。

事务撤销造成的撤销丢失:在事务A修改后(未提交),事务B修改时失败回滚,会同时把事务的修改也回滚了。

事务提交造成的覆盖丢失:事务A修改后(未提交),事务B修改成功,且提交,导致事务A的数据被覆盖。

2、脏读 – 事务执行中读取到了其他事务未提交的数据。

3、读已提交 – 事务读取到其他事务已经提交的数据。

4、不可重复读 – 同一事务中,多次读到的同一数据返回结果不一样。

5、可重复读 – 同一事务中,多次读到的同一数据返回结果一样。

6、幻读 – 在可重复读下,事务A查询某个数据不存在,然后这个时候事务B插入了这个数据,事务A想去插入这个数据时就报错,在可重复读情况下,事务A又去查询这个数据确实不存在。

  • 不可重复读关注的是数据的变化: 一个事务内,相同的查询在不同时间点返回不同的数据。这可能是因为其他事务修改了数据,导致读取到的结果不一致。
  • 幻读关注的是数据的新增或删除: 一个事务内,相同的查询在不同时间点返回了不同数量的行。这可能是因为其他事务在这个范围内插入或删除了数据,导致读取到的结果不一致。

事务隔离级别

SQL 标准定义了四个隔离级别:

  • 读未提交(read uncommitted),指一个事务还没提交时,它做的变更就能被其他事务看到;
  • 读提交(read committed),指一个事务提交之后,它做的变更才能被其他事务看到;
  • 可重复读(repeatable read),指一个事务执行过程中看到的数据,一直跟这个事务启动时看到的数据是一致的,MySQL InnoDB 引擎的默认隔离级别
  • 串行化(serializable);会对记录加上读写锁,在多个事务对这条记录进行读写操作时,如果发生了读写冲突的时候,后访问的事务必须等前一个事务执行完成,才能继续执行;

ReadView工作模式

《MySQL》事务篇_第1张图片

ReadView中四个重要字段:

  • m_ids:当前数据库中活跃事务(已开启但未提交的事务)id的列表。
  • min_trx_id:活跃事务id列表中最小的事务id。
  • max_trx_id:创建ReadView时,数据库给之后要开启事务的id值。
  • creatot_trx_id:创建该ReadView的事务id。

聚簇索引中的两个隐藏列

  • trx_id:保存当前记录由哪个事务产生或修改的。
  • roll_pointer:每次对记录的改动都会把就版本的记录存入undo日志,该字段就是一个指向旧版本的指针。

判断记录对于某ReadView来说是否可见:

  1. 如果记录隐藏字段的trx_id < ReadView中的min_trx_id,说明记录是在ReadView创建前就有了,所以这条记录是可见的。
  2. 如果记录隐藏字段的trx_id >= ReadView中的max_trx_id,说明记录是在ReadView创建后产生的,所以这条记录是不可见的。
  3. 如果记录隐藏字段的trx_id 在min_trx_id和max_trx_id之间
    • 情况一,trx_id在m_ids中,说明这条记录还没有提交,是不可见的。
    • 情况二,trx_id不在m_ids中,说明这条记录已经提交,是可见的。

读已提交工作模式

怎么解决的读未提交?
在一个事务中每次查询操作都会生成一个ReadView。事务A读取到事务B未提交的数据,根据“判断记录对于某ReadView来说是否可见”中的3-情况一,可知事务A访问不到事务B未提交的数据,所以解决了读未提交。

可重复读工作模式

怎么解决的不可重复读?
在事务启动时生成一个ReadView,之后整个事务中都是同一个ReadView。
根据“判断记录对于某ReadView来说是否可见”中的2可知,对于其他事务已经提交的数据,在该事务中是不可见的。

可重复读未完全解决幻读

在可重复读模式下,普通的select语句是快照读。其他都是当前读。

快照读通过 MVCC 解决幻读,当前读通过 next-key lock 解决幻读。

但仍可能出现幻读场景

情况一:

  • T1 时刻:事务 A 查询 id = 5 的值不存在
  • T2 时刻:事务 B 插入一个 id = 5 的记录并提交;
  • T3 时刻:事务 A 更新 id = 5 的记录。
  • T4 时刻:事务 A 查询 id = 5 的记录,存在!

情况二

  • T1 时刻:事务 A 先执行「快照读语句」:select * from t_test where id > 100 得到了 3 条记录。
  • T2 时刻:事务 B 往插入一个 id= 200 的记录并提交;
  • T3 时刻:事务 A 再执行「当前读语句」 select * from t_test where id > 100 for update 就会得到 4 条记录,此时也发生了幻读现象。

以上的解决方法是,在事务的一开始就使用 select … for update 执行当前读。

你可能感兴趣的:(MySQL,mysql)