MySQL事务的隔离级别

前置阅读

快速搭建 Linux 学习平台

一、事务的特性

对于事务,我觉得有一句英文描述的非常贴切:All or not, now or never.

事务 (Transaction)可以说是关系型数据库最重要的特性了。SQL 事务就是一个或者多个 SQL 语句的集合,这些 SQL 语句组成了一个单个的逻辑单元,所以它只有两种执行结果:commit 或者 rollback。学过操作系统的同学都会了解到,当程序引入多进程或者多线程时,也随之而来的并发问题。同样事务也是为了保证在并发执行的情况下数据的完整性。

事务开启语句是由 begin 或者 start transaction 命令开始的,或者把自提交特性关闭(set autocommit = 0)。事务结束语句通常使用 commit 或者 rollback。commit 表示提交事务,事务对数据库的修改成为永久性的,rollback 表示回滚事务,撤销正在进行中的所有未提交的修改。

事务的特性即:ACID。

  • Atomicity 原子性

  • Consistency 一致性

  • Isolation 隔离性

    Isolation is the database-level property that controls how and when changes are made, and if they become visible to each other, users, and systems.

    隔离性是控制修改如何(How)和 何时(When)发生,以及它们是否对于其它事务、用户和系统是可见的的数据库级别属性。

  • Durability 持久性

这里只重点介绍 Isolation (隔离性)是希望突出重点,关于其它三点性质,读者可以自行查阅理解。

注:acid 在英文有酸性的意思。同样,另一个分布式事务,它的特性是 base,它有碱性的意思。

二、事务的隔离性

MySQL InnoDB 存储引擎实现了 SQL 标准的 4 种隔离级别,用来限定事务内外的哪些改变是可见的,哪些是不可见的。

MySQL是各个隔离级别分别是:

  • R ead U ncommitted 读未提交,简称 RU

    在一个事务中,它可以读取到其他事务还未提交的数据变化,这种现象称为脏读。

  • R ead C ommitted 读已提交,简称 RC

    在一个事务中,可以读取到其他事务已经提交的数据变化,这种现象称为不可重复读。

  • R epeatable R ead 可重复读,简称 RR

    它是 MySQL 默认的事务隔离级别。在一个事务中,从它开始直到事务结束,都可以反复读取到事务刚开始看到的数据,并一直不会发生变化,避免了脏读、不可重复读和幻读现象。

  • Serialization 可串行化

    不建议在生产环境使用,在某些特殊情况下会使用到。

这四个隔离级别的隔离性是逐渐增大的,隔离性越大,系统的开销越大。默认的隔离级别是 RR,如果实际的应用不需要这个隔离级别,可以降低隔离级别,这样会提高性能。但是除非你对数据库的隔离级别很了解,否则最好只使用默认的隔离级别。

三、隔离性实践

下面会涉及到实际的操作,它依赖的环境是前置条件中的那篇快速搭建环境的博客中所搭建的(MySQL 8.0)。

MySQL 默认的隔离级别是:REPEATABLE-READ,可重复读,简称 RR。

  • 查看默认的隔离级别:show variables like '%isolation'; 或者 select @@transaction_isolation;
  • 修改默认的隔离级别:SET transaction_isolation = 'READ-UNCOMMITTED';

注:这里要特别注意,在命令行中输入不要漏掉了末尾的 ;

注:这里修改的是当前 session 的隔离级别,不是全局的隔离级别,所以需要在每个终端执行。


创建数据库和测试表

创建并使用数据库:CREATE database crazy_dragon;

切换到数据库:USE crazy_dragon;

创建测试表:

sql 复制代码
CREATE table t_user_account(
	id INT NOT NULL AUTO_INCREMENT,
    name VARCHAR(20) NOT NULL,
    balance INT NOT NULL,
    PRIMARY KEY (id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

这个表是一个简化的用户账户表(id,name,balance)。

注意:balance 余额。

插入一条测试数据:

读未提交 RU

SET transaction_isolation = 'READ-UNCOMMITTED';

在读未提交(RU)的隔离级别下,会出现脏读的现象。这里假设有两个事务,简称事务 A 和 事务 B。在事务 A 在执行的过程中,它是可以感知到事务 B 的 DML 操作的,这样来说其实读未提交就相当于是没有隔离级别了。

事务 A 把 id = 1 的用户 tom 的余额更新为 1200,接着事务 B 进行查询,此时它查出的结果为 1200,然后事务 A 回滚。此时数据库中的余额仍为 1000,但是事务B读取到了1200,这就是脏读。

读已提交 RC

SET transaction_isolation = 'READ-COMMITTED';

在 RC 隔离级别下,可以避免脏读。同样,事务 A 和事务 B,现在的隔离级别是读已提交。所以,只有已经提交的数据,才可以被另一个事务感知到,这样就不会发生脏读了。

读已提交避免了脏读现象,但是还有不可重复读和幻读问题。不可重复读,指的是在一个事务范围内,多次查询结果字段值不同。幻读,值的是在一个事务范围内,多次查询结果行数不同。所以,这里要注意区分这两个的区别:不可重复读,强调是是字段值;幻读,强调的是数据结果的行数。

下面来分别演示不可重复读和幻读的情况。

第二个事务前后两次查询的的 balance 字段结果不同,这就是不可重复读(update)。多次读取的结果无法确定(无法确定另一个事务的改动)。

第二个事务两次查询的结果行数不一致,这就是幻读(insert)。

幻读和不可重复读很相似,但是幻读强调的是查询结果集合的增减,而不是单条数据的更新。

可重复读 RR

默认的隔离级别:Repeatable Read,简称 RR。可以解决不可重复读和幻读。

set transaction_isolation = 'REPEATABLE-READ'

下面分别演示了,在 RR 隔离级别下,不可重复读和幻读问题被解决。

参考博客

Understanding the Isolation Property in a Database (lifewire.com)

MySQL :: MySQL 8.0 Reference Manual :: 15.7.2.1 Transaction Isolation Levels

A primer on sql transactions

相关推荐
问道飞鱼5 小时前
【数据库相关】TxSQL新增数据库节点步骤
数据库·mysql·txsql·新增节点
Ka1Yan6 小时前
MySQL索引优化
开发语言·数据结构·数据库·mysql·算法
程序猿 董班长9 小时前
springboot配置多数据源(mysql、hive)
hive·spring boot·mysql
且行志悠14 小时前
Mysql的使用
mysql
白鹭14 小时前
MySQL源码部署(rhel7)
数据库·mysql
星期天要睡觉16 小时前
MySQL 综合练习
数据库·mysql
JosieBook17 小时前
【数据库】MySQL 数据库创建存储过程及使用场景详解
数据库·mysql
处女座_三月17 小时前
改 TDengine 数据库的时间写入限制
数据库·sql·mysql
DemonAvenger17 小时前
MySQL与应用程序的高效交互模式:从基础到实战的最佳实践
数据库·mysql·性能优化
皆过客,揽星河19 小时前
mysql进阶语法(视图)
数据库·sql·mysql·mysql基础语法·mysql进阶语法·视图创建修改删除