五分钟后,你将真正理解MySQL事务隔离级别!

本文涉及的产品
RDS MySQL Serverless 基础系列,0.5-2RCU 50GB
RDS MySQL Serverless 高可用系列,价值2615元额度,1个月
简介: 在SQL标准中定义了四种隔离级别,每一种级别都定义了一个事务所做的修改,在另外一个事务内和事务间,哪些是可见的,哪些是不可见的。低级别的隔离级一般支持更高的并发处理,并拥有更低的系统开销。

什么是事务?

事务是一组原子性的SQL操作,所有操作必须全部成功完成,如果其中有任何一个操作因为崩溃或其他原因无法执行,那么所有的操作都不会被执行。也就是说,事务内的操作,要么全部执行成功,要么全部执行失败。

事务的结束有两种,当事务中的所有操作全部成功执行时,事务提交。如果其中一个操作失败,将发生回滚操作,撤消之前到事务开始时的所有操作。

事务的特性

一个运行良好的事务处理系统,还需要具备四个特性:原子性(Atomicity)、一致性(Consistency)、隔离性(Isolation)和持续性(Durability)。这四个特性简称为ACID特性。

原子性(Atomicity)

一个事务必须被视为一个不可分割的最小逻辑工作单元,整个事务中的所有操作要么全部提交成功,要么全部失败回滚。对于一个事务来说,不可能只执行其中的一部分操作,而不执行其中的另外一部分操作,这就是事务的原子性。

一致性(Consistency)

事务执行的结果必须是从一个一致性的状态转换到另外一个一致性的状态。当数据库只包含成功事务提交的结果时,就说数据库处于一致性状态。如果事务因为崩溃或其他原因尚未完成,被迫中断最终事务没有提交,那么事务中所做的修改也不会保存到数据库中。

隔离性(Isolation)

通常来说,一个事务的执行不能其它事务干扰。也就是说,一个事务内部的操作及使用的数据对其它并发事务是隔离的,并发执行的各个事务之间不能互相干扰。后面我们将要讲解隔离级别(Isolation Level)的时候,会发现为什么我们要说“通常来说”是隔离的。

持续性(Durability)

事务一旦提交,它对数据库中的数据的修改就应该是永久性的。此时即使系统崩溃,修改的数据也不会丢失。不过,实际上持久性也分很多不同的级别,有些持久性策略能够提供非常强的安全保障,而有些则未必。

事务隔离级别

在SQL标准中定义了四种隔离级别,每一种级别都定义了一个事务所做的修改,在另外一个事务内和事务间,哪些是可见的,哪些是不可见的。低级别的隔离级一般支持更高的并发处理,并拥有更低的系统开销。

未提交读(Read Uncommitted)

未提交读级别中,事务中的修改即使没有提交,对其他事务也是可见的。读取到了事务没有提交的数据,就被成为脏读(Dirty Read)。事务没有提交的数据是很“脏”的,被读取到会引起很多问题。从性能角度上看,未提交读级别不会比其他级别好很多,但缺乏其他级别的好处,所以在实际应用中很少被用到。

为加上深对未提交读级别的理解,让我们看一个脏读的例子,首先设置事务隔离级别为未提交读

mysql> set session transaction isolation level read uncommitted;
Query OK, 0 rows affected (0.00 sec)

再检验一下事务隔离级别:

mysql> select @@tx_isolation;
+------------------+
| @@tx_isolation   |
+------------------+
| READ-UNCOMMITTED |
+------------------+
1 row in set, 1 warning (0.00 sec)

左右分别为两个用户,左边是用户A,右边是用户B,时间线从上至下:

#用户A:查询user表,有一条OneMoreStudy的记录        ↓
mysql> select * from user;                        ↓
+----+--------------+                            ↓
| id | name         |                            ↓
+----+--------------+                            ↓
|  1 | OneMoreStudy |                            ↓
+----+--------------+                            ↓
1 row in set (0.00 sec)                            ↓
                                                ↓
                                                ↓            #用户B:开始事务
                                                ↓            mysql> start transaction;
                                                ↓            Query OK, 0 rows affected (0.00 sec)
                                                ↓            
                                                ↓            #用户B:更新user表的一条记录
                                                ↓            mysql> update user set name = 'OMS' where id = 1;
                                                ↓            Query OK, 1 row affected (0.01 sec)
                                                ↓            Rows matched: 1  Changed: 1  Warnings: 0
                                                ↓
#用户A:查询user表,有一条OMS的记录,脏读            ↓
mysql> select * from user;                        ↓
+----+------+                                    ↓
| id | name |                                    ↓
+----+------+                                    ↓
|  1 | OMS  |                                    ↓
+----+------+                                    ↓
1 row in set (0.00 sec)                            ↓
                                                ↓
                                                ↓            #用户B:提交事务
                                                ↓            mysql> commit;
                                                ↓            Query OK, 0 rows affected (0.00 sec)

提交读(Read Committed)

提交读级别中,一个事务开始时,只能查询到其他的事务已经提交的修改。也就是说,一个事务从开始到提交之前,任何的修改对其他的事务都是不可见的。提交读级别基本满足了事务的隔离性。

不过,在同一事务中两次查询之间,有其他事务的修改被提交,那么两次查询到结果可能不相同,这就是不可重复读

为了更好的理解不可重复读,让我们看一个例子,首先设置事务隔离级别为提交读

mysql> set session transaction isolation level read committed;
Query OK, 0 rows affected (0.00 sec)

再检验一下事务隔离级别:

mysql> select @@tx_isolation;
+----------------+
| @@tx_isolation |
+----------------+
| READ-COMMITTED |
+----------------+
1 row in set, 1 warning (0.00 sec)

左右分别为两个用户,左边是用户A,右边是用户B,时间线从上至下:

#用户A:开始事务
mysql> start transaction;
Query OK, 0 rows affected (0.00 sec)

#用户A:查询user表,有一条OneMoreStudy的记录
mysql> select * from user;
+----+--------------+
| id | name         |
+----+--------------+
|  1 | OneMoreStudy |
+----+--------------+
1 row in set (0.00 sec)

                                                        #用户B:更新user表的一条记录
                                                        mysql> update user set name = 'OMS' where id = 1;
                                                        Query OK, 1 row affected (0.00 sec)
                                                        Rows matched: 1  Changed: 1  Warnings: 0

#用户A:查询user表,有一条OMS的记录,不可重复读
mysql> select * from user;
+----+------+
| id | name |
+----+------+
|  1 | OMS  |
+----+------+
1 row in set (0.00 sec)

#用户A:提交事务
mysql> commit;
Query OK, 0 rows affected (0.00 sec)

可重复读(Repeatable Read)

可重复读级别中,保证了在同一个事务中多次读取同样记录的结果是一致的。即使多次读取之间有其他事务对其结果做了修改,同一个事务中多次读取的结果也是一致的。可重复读级别也是MySQL的默认事务隔离级别。

不过,当一个事务在读过某个范围内的记录时,其他事务又在这个范围内插入了新的记录,当之前的事务再一次读取这个范围的记录时,不会读取到新插入的那条记录,这被称为幻读

为了更好的理解幻读,让我们看一个例子,首先把事务隔离级别设置为可重复读

mysql> set session transaction isolation level repeatable read;
Query OK, 0 rows affected (0.00 sec)

再检验一下事务隔离级别:

mysql> select @@tx_isolation;
+-----------------+
| @@tx_isolation  |
+-----------------+
| REPEATABLE-READ |
+-----------------+
1 row in set, 1 warning (0.00 sec)

左右分别为两个用户,左边是用户A,右边是用户B,时间线从上至下:

#用户A:开始事务
mysql> start transaction;
Query OK, 0 rows affected (0.00 sec)

#用户A:查询user表,有一条记录
mysql> select * from user;
+----+--------------+
| id | name         |
+----+--------------+
|  1 | OneMoreStudy |
+----+--------------+
1 row in set (0.00 sec)

                                                            #用户B:插入一条数据
                                                            mysql> insert into user (name) value ('OneMoreStudy');
                                                            Query OK, 1 row affected (0.01 sec)

#用户A:查询user表,还是一条记录,幻读
mysql> select * from user;
+----+--------------+
| id | name         |
+----+--------------+
|  1 | OneMoreStudy |
+----+--------------+
1 row in set (0.00 sec)

#用户A:提交事务
mysql> commit;
Query OK, 0 rows affected (0.00 sec)

#用户A:查询user表,两条记录
mysql> select * from user;
+----+--------------+
| id | name         |
+----+--------------+
|  1 | OneMoreStudy |
|  2 | OneMoreStudy |
+----+--------------+
2 rows in set (0.00 sec)

可串行化(Serializable)

可串行化级别中,强制事务串行执行,是最高的隔离级别。在这个级别中,虽然避免了上面提到的幻读,但是会在读取的每一行上加锁,可能导致大量的超时和锁竞争问题,所以在实际应用中很少被用到。除非,非常需要确保数据一致性并且不要求高并发,可以采用可串行化级别。

总结

本文首先简单介绍了事务及其ACID特性,然后着重讲解了事务的四种隔离级别:

  1. 未提交读:事务中的修改即使没有提交,对其他事务也是可见的。
  2. 提交读:事务开始时,只能查询到其他的事务已经提交的修改。
  3. 可重复读:保证在同一个事务中多次读取同样记录的结果是一致的。
  4. 可串行化:强制事务串行执行。
隔离级别 脏读 不可重复读 幻读
未提交读 可能 可能 可能
提交读 不可能 可能 可能
可重复读 不可能 不可能 可能
可串行化 不可能 不可能 不可能


万猫学社

微信扫描二维码,关注万猫学社

获得更多Java技术干货。

相关实践学习
基于CentOS快速搭建LAMP环境
本教程介绍如何搭建LAMP环境,其中LAMP分别代表Linux、Apache、MySQL和PHP。
全面了解阿里云能为你做什么
阿里云在全球各地部署高效节能的绿色数据中心,利用清洁计算为万物互联的新世界提供源源不断的能源动力,目前开服的区域包括中国(华北、华东、华南、香港)、新加坡、美国(美东、美西)、欧洲、中东、澳大利亚、日本。目前阿里云的产品涵盖弹性计算、数据库、存储与CDN、分析与搜索、云通信、网络、管理与监控、应用服务、互联网中间件、移动服务、视频服务等。通过本课程,来了解阿里云能够为你的业务带来哪些帮助     相关的阿里云产品:云服务器ECS 云服务器 ECS(Elastic Compute Service)是一种弹性可伸缩的计算服务,助您降低 IT 成本,提升运维效率,使您更专注于核心业务创新。产品详情: https://www.aliyun.com/product/ecs
相关文章
|
4天前
|
SQL 安全 关系型数据库
【MySQL实战笔记】03.事务隔离:为什么你改了我还看不见?-01
【4月更文挑战第6天】MySQL事务的隔离性确保数据操作的完整性和一致性,ACID原则包括原子性、一致性、隔离性和持久性。事务隔离级别有四种:读未提交、读提交、可重复读和串行化,分别解决并发问题如脏读、不可重复读和幻读。不同隔离级别在效率和安全性间权衡,例如读未提交允许未提交变更可见,而串行化通过锁保证安全但可能降低效率。在不同隔离级别下,事务看到的数据状态会有所变化,例如在可重复读级别,事务始终看到初始数据,而在串行化级别,事务会等待其他事务完成再继续,避免数据冲突。
278 10
|
4天前
|
SQL 存储 缓存
【MySQL】事务
【MySQL】事务
13 0
|
4天前
|
SQL 存储 关系型数据库
MySQL索引及事务
MySQL索引及事务
24 2
|
4天前
|
存储 关系型数据库 MySQL
MySQL事务简述
MySQL事务简述
6 0
|
4天前
|
存储 算法 关系型数据库
MySQL事务与锁,看这一篇就够了!
MySQL事务与锁,看这一篇就够了!
|
4天前
|
Java 关系型数据库 MySQL
MySQL 索引事务
MySQL 索引事务
13 0
|
4天前
|
SQL 安全 关系型数据库
【Mysql-12】一文解读【事务】-【基本操作/四大特性/并发事务问题/事务隔离级别】
【Mysql-12】一文解读【事务】-【基本操作/四大特性/并发事务问题/事务隔离级别】
|
4天前
|
存储 关系型数据库 MySQL
Mysql学习--深入探究索引和事务的重点要点与考点
Mysql学习--深入探究索引和事务的重点要点与考点
|
4天前
|
存储 SQL 关系型数据库
Mysql_数据库事务
Mysql_数据库事务
|
4天前
|
缓存 关系型数据库 MySQL
【专栏】提升MySQL性能和高可用性的策略,包括索引优化、查询优化和事务管理
【4月更文挑战第27天】本文探讨了提升MySQL性能和高可用性的策略,包括索引优化、查询优化和事务管理。通过合理使用B-Tree和哈希索引,避免过度索引,以及优化查询语句和利用查询缓存,可以改善性能。事务管理中,应减小事务大小并及时提交,以保持系统效率。主从或双主复制可增强高可用性。综合运用这些方法,并根据实际需求调整,是优化MySQL的关键。

推荐镜像

更多