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

相关推荐
White_Mountain6 小时前
在Ubuntu中配置mysql,并允许外部访问数据库
数据库·mysql·ubuntu
老王笔记6 小时前
GTID下复制问题和解决
mysql
Lojarro7 小时前
【Spring】Spring框架之-AOP
java·mysql·spring
TianyaOAO7 小时前
mysql的事务控制和数据库的备份和恢复
数据库·mysql
Ewen Seong8 小时前
mysql系列5—Innodb的缓存
数据库·mysql·缓存
W21559 小时前
Liunx下MySQL:表的约束
数据库·mysql
nbsaas-boot11 小时前
探索 JSON 数据在关系型数据库中的应用:MySQL 与 SQL Server 的对比
数据库·mysql·json
奥顺11 小时前
PHPUnit使用指南:编写高效的单元测试
大数据·mysql·开源·php
苹果醋313 小时前
SpringBoot快速入门
java·运维·spring boot·mysql·nginx
ROCKY_81713 小时前
Mysql复习(一)
数据库·mysql