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

什么是事务?

事务是一组原子性的SQL操做,全部操做必须所有成功完成,若是其中有任何一个操做由于崩溃或其余缘由没法执行,那么全部的操做都不会被执行。也就是说,事务内的操做,要么所有执行成功,要么所有执行失败。mysql

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

欢迎关注微信公众号:万猫学社,每周一分享Java技术干货。数据库

事务的特性

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

欢迎关注微信公众号:万猫学社,每周一分享Java技术干货。微信

原子性(Atomicity)

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

欢迎关注微信公众号:万猫学社,每周一分享Java技术干货。并发

一致性(Consistency)

事务执行的结果必须是从一个一致性的状态转换到另一个一致性的状态。当数据库只包含成功事务提交的结果时,就说数据库处于一致性状态。若是事务由于崩溃或其余缘由还没有完成,被迫中断最终事务没有提交,那么事务中所作的修改也不会保存到数据库中。高并发

欢迎关注微信公众号:万猫学社,每周一分享Java技术干货。性能

隔离性(Isolation)

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

欢迎关注微信公众号:万猫学社,每周一分享Java技术干货。

持续性(Durability)

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

欢迎关注微信公众号:万猫学社,每周一分享Java技术干货。

事务隔离级别

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

欢迎关注微信公众号:万猫学社,每周一分享Java技术干货。

未提交读(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)

欢迎关注微信公众号:万猫学社,每周一分享Java技术干货。

提交读(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)

欢迎关注微信公众号:万猫学社,每周一分享Java技术干货。

可重复读(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)

欢迎关注微信公众号:万猫学社,每周一分享Java技术干货。

可串行化(Serializable)

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

欢迎关注微信公众号:万猫学社,每周一分享Java技术干货。

总结

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

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

欢迎关注微信公众号:万猫学社,每周一分享Java技术干货。

相关文章
相关标签/搜索