🍖事务隔离机制

一.事务隔离机制介绍

事务具有原子性、一致性、隔离性、持久性四大特性

隔离性顾名思义指的就是事务彼此之间隔离开, 多个事务在同时处理一个数据时彼此之间互相不影响, 如果隔离的不够好就有可能会产生脏读、不可重复度、幻读等读现象

二.隔离性的四个级别

1.等级(隔离程度)由低到高

  • Read uncommitted (未提交读)
  • Read committed (提交读)
  • Repeatable read (可重复读) (mysql默认)
  • Serializable (可序列化)

2.依次解决的读现象

  • ✔ : 可能出现

  • ❌ : 不会出现

  脏读 不可重复读 幻读
Read uncommitted
Read committed ×
Repeatable read(mysql默认) × ×
Serializable × × ×

需要强调的是 : 我们确实可以采用提高事务的隔离级别的方式来解决脏读、不可重复读、幻读等问题, 但与此同时, 事务的隔离级别越高, 并发能力也就越低; 所以, 还需要读者根据业务需要进行权衡

三.四种级别介绍

1.未提交读 (Read uncommitted)

  • 定义

是最低的隔离级别, 在这种事务隔离级别下, 一个事务可以读到另外一个事务未提交的数据

  • 数据库加锁情况(实现原理)

事务在读数据的时候并未对数据加锁

事务在修改数据的时候只对数据增加行级共享锁

  • 现象解释

事务1读取某行记录时, 事务2也能对这行记录进行读取、更新, 并且因为事务一并未对数据增加任何锁

当事务2也对该记录进行更新时, 事务1再次读取该记录, 能读到事务2对该记录的修改版本 (因为事务2只增加了共享读锁, 事务1可以再增加共享读锁读取数据), 即使该修改尚未被提交, 若此时事务2回滚, 那事务1读到的就脏数据了, 这就引发了脏读现象

事务1更新某行记录时, 事务2不能对这行记录做更新, 直到事务1结束 (因为事务1对数据增加了共享读锁, 事务2不能增加排他写锁进行数据的修改)

2.提交读 (Read committed)

  • 定义

可以理解成都已提交, 在一个事务修改数据过程中, 如果事务还没提交, 其他事务不能读该数据

  • 数据库加锁情况

事务对当前被读取的数据增加行级共享锁(读到时才加锁), 一旦读完该行, 立即释放该行行级共享锁

事务在更新某数据的瞬间(在更新的瞬间), 必须先对其加行级排它锁, 直到事务结束才释放

  • 现象解释

事务1在读取某行记录的整个过程中, 事务2都可以对该行记录进行读取 (因为事务一对该行记录增加行级共享锁的情况下, 事务二同样可以对该数据增加共享锁来读数据)

事务1读取某行的一瞬间, 事务2不能修改该行数据, 但是, 只要事务1读取完改行数据, 事务2就可以对该行数据进行修改 (事务一在读取的一瞬间会对数据增加共享锁, 任何其他事务都不能对该行数据增加排他锁; 但是事务一只要读完该行数据, 就会释放行级共享锁, 一旦锁释放, 事务二就可以对数据增加排他锁并修改数据)

事务1更新某行记录时, 事务2不能对这行记录做更新, 直到事务1结束 (事务一在更新数据的时候, 会对该行数据增加排他锁, 知道事务结束才会释放锁, 所以, 在事务二没有提交之前, 事务一都能不对数据增加共享锁进行数据的读取; 所以, 提交读可以解决脏读的现象)

3.可重复读 (Repeatable reads)

  • 定义

由于提交读隔离级别会产生不可重复读的读现象, 所以比提交读更高一个级别的隔离级别就可以解决不可重复读的问题, 这种隔离级别就叫可重复读

  • 数据库锁情况

事务在读取某数据的瞬间 (就是开始读取的瞬间), 必须先对其加行级共享锁, 直到事务结束才释放

事务在更新某数据的瞬间 (就是发生更新的瞬间), 必须先对其加行级排他锁, 直到事务结束才释放

  • 现象解释

事务1在读取某行记录的整个过程中, 事务2都可以对该行记录进行读取 (因为事务一对该行记录增加行级共享锁的情况下, 事务二同样可以对该数据增加共享锁来读数据)

事务1在读取某行记录的整个过程中, 事务2都不能修改该行数据 (事务一在读取的整个过程会对数据增加共享锁, 直到事务提交才会释放锁, 所以整个过程中, 任何其他事务都不能对该行数据增加排他锁; 所以, 可重复读能够解决不可重复读的读现象)

事务1更新某行记录时, 事务2不能对这行记录做更新, 直到事务1结束 (事务一在更新数据的时候, 会对该行数据增加排他锁, 知道事务结束才会释放锁, 所以, 在事务二没有提交之前, 事务一都能不对数据增加共享锁进行数据的读取; 所以, 提交读可以解决脏读的现象)

4.可序列化 (Serializable)

  • 定义

是最高的隔离级别, 前面三种隔离级别都无法解决的幻读, 在可序列化的隔离级别中可以解决

  • 数据库锁情况

事务在读取数据时, 必须先对其加表级共享锁, 直到事务结束才释放

事务在更新数据时, 必须先对其加表级排他锁, 直到事务结束才释放

  • 现象解释

事务1正在读取A表中的记录时, 则事务2也能读取A表, 但不能对A表做更新、新增、删除, 直到事务1结束 (因为事务一对表增加了表级共享锁, 其他事务只能增加共享锁读取数据, 不能进行其他任何操作)

事务1正在更新A表中的记录时, 则事务2不能读取A表的任意记录, 更不可能对A表做更新、新增、删除, 直到事务1结束 (事务一对表增加了表级排他锁, 其他事务不能对表增加共享锁或排他锁, 也就无法进行任何操作)

  • 序列化事务产生的效果
  • 无法读取其他事务已经修改单位提交的记录
  • 在当前事务完成之前, 其他事务不能修改当前事务已经读取的记录
  • 在当前事务完成之前, 其他事务插入的新记录, 其索引键值不能在当前事务的任何语句所读取的索引键范围中

5.隔离级别的选择

四种事务隔离级别从隔离程度上越来越高, 但同时在并发性上也就越来越低; 之所以有这么几种隔离级别, 就是为了方便开发人员在开发过程中根据业务需要选择最合适的隔离级别

四.修改事务隔离级别

1.查看当前事务隔离级别

  • 可以通过模糊匹配变量名
show variables like "%tx_isolation";

image-20210302190403075

  • 直接查看变量 tx_isolation
select @@tx_isolation;

image-20210302190600192

  • 也可以通过以下语句查看
select @@global.tx_isolation;   # 查看全局事务隔离级别
select @@session.tx_isolation;  # 查看当前会话事务隔离级别

image-20210302190857307

ps : 在MySQL 8.0.3 中, tx_isolation 变量被 transaction_isolation 变量替换了; 在 MySQL 8.0.3 版本中查询事务隔离级别, 只要把上述查询语句中的 tx_isolation 变量替换成 transaction_isolation 变量即可

2.修改事务隔离级别

Mysql提供了 set transaction 语句来改变单个会话或者全局会话的事务隔离级别

set [session|global] transaction isolation level
    [read ununcommitted|read committed|repeatable read|serializable]
  • session : 表示修改的事务隔离级别将应用于当前 session(当前 cmd 窗口) 内的所有事务
  • global : 表示修改的事务隔离级别将应用于所有 session (全局) 中的所有事务, 且当前已经存在的 session 不受影响
  • 如果省略 session 和 global, 则表示修改的事务隔离级别将应用于当前 session 内的下一个还未开始的事务

3.用户权限问题

任何用户都能改变会话的事务隔离级别, 但是只有拥有 super 权限的用户才能改变全局的事务隔离级别

  • 验证修改全局事务隔离级别, 当前会话不受影响
select @@global.tx_isolation;   # 查看全局事务隔离级别
select @@session.tx_isolation;  # 查看当前会话事务隔离级别
set global transaction isolation level read committde; 
# 修改全局事务隔离级别

image-20210302195744397

  • 可以通过直接修改 tx_isolation 变量来修改当前 session 的事务隔离级别
set tx_isolation="read-committed";
select @@session.tx_isolation;

image-20210302204911467

---end---

posted @ 2021-03-02 20:51  给你骨质唱疏松  阅读(222)  评论(0编辑  收藏  举报