欲望以提升热忱,毅力以磨平高山!|

navyum

园龄:4个月粉丝:0关注:0

03.事务隔离级别

事务:

  • 事务:就是要保证一组数据库操作,要么全部成功,要么全部失败
  • 事务的特性:ACID,原子性、一致性、隔离性、持久性
  • mysql事务是由引擎提供的支持,MyISAM 引擎就不支持事务
  • 多事务同时执行可能会出现的问题:脏写、脏读、不可重复读、幻读
    • 脏读:在一个事务内读到了另一个未提交事务修改过的数据
    • 不可重复读:在一个事务内多次读取同一个数据由于其他事务的提交,导致出现前后两次读到的数据不一样的情况
    • 幻读:在一个事务内多次查询某个符合查询条件的记录数量由于其他事务的提交,出现前后两次查询到的记录数量不一样的情况

事务的状态机:

Img

事务的隔离级别:

  1. 读未提交(read uncommitted):一个事务还没提交时,它做的变更能被别的事务看到
  2. 读提交(read committed):一个事务提交之后,它做的变更才会被其他事务看到
  3. 可重复读(repeatable read):一个事务执行过程中看到的数据,总是跟这个事务在启动时看到的数据是一致的。在可重复读隔离级别下,未提交的变更对其他事务是不可见的。
  4. 串行化(serializable):对于同一行记录“写”会加“写锁”,“读”会加“读锁”。当出现读写锁冲突的时候,后访问的事务必须等前一个事务执行完成,才能继续执行
  5. InnoDB引擎的默认隔离级别是可重复读,但无法完全避免幻读 针对幻读的的避免措施
    1. 快照读:通过MVCC机制,创建一致性视图,确保其他事务不影响当前事务的记录数量
    2. 当前读:通过next-key lock(记录锁+间隙锁),阻塞插入操作

关于隔离级别的例子:

图片 1. 若隔离级别是“读未提交”,V1=2,V2=V3=2。事务B虽然还没有提交,但是结果已经被事务A看到了。 2. 若隔离级别是“读提交”,V1=1,V2=V3=2。事务B的更新在提交后才能被A看到。 3. 若隔离级别是“可重复读”,V1=V2=1,V3=2。之所以 V2 还是 1,遵循的就是这个要求:事务在执行期间看到的数据前后必须是一致的。 4. 若隔离级别是“串行化”,则在事务 B 执行“将 1 改成 2”的时候,会被锁住(A先获得锁)。直到事务A提交后,事务B才可以继续执行。所以从A的角度看, V1、V2=1,V3=2

Undo log:

举个例子: 图片 1. 实际上每条记录在更新的时候都会同时记录一条回滚操作。记录上的最新值,通过回滚操作,都可以得到前一个状态的值. 2. 在视图 A、B、C 里面,这一个记录的值分别是 1、2、4。 3. 视图A下,视图C将值修改为4,此时视图A通过视图C添加的回滚操作,逐个回滚即可得到试图A下对应的值1

事务的启动方式:

  1. 显式启动事务语句:
    • 启动事务: beginstart transaction
    • 提交语句: commit
    • 回滚语句: rollback > [!TIP] beginstart transaction命令并不是一个事务的起点,在执行到它们之后的第一个select语句,事务才真正启动。
  2. 关闭自动提交:set autocommit = 0
    • 作用:关闭当前线程的自动提交功能
    • 影响:开启后,select 语句需要手动提交/回退
    • 周期:该事务持续存在直到主动执行 commitrollback,或者断开连接

一致性视图:

  • 作用:定义事务执行期间用来定义“我能看到什么数据”
  • 生效范围:
    • SELECT语句查询结果产生影响
    • RU 没有视图,SELECT直接返回记录最新值
    • RC 在每个SELECT语句开始执行的时候创建的
    • RR 在事务启动时随后的第一个SELECT语句时创建,整个事务存在期间都用这个视图
    • Serial 没有视图,通过锁实现数据访问
  • 一致性视图创建的两个时机:
    • begin/start transaction开启的事务:
      1. RC下,每个select都会创建
      2. RR下,执行第一个快照读语句select时创建
    • start transaction with consistent snapshot创建一致性快照:
      1. 执行该事务语句后立即创建
      2. 忽视当前隔离级别的限制

mysql是如何实现的多版本的一致性读MVCC:

概念:

  1. MVCC:通过「版本链」来控制并发事务访问同一个记录时的行为就叫 MVCC(多版本并发控制)
  2. transaction id: InnoDB 里面每个事务有一个唯一的事务 ID。在事务开始的时候(实际是在发生增删改时才申请),向InnoDB的事务系统申请的,按申请顺序严格递增的。
  3. row trx_id:每次事务更新数据会生成一个新的数据版本,并且把事务的 transaction id记录到这个数据版本row的事务ID列,记为 row trx_id
  4. 多版本:数据表中的一行记录,其实可能有多个版本且每个版本有自己的 row trx_id,多个版本的记录通过roll_pointer连接,形成链式结构,通过undo log可以访问
  5. 在一个事务中,如果事务本身没有修改数据结果,那么不论在什么时候查询,看到这行数据的结果都是一致的。

如何查找当前事务的可读版本:

  1. 思路:一致性视图 + row不同版本之前的顺序关系(undo log),通过undo log,获取row的trx_id,判断对应的row是否可读,不可用则继续执行undo,直到找到当前的可读row trx_id

  2. 具体规则:

    1. InnoDB 为每个事务构造了一个数组m_ids,用来保存这个事务启动瞬间,当前活跃状态所有事务ID。(“活跃”是指:启动了但还没ids
    2. 活跃事务数组里面事务ID的最小值记为低水位min_trx_id
    3. 当前系统里面已经创建过的事务ID的最大值加1记为高水位max_trx_id
    4. 视图数组和高水位,就组成了当前事务的一致性视图(read-view)如下: Img
  3. 结合undo log 如下: Img

  4. 通过一致性视图找可读版本的解读:

    1. 根据版本链/undo log,从当前row的最大的trx_id逐个往前找
    2. 如果trx_id > max_trx_id,则在未提交事务区域,则不可见;根据版本链找到下一个trx_id
    3. 如果 min_trx_id < trx_id < max_trx_id,再额外判断trx_id是否在m_ids内
      1. 如果在,则当前找到的trx_id,对应的事务是活跃事务还未提交,不可见。根据版本链找到下一个trx_id
      2. 如果不在,则当前事务的trx_id已经提交,可见
    4. 如果 min_trx_id > trx_id,则在已提交事务区域,可见
  5. 可重复读下(RR),事务可见版本结论:

    1. 对于未提交的版本,不可见;
    2. 对于已提交的版本,如果是在视图创建后提交的,不可见;是在视图创建前提交的可见。
    3. 对于自己的更新的版本总是可见;
    4. 可见版本是找到上述版本中最大的trx_id的那个
  6. 读提交下(RC), 事务可见版本结论:

    1. 每一个语句执行前都会重新算出一个新的视图。所有读都是当前读(即读取当前已提交的最新版本)
  7. 对于更新逻辑:

    1. 一致性读不适用更新逻辑(update语句)。更新逻辑都是先读后写的,读当前的值,称为当前读(current read)。
    2. 在同一个事务内的记录UPDATE更新,使用当前读,会被后续的SELECT查到
    3. 强制select 语句使用当前读的两种方式:
    (S锁 共享锁): select * from t where id=1 lock in share mode
    (X锁 排他锁): select * from t where id=1 for update
  8. 当前读: 读取当前最新的记录。update、insert、delete语句、加锁的select语句

  9. 快照读: 读取可见的快照的记录。仅普通select语句

  10. 一致性读:当一个事务进行一致性读时,它实际上是在读取这个事务的一致性视图

  11. next-key lock:间隙锁+记录锁,解决RR隔离级别下,使用当前读时的幻读问题

    1. select * from x where id > 2 for update;
    2. 事务A会加上一个(2,+∞]的next-key lock,阻塞其他事务向此区间写入数据
    3. 事务B会申请一个插入意向锁,等待A提交后再执行 Img
  12. 插入意向锁:

  13. 无法避免的幻读:

    1. 事务A先使用快照读,事务B插入/删除记录,事务A使用当前读,则会发生幻读 Img
    2. 从业务上避免的措施: 尽量在开启事务之后,马上执行 select … for update 这类当前读的语句,对记录加 next-key lock,从而避免其他事务插入一条新记录

本文作者:navyum

本文链接:https://www.cnblogs.com/navyum/p/18509415

版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。

posted @   navyum  阅读(3)  评论(0编辑  收藏  举报
//自己上传到博客园的js
点击右上角即可分享
微信分享提示