当前位置: 首页 > news >正文

『 数据库 』MySQL 事务(一)

个人博客地址

个人博客: 花开富贵

文章目录

  • 个人博客地址
    • 1 什么是事务
    • 2 为什么会有事务
    • 3 事务的版本支持
    • 4 事务的提交方式
    • 5 事务的常见操作
      • 5.1 准备工作 - 设置隔离级别
      • 5.2 准备工作 - 创建测试表
      • 5.3 开启事务与事务的回滚操作
        • 5.3.1 ROLLBACK 回滚操作
        • 5.3.2 事务的自动回滚
        • 5.3.3 为什么说 BEGIN 与 START TRANSACTION 这两种方式被称为开启手动事务?
        • 5.3.4 auto_increment 与 ROLLBACK 回滚
      • 5.4 事务的提交
    • 6 事务的隔离级别
      • 6.1 脏读, 不可重复读与幻读
      • 6.2 查看事务的隔离级别
      • 6.3 设置事务的隔离级别
      • 6.4 读未提交 READ UNCOMMITTED
      • 6.5 读已提交 READ COMMITTED
      • 6.6 可重复读 REPEATABLE READ
      • 6.7 串行化 SERIALIZABLE


1 什么是事务

事务本质上是对数据库原子操作的一个称呼;

MySQL数据库通常为一个服务(mysqld), 部署在网络中, 这样避免不了多个客户端同时向数据库进行访问并进行CURD操作;

同样的属于一种存在访问临界资源的状态;

假设数据库中存在一条记录, 当两个客户端同时访问该表并对该记录进行删除操作, 那么势必造成数据错误;

因此MySQL对大部分的数据库操作进行了原子操作化;

换种说法来说明什么是事务:

  • 对一条或者多条DML操作的原子化封装即为事务

MySQL通常使用BEGIN或者START TRANSACTION开启事务, 通过COMMIT来提交(结束)事务;

这里可以举一个简单的示例:

其中两个客户端登录同一个数据库服务, 同时对一张表进行操作:

show create table test1\G
*************************** 1. row ***************************Table: test1
Create Table: CREATE TABLE `test1` (`id` int NOT NULL AUTO_INCREMENT,`name` varchar(20) DEFAULT NULL,PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci
1 row in set (0.00 sec)

其中一个客户端通过START TRANSACTION开启事务, 另一个不开启事务;

开启事务的客户端对表内进行数据插入并查看表内记录是否被插入成功:

insert into test1(name) value('ZhangSan');

确认插入成功后通过另一个客户端来查看是否有记录:

从查询结果来看, 对于上方的客户端而言, 数据已经被插入, 而对于下方的客户端而言记录并没有被插入;

通常情况下, 当一个事务被开启后, 其所有的记录需要进行提交才能真正的将数据进行持久化存储, 因此对于上方客户端而言似乎已经插入了数据, 但实际上并没有进行插入;

此时针对上方的客户端我们通过COMMIT提交来结束事务, 而后再次在下方的数据库中进行查询操作;

从结果可以看出, 当开启事务的客户端通过COMMIT提交事务后, 这个事务中插入的记录将被真正的记录在数据库中;

通常情况下, 事务这个概念是面向用户的而不是面向开发者的, 当用户使用某个功能时, 这个功能在内部对数据库进行一系列的操作, 包括查询, 更新, 删除等操作;

因此对于用户, 我们需要确保数据的安全以及正常, 我们需要将一个或者多个操作封装为同一个操作并赋予原子性, 这就是事务;

一个完整的事务, 通常需要满足以下四大属性:

  1. 原子性Atomicity

    一个事物中的所有操作都是原子的, 其只有两种状态, 要么事务内的操作全部完成, 要么事务内的操作全部没有完成, 不会结束再其中的某个操作中;

    通常情况下, 当事务在执行过程中发生错误, 将会被回滚(rollback), 回滚操作会到事务开始前的状态, 相当于这个事务从来没有执行过;

  2. 一致性 Consistency

    在事务开始之前和事务结束以后, 数据库的完整性没有被破坏, 表示写入的记录必须完全符合所有的预设规则;

    包含记录的精确性, 串联型以及后续数据库可以自发的完成预定的工作;

  3. 隔离性 Isolation

    数据库允许多个并发事务同时对数据库中的数据进行读写和修改;

    隔离性可以防止多个事务并发执行时由于交叉执行而导致数据不一致, 事务隔离分为不同级别;

    • 读未提交 Read Uncommitted
    • 读已提交 Read Committed
    • 可重复度 Repeatable Read
    • 串行化 Serializable
  4. 持久性 Durability

    事务处理结束后, 对数据库中数据的修改是永久的, 即使系统故障也不会造成数据丢失;

上述的这些属性被简称为ACID;

由于mysqld是一个网络服务, 因此同样的会被多个客户端所给访问, 并且执行多个事务, 这意味着对其而言, 将会存在大量的事务, 而在系统中, 若是存在大量的类似结构的数据, 那么势必要对这些数据进行管理, 而管理的方式即为:

  • 先描述 后组织

可以理解为, 在MySQL - InnoDB中, 最小的数据库操作单位为==“事务”==;


2 为什么会有事务

在上文中我们提到, 事务这个概念通常针对于上层(数据库的使用者), 当用户进行一个事件或者操作时, 针对于数据库而言实际上是一个以上的操作;

因此势必这些操作需要被管理起来以确保数据库的原子性, 隔离性, 一致性以及持久性;

同时, 由于数据库支持事务, 因此可以简化上层的编程模型;

当程序需要访问数据库时, 程序的设计者只需要了解该数据的事务如何使用, 并不需要考虑数据库中的原子性, 隔离性, 一致性以及持久性, 在开发过程中程序设计在一定程度上与访问数据库进行解耦;


3 事务的版本支持

MySQL中, 只有InnoDB存储引擎支持事务, MyISAM不支持事务;

MySQL中可以通过语句来查看哪些存储引擎支持事务;

SHOW ENGINES;


4 事务的提交方式

事务的提交commit方式通常有两种:

  • 手动提交
  • 自动提交

通常默认情况下, MySQL的提交方式为自动提交方式;

当然, 这种默认提交方式通常可以在MySQL的配置文件中寻找进行修改(如/etc/my.cnfmy.ini);

提交方式主要受参数autocommit来管理, 当其值为1时表示自动提交, 0则为手动提交;

可以通过show语句来查看其当前的提交方式:

SHOW VARIABLES LIKE 'autocommit';

同时可以通过SET来对提交方式进行修改;

SET autocommit = 1; # 表示启用自动提交
SET autocommit = 0; # 表示关闭自动提交, 采用手动提交方式


5 事务的常见操作

在进行常见操作之间, 首先要将数据库的隔离级别设置为==“读未提交”==READ UNCOMMITTED;


5.1 准备工作 - 设置隔离级别

通常可以采用命令来查看当前的隔离级别:

# mysql version < mysql 8.0 
SELECT @@_ISOLATION;# mysql version >= mysql 8.0
SELECT @@TRANSACTION_ISOLATION;

我使用的MySQL版本为:

mysql --version
mysql  Ver 8.0.43-0ubuntu0.22.04.2 for Linux on x86_64 ((Ubuntu))

同样的可以使用SET操作来设置隔离级别:

SET GLOBAL TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;

由于设置的是全局事务隔离级别, 因此需要重启MySQL客户端;

可以看到重启客户端后, 对应的隔离级别被更改;

这个隔离级别也是在事务中最低的隔离级别;


5.2 准备工作 - 创建测试表

除此之外我们还需要一张测试表, 表结构如下:

mysql> show create table test1\G
*************************** 1. row ***************************Table: test1
Create Table: CREATE TABLE `test1` (`id` int NOT NULL AUTO_INCREMENT,`name` varchar(20) NOT NULL,`price` decimal(7,2) DEFAULT NULL,PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci
1 row in set (0.00 sec)


5.3 开启事务与事务的回滚操作

事务通常可以通过BEGINSTART TRANSACTION开启;

  1. BEGIN

    mysql> begin;
    Query OK, 0 rows affected (0.00 sec)
    
  2. STRAT TRANSACTION

    mysql> start transaction;
    Query OK, 0 rows affected (0.00 sec)
    

这两种方式开启事务为手动开启事务;

当开启了事务后, 接下来所有的DML语句都将属于同一个事务内, 需要进行commit提交才能将操作的数据进行持久化;

准确的来说, 事务实际上做了一个这样的操作, 当使用BEGIN或者START TRANSACTION开启事务时, 服务端将会针对此次会话自动产生一个类似于缓存的操作, 当客户端对服务端进行对应的DML操作时, 这些操作将会暂存在服务端当中, 当客户端使用COMMIT对这些操作进行提交时, 服务端才会将之前所执行的DML操作永久存储在服务端内部, 以达成一个持久性;


5.3.1 ROLLBACK 回滚操作

在进行事务的DML操作时, 通常可能会出现一些不可避免的问题, 例如插入/更新/删除了错误的数据, 而为了避免这种操作导致整个事务的DML操作失效, 数据库通常会为其备选一个ROLLBACK回滚操作;

回滚操作允许在事务执行的过程当中使用ROLLBACK命令将DML操作回滚到之前的某个点或是最初始的状态, 通常回滚到某个点前, 需要确保对应的操作有保存点, 保存点需要使用SAVEPOINT point_name记录;

  • 保存点ROLLBACK TO操作

    保存点ROLLBACK操作需要先试用SAVEPOINT point_name来记录保存点;

    1. 插入数据并设置保存点

      mysql> BEGIN; # 开启事务
      Query OK, 0 rows affected (0.00 sec)mysql> insert into test1(name, price) values('ZhangSan', 100); # 插入第一条数据
      Query OK, 1 row affected (0.00 sec)mysql> savepoint p1; # 设置保存点p1
      Query OK, 0 rows affected (0.00 sec)mysql> insert into test1(name, price) values('LiSi', 100); # 插入第二条数据
      Query OK, 1 row affected (0.00 sec)mysql> savepoint p2; # 设置保存点p2
      Query OK, 0 rows affected (0.00 sec)mysql> insert into test1(name, price) values('WangWu', 100); # 插入第三条数据
      Query OK, 1 row affected (0.00 sec)mysql> savepoint p3; # 设置保存点p3
      Query OK, 0 rows affected (0.00 sec)mysql> insert into test1(name, price) values('ZhaoLiu', 100); # 插入第四条数据
      Query OK, 1 row affected (0.00 sec)mysql> savepoint p4; # 设置保存点p4
      Query OK, 0 rows affected (0.00 sec)
      

      此处设置了四个保存点, 值得注意的是, 保存点的设置只能设置在当前位置;

    2. 回滚操作

      当需要回滚到对应的位置时, 只需要使用ROLLBACK TO point_name即可;

      mysql> ROLLBACK TO p3;
      Query OK, 0 rows affected (0.00 sec)mysql> select * from test1;
      +----+----------+--------+
      | id | name     | price  |
      +----+----------+--------+
      |  3 | ZhangSan | 100.00 |
      |  4 | LiSi     | 100.00 |
      |  5 | WangWu   | 100.00 |
      +----+----------+--------+
      3 rows in set (0.00 sec)
      

      当回滚到对应的位置时, 不允许再由当前的保存点回滚到原始位置(回滚前的位置);

      如由p4位置滚回p3位置后, 不允许再由p3位置滚至p4位置;

  • 直接回滚操作ROLLBACK

    直接回滚指以当前位置直接通过ROLLBACK命令回滚至该事务开启前的状态, 同时, 当没有设置保存点时, 只能通过ROLLBACK命令进行回滚, 无法回滚到保存点(因为无保存点存在);


5.3.2 事务的自动回滚

在上文中提到, 可以通过ROLLBACK对事务进行手动回滚, 除此之外事务在某些情况下将会自动回滚;

这种情况通常出现在客户端崩溃, 连接断开等一系列异常情况;

由于事务需要保持其的四大属性:

  1. 原子性
  2. 隔离性
  3. 一致性
  4. 持久性

DML操作执行到中间过程中客户端出现上述等异常情况时, 其首先需要保证其隔离性, 此次异常的操作不能影响其他事务的运行;

其次是原子性, 在上文中我们提到, 以事务为单位的一组(一条或多条)DML语句只有两种情况:

  • 已完成
  • 未开始

并不存在中间状况;

其次是对于MySQL数据库而言, 其并不能确定在事务的执行过程中中断前的DML语句是否属于一个完整的事务, 因此MySQL不能贸然的为上层做出提交COMMIT决定来对这一系列的DML语句保持其永久性;

基于上述的这几点条件, 因此在事务的执行过程中遇到了异常情况导致客户端与服务端断开, 其服务端mysqld将会选择将这一组DML语句进行ROLLBACK;

从图中可以看出, 在事务执行期间使用exit退出MySQL客户端, 重新登录数据库并查看表内数据时, 为事务开启前状态, 表明客户端退出连接时, 对应的将会进行ROLLBACK操作;

这里实验的是exit, 实际上在其他异常操作(造成客户端服务端断开的操作)也同样是该效果, 因此不进行赘述;


5.3.3 为什么说 BEGIN 与 START TRANSACTION 这两种方式被称为开启手动事务?

在上文中, 我们提到了事务的自动提交, 同时我们可以使用sql命令来查看当前是否支持自动提交事务的操作;

show variables like 'autocommit';

当结果为ON表示自动提交为开启状态, 否则为自动提交未开启状态;

当自动提交autocommit为开启状态时, 默认的任何一条sql语句都会被认为是一条单独的事务, 在执行后将会被提交, 而autocommit为关闭状态时, 同样是事务状态, 但此时的事务状态将为手动提交;

若是在autocommitOFF状态时对数据库进行操作却又不手动commit, 那么所做的所有DML操作将永远不会被持久化;

因为没有commit提交, 因此并不会被认为是一个完整的事务;

从图中可以看出, 这里两个会话针对于同一个数据库与同一张表;

其中上面的会话关闭autocommit后对数据进行插入, 随后通过exit退出连接, 而后使用另一个会话查看对应表内数据情况发现数据并没有持久化的被存储到数据库中;

那么回到标题问题:

  • 为什么说 BEGINSTART TRANSACTION 这两种方式被称为开启手动事务?

    autocommit开启状态时, 每一条DML语句都将是单独的事务, 在执行过后将会直接提交到服务端中, 而使用了 BEGINSTART TRANSACTION 时, 这种视单独一条DML语句为一个事务的现象将会被打破, 必须手动COMMIT进行提交, 否则不会视该组DML语句为事务, 因此为开启手动事务, 这取决于是否自动提交;


5.3.4 auto_increment 与 ROLLBACK 回滚

在上文中, 我们所使用的table存在有auto_increment自增的column;

但是我们可以观察到, 实际上当进行ROLLBACK操作后, 对应表内的数据将会被退回(滚回), 而auto_increment计数器并不会恢复;

我们再一次进行实验;

其中表结构为:

show create table test1\G
*************************** 1. row ***************************Table: test1
Create Table: CREATE TABLE `test1` (`id` int NOT NULL AUTO_INCREMENT,`name` varchar(20) DEFAULT NULL,`sal` decimal(7,2) DEFAULT NULL,PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci
1 row in set (0.111 sec)

启动手动事务, 插入数据, 而后ROLLBACK进行回滚操作, 再使用 show create table来查看其中auto_increment的值;

或许这里还未经过COMMIT, 我们不一定知道计数器是否重置, 接下来我们进行COMMIT并再次查看auto_increment的值;

从结果可以看出确实我们在进行ROLLBACK回滚后, 其auto_increment并不会滚回;

这本质上是因为事务操作的范围与auto_incremnet的范围不同;

对于auto_increment而言, 其是一个针对于全局的计数器, 而事务中所进行的操作是针对单张表的操作, 当表内存在auto_incremnet的列时, 任何对该表进行的插入动作都会分配全局的计数资源;

这意味着事务中的ROLLBACK只能对单张表内的数据进行恢复, 而没有权限回滚MySQL全局中已经分配出来的计数资源;

通常情况下, 当进行ROLLBACK后, auto_increment将会造成==“空洞”== (ID gaps)现象, 而通常auto_increment被设置在主键中, 主键能够确保单条记录的唯一性, 因此出现的空洞并不会影响数据的唯一性;

虽然不影响记录的唯一性, 但是或多或许会造成资源的枯竭, 通常情况下, 主键在进行自增时, 通常会根据该列的属性存在一个最大值, 而若是出现了大量的空洞, 可能会导致数据未插满, 但主键资源不够用的情况;


5.4 事务的提交

当事务开启时, 需要对事务进行COMMIT提交, 执行提交后, 该事务才会被持久化的存储在服务端;

当然这里需要排除AUTOCOMMIT=ON的状态, 当自动提交状态为开启且没有手动执行START TRANSACTIONBEGIN来开启手动事务时, 单条DML语句都是一个独立的事务, 将会自动进行COMMIT;

从这里可以看出, 在InnoDB中, 所有的事务都必需包含在BDGIN/START TRANSACTIONCOMMIT中;


6 事务的隔离级别

MySQL核心的部分为mysqld服务器, 一个服务器说明其必然会同时被几个客户端同时访问, 因此服务器需要有对应的隔离级别来保证事务之间的隔离性;

在思考隔离级别前, 我们有一个问题值得深思:

  • 对于数据库而言, 是否每时每刻都需要看到最新的记录?

    答案是否定的, 不同时间的定点查询, 并不一定要看到最新的记录;

    我们更希望的是, 每次在对数据库进行查询时, 其能获取到的记录是定格在某个时刻的;

    就例如可能我们在进行此时此刻的财务报表的制作, 而时间线是不断流动的, 我们不能保证在定点之后没有新的数据来到库中;

    而对我们而言, 新来的数据并与我们无关, 因此这些随着时间所新来的数据对我们当前的工作而言是没有意义的;

    因此实际在数据库的查询过程中, 我们不希望每时每刻都能获取最新的数据, 相反我们需要让数据定格在某一时刻;

    隔离级别就是为此而生, 让开发者根据场景选择适用的隔离方式;

事务通常分为几个隔离级别, 不同的隔离级别对应的行为不同, 对应的能防止不同级别的数据错误;

对应的隔离级别分别有:

  1. 读未提交 READ UNCOMMITTED

    事务之间允许读取其未COMMITDML操作;

  2. 读已提交 READ COMMITTED

    事务之间只允许读其已经COMMITDML操作;

  3. 可重复读 REPEATABLE READ

    事务与事务之间将在第一次SELECT语句后形成一个快照, 确保只能读取快照内的数据;

  4. 串行化 SERIALIZABLE

    最高的隔离级别, 事务之间在进行DML操作时, 将会强行排序, 当一个事务进行增删改操作时, 另一个数据必须要COMMIT退出, 否则执行该增删改操作的事务将会被阻塞;

不同的隔离级别通常可以让事务之间有着不同的隔离性从而确保数据在某些方面上属于正确的状态;


6.1 脏读, 不可重复读与幻读

通常情况下, 多个事务同时对一张表内的记录进行DML操作时, 将会出现不同程度的数据库, 我们对这些数据错误(尤其在查询过程中出现)的称之为==“脏读”==, “不可重复读"以及"幻读”;

  • 脏读

    脏读通常针对于一个事务读取到另一个事务的未COMMITDML操作, 当这些DML操作被ROLLBACK时, 读取该操作的事务所读取到的数据即为脏数据(这些数据并没有真实存在过);

    如改图所示, 存在两个事务(TransactionATransactionB);

    事务A对表内进行操作但未进行提交时, 事务B通过SELETC读取到了该数据并提交给了上层;

    但此后事务A又对原本的插入操作进行了ROLLBACK回滚, 而原先事务B所查询到的记录中存在实际不存在的记录, 因此这种记录被称为脏数据, 即为==“脏读”==;

  • 不可重复读

    不可重复读是针对已经COMMIT的数据而言的, 同时不可重复读是针对单行数据在同一个事务内的不同查询时间;

    如短时间查询到同一条记录, 但所给出的结果不同, 这种数据错误被称为不可重复读;

    如两个事务(TransactionATransactionB)同时对一张表进行DML操作, 其中事务A进行写操作, 事务B进行读操作;

    事务B对某条记录的查询, 第一次的查询结果为N;

    在查询第二次前, 事务A对表内数据进行了DML操作(Insert, Update, Delete);

    而后事务B再次对该记录进行查询, 结果为N ± M, 此时对于该条记录的数据错误则被称为==“不可重复读”==;

  • 幻读

    幻读同样是针对已经COMMIT的数据而言, 但其通常是其在进行SELECT操作后查询出对应的结果记录数量为N, 但在接下来的DML操作(Insert, Update, Delete)对数据进行操作时, 将会多/少操作对应的记录, 这种数据错误被称为幻读;

    假设同样存在两个事务(TransactionATransactionB)同时对一张表进行操作, 此时事务之间通过某些设定, 已经不会出现不可重复读与脏读现象;

    该表中的内容为:

    事务A对表内新增了一条数据, 其ID = 21且进行了commit, 由于隔离性的设置, 事务A可以查询到最新的表内容, 而事务B需要维持一致性, 因此查询结果还是如上图一致;

    而事务B在对表内进行写操作时(UPDATE, DELETE)可以对原来不在表中的数据进行操作;

    这种现象被称为==“幻读”==;


6.2 查看事务的隔离级别

在MySQL中, 通常我们可以使用SQL语句来设置事务的隔离级别:

# mysql version < mysql 8.0 
SELECT @@TX_ISOLATION;# mysql version >= mysql 8.0
SELECT @@TRANSACTION_ISOLATION;

由于本机的MySQL版本为version >= mysql 8.0, 因此采用第二种方式;

同时, 事务的隔离级别设置与查看有着不同的范围(生命周期), 分别为:

  • 全局

    select @@global.transaction_isolation;
    
  • 单次会话

    select @@session.transaction_isolation;
    
  • 下一个事务

    通常没有具体的查看语句, 可以直接使用:

    select @@transaction_isolation;
    

6.3 设置事务的隔离级别

同样的, 设置事务的隔离级别有三个范围, 从大到小分别为全局, 当前会话以及下一次事务;

默认情况下, 下一次事务的隔离级别继承当前会话的隔离级别, 当前会话的隔离级别继承全局隔离级别;

需要注意的是, 当设置全局隔离级别后本质是修改配置文件, MySQL 并不会在当前会话中读取已经设置的新的隔离级别;

因此当设置完全局的隔离级别后, 需要重新启动MySQL, 确保MySQL的当前会话读取到了全局隔离级别, 才能更新当前会话的隔离级别, 从而对下一次事务的隔离级别产生有效效果;

  • 全局

    SET GLOBAL TRANSACTION_ISOLATION = 'xxxx'; -- xxxx 表示隔离级别, 如 'READ UNCOMMITTED'
    
    • 示例:

       mysql> select @@global.transaction_isolation;
      +--------------------------------+
      | @@global.transaction_isolation |
      +--------------------------------+
      | REPEATABLE-READ                |
      +--------------------------------+
      1 row in set (0.004 sec)mysql> select @@session.transaction_isolation;
      +---------------------------------+
      | @@session.transaction_isolation |
      +---------------------------------+
      | REPEATABLE-READ                 |
      +---------------------------------+
      1 row in set (0.004 sec)mysql> select @@transaction_isolation;
      +-------------------------+
      | @@transaction_isolation |
      +-------------------------+
      | REPEATABLE-READ         |
      +-------------------------+
      1 row in set (0.003 sec)mysql> set global transaction_isolation = 'read-uncommitted';
      Query OK, 0 rows affected (0.004 sec)mysql> select @@global.transaction_isolation;
      +--------------------------------+
      | @@global.transaction_isolation |
      +--------------------------------+
      | READ-UNCOMMITTED               |
      +--------------------------------+
      1 row in set (0.004 sec)mysql> select @@session.transaction_isolation;
      +---------------------------------+
      | @@session.transaction_isolation |
      +---------------------------------+
      | REPEATABLE-READ                 |
      +---------------------------------+
      1 row in set (0.003 sec)
      

      可以看到当全局隔离级别设置完毕后, 并不会影响当前会话, 因此更不会影响下一个事务;

      通常只需要重启MySQL即可;

      如图所示, 重启后, 事务隔离级别发生变化;

  • 当前会话(连接)

    SET SESSION TRANSACTION_ISOLATION = 'xxxx'; -- xxxx 表示隔离级别, 如 'READ UNCOMMITTED'
    
    • 示例

      mysql> set session transaction_isolation = 'read-committed';
      Query OK, 0 rows affected (0.004 sec)mysql> select @@session.transaction_isolation;
      +---------------------------------+
      | @@session.transaction_isolation |
      +---------------------------------+
      | READ-COMMITTED                  |
      +---------------------------------+
      1 row in set (0.004 sec)mysql> select @@global.transaction_isolation;
      +--------------------------------+
      | @@global.transaction_isolation |
      +--------------------------------+
      | READ-UNCOMMITTED               |
      +--------------------------------+
      1 row in set (0.003 sec)
      

      当设置当前会话的隔离级别时将会立刻生效, 但当此次会话结束后, 其隔离级别将会恢复;

  • 下一次事务

    只针对下一次事务, 当下一次事务结束后将会恢复隔离级别:

    SET TRANSACTION ISOLATION LEVEL 隔离级别;
    
    • 示例

      SET TRANSACTION ISOLATION LEVEL READ COMMITTED;
      Query OK, 0 rows affected (0.003 sec)
      

    无具体查看方式;


6.4 读未提交 READ UNCOMMITTED

隔离级别最低的隔离级别;

在该隔离级别下, 两个事务都将看到对方未提交的DML操作, 该隔离级别下将会出现, 脏读, 不可重复读, 幻读的现象, 最主要体现在脏读现象;

从图中示例可以看出, 当隔离级别为READ UNCOMMITTED时, 事务B可以查询到事务A的未提交记录;

当事务A进行ROLLBACK回滚后, 对应的事务B所得到的记录将为脏数据, 因此发生脏读现象;


6.5 读已提交 READ COMMITTED

读提交隔离级别 READ COMMITTED要比 READ UNCOMMITTED读未提交要高一级, 其不允许事务读到另一个事务未提交的记录, 从而有效避免了脏读;

从该图示例可以看出, 重复6.4中的操作, 并没有发生脏读现象;

虽然该隔离级别能够预防脏读现象, 但其无法预防不可重复读现象;

从该图示例中可以看到, 在该隔离级别下, 事务B对一条记录进行查询两次, 中途事务A对其中的值进行了UPDATECOMMIT, 因此事务B两次对同一个记录的查询结果不同, 发生了不可重复读;


6.6 可重复读 REPEATABLE READ

隔离级别可重复读 REPEATABLE READ要比读提交 READ COMMITTED要更高一级, 当两个事务对同一张表进行查询SELECT操作时, 将会为他们各自创建一个属于其事务的快照, 所有的查询操作将在各自的快照中, 其中一个事务对表内数据进行修改时, 并不会影响另一个事务的快照, 从而增强隔离性(生成快照的时机可以进行修改, 通常为事务开启的时机或者是第一次的SELECT操作);

从结果可以看出, 事务B对同一条记录进行了两次查询, 事务A在两次查询的过程中对同一条记录的值进行了修改并COMMIT提交事务, 但不影响事务B的查询结果, 未发生不可重复读现象;

虽然可重复读REPEATABLE READ隔离级别解决了不可重复读问题, 但其并不能预防幻读现象;

从例图结果中可以看出, 在事务A对表内进行插入时, 并不影响事务B的查询, 但当事务B对对应的记录进行更新(写操作)时, 将会出现上一次查询未曾存在的记录;

这本质与该隔离级别的机制有关, 在该隔离级别下, 当事务进行第一次查询操作时, InnoDBMVCC将会为这次读操作生成一个数据快照, 自此接下来所有的查询操作都将围绕这个快照中进行;

当两个事务同时对一张表进行操作时, 假设事务对表内插入或者删除了一条记录并COMMIT, 那么这个事实就算已经发生了, 因此针对另一个事务而言, 当前所查询的结果存在幻影(有新记录没有被曝光或有记录被删除但该快照中仍然存在);

MVCC的快照并不是万能的, DML中增删改操作是根据表的实际的, 尤其是当进行范围操作且范围操作覆盖了幻影时, 事务将必须遵从实际的表结构进行操作, 因此会提前暴露出幻影记录, 造成一致性被影响, 这种现象被称为幻读;


6.7 串行化 SERIALIZABLE

串行化 SERIALIZABLE是所有隔离级别最高的, 其完美的解决了事务之间可能存在的几个问题(脏读, 不可重复读, 幻读);

其通过强制事务串行操作, 避免了上述的几个问题, 虽然完美解决了上述问题, 但在效率上会因为强行的串行操作导致时间开销变多;

从例图的结果可以看出, 当使用SERIALIZABLE串行化隔离级别时, 将会强行让事务与事务之间串行操作, 这个串行操作体现在写操作中, 当使用读操作时, 事务之间不会出现串行, 不会被另一个事务造成阻塞;

而当一个事务进行写操作时, 不能同时存在其他事务, 以避免其他事务出现潜在幻读或者其他问题, 当然这一点也体现在正在操作的事务上, 强制事务间串行来保证数据间的隔离性;

当然在上文中提到了==“效率上会因为强行的串行操作导致时间开销变多”==, 这点在图中可以观察到, 在插入过程中, 未及时将另一个事务COMMIT导致插入时间变为了 1 min 26.113 sec;

质与该隔离级别的机制有关, 在该隔离级别下, 当事务进行第一次查询操作时, InnoDBMVCC将会为这次读操作生成一个数据快照, 自此接下来所有的查询操作都将围绕这个快照中进行;

当两个事务同时对一张表进行操作时, 假设事务对表内插入或者删除了一条记录并COMMIT, 那么这个事实就算已经发生了, 因此针对另一个事务而言, 当前所查询的结果存在幻影(有新记录没有被曝光或有记录被删除但该快照中仍然存在);

MVCC的快照并不是万能的, DML中增删改操作是根据表的实际的, 尤其是当进行范围操作且范围操作覆盖了幻影时, 事务将必须遵从实际的表结构进行操作, 因此会提前暴露出幻影记录, 造成一致性被影响, 这种现象被称为幻读;

http://www.dtcms.com/a/610984.html

相关文章:

  • Windows(一)_powershell文件切分
  • Spring AI RAG RetrievalAugmentationAdvisor源码分析
  • 做的好的奥运会网站织梦的网站模板免费吗
  • 自己做的网站怎么打开电影爱好网
  • PyTorch-混合精度训练(amp)
  • 集约化网站群建设网站建设的公司上海
  • 每日两题day43
  • JVM入门知识点
  • 悬镜安全获评2024年度北京市科学技术奖
  • 用yershop做网站win7搭建网站服务器
  • 网站空间国外那个好网站推广代理
  • 云南手机网站建设英文网站建设需求
  • P11096 体育课 top51goc CSP2025模拟第二次第二题题解​ ​
  • Structured Output Parser in LangChain
  • 解释性语言和编译型语言的区别与不同 | 深入了解两者的特点与应用
  • asp网站镜像代码网站建设分金手指专业十六
  • 建设一个网站首先需要什么滕州做网站哪家好
  • 多场景服务机器人代理品牌深度解析
  • 建设网站答辩情况wordpress 搜索 范围
  • “干活”机器人“教练”登场:宇树机器人推出首款轮式机器人G1-D
  • 素马网站制作开发天猫交易购买平台
  • 增强AI编程助手效能:使用开源Litho(deepwiki-rs)深度上下文赋能iFlow
  • 郑州网站建设找三牛天津建设工程信息网评标专家 终审
  • LTE和5G NR中的PDSCH和PUSCH
  • 【HarmonyOS 6】静态和动态添加应用快捷方式详解
  • 手机网站模板更换方法设计上海网站建设
  • 手机网站竞价莆田外贸自建网站
  • 让设备 “开口说话”:设备间通信艺术与以太网温湿度传感器通信实现
  • 宝安做小程序有推荐吗wordpress 百度优化
  • HTML 页面跳转实现