数据库是要被广大客户所共享访问的,在大量用户并发访问数据库过程中很有可能出现以下几种不不确定情况:
A、脏读:一个事务读取到了另一个事务未提交的数据操作结果。这是相当危险的,因为很可能另一个事务的所有的操作都被回滚;
B、更新丢失:两个事务都同时更新一行数据,一个事务对数据的更新把另一个事务对数据的更新覆盖了。这是因为系统没有执行任何的锁操作,因此并发事务并没有被隔离开来。
C、不可重复读:一个事务对同一行数据重复读取两次,但是却得到了不同的结果。
为了避免上面出现的几种情况,标准SQL规范中定义了四种事务隔离级别,包括了一些具体规则,用来限定事务内外的哪些改变是可见的,哪些是不可见的。不同的隔离级别对事务的处理不同,事务隔离级别越高,为避免冲突所花的精力也就越多。低级别的隔离级一般支持更高的并发处理,并拥有更低的系统开销。
四种事务隔离级别按等级从低到高分别为:
A、Read Uncommitted(读取未提交内容);
B、Read Committed(读取提交内容);
C、Repeatable Read(可重读);
D、Serializable(可串行化)。
下面分别对这四种事务隔离级别进行讲解并举例。
为了方便测试说明,先建立测试表user如下(仅含id和name两个字段,是不是够简单,没错,就是要简单):
以下四种事务隔离级别都用这个user表进行演示说明。
1、Read Uncommitted(读取未提交内容)
在该隔离级别,所有事务都可以看到其他未提交事务的执行结果。通俗地讲就是,在一个事务中可以读取到另一个事务中新增或修改但未提交的数据。因为另一个事务可能回滚,所以在第一个事务中读取到的数据很可能是无效的脏数据,造成脏读现象。读取未提交的数据,也被称为脏读。
实例如下:
更改事务隔离级别为read-uncommitted读取未提交内容:
分别开始两个事务A、B,做如下操作:
事务A |
事务B |
Query OK, 0 rows affected (0.00 sec) mysql> select * from user; Empty set (0.00 sec) |
1、开始事务: mysql> start transaction; Query OK, 0 rows affected (0.00 sec) 2、查询user表的数据: mysql> select * from user; Empty set (0.00 sec) |
|
3、插入一条数据: mysql> insert into user values(1,'user1'); Query OK, 1 row affected (0.00 sec) 4、查询user表的数据: mysql> select * from user; +----+-------+ | id | name | +----+-------+ | 1 | user1 | +----+-------+ 1 row in set (0.00 sec) |
3、查询user表的数据: mysql> select * from user; +----+-------+ | id | name | +----+-------+ | 1 | user1 | +----+-------+ 1 row in set (0.00 sec) |
|
|
5、回滚事务: mysql> rollback; Query OK, 0 rows affected (0.01 sec) mysql> select * from user; Empty set (0.00 sec) |
4、查询user表的数据: (在事务B回滚后查看数据) mysql> select * from user; Empty set (0.00 sec) |
|
经过上面的实例可以看出,事务B插入了一条数据但是没有提交,此时事务A可以查询到那条未提交的数据,但一旦事务B回滚后,事务A就查询不到那条数据。事务A在事务B提交之前查询到的那条数据是无效的,即脏数据,因为没法确保事务B最后是否会成功提交或者成功提交之前是否还会对该数据进行修改,依据脏数据所做的操作可能是不正确的,极有可能造成脏读现象。
2、Read Committed(读取提交内容)
这是大多数数据库系统的默认隔离级别(但不是mysql默认的),它满足了隔离的简单定义:一个事务只能看见已经提交事务所做的修改。换句话说,一个事务从开始直到提交之前,所做的任何修改对其他事务都是不可见的。这个级别有时候也叫做不可重复读(nonrepeatable read),因为两次执行同样的查询,可能会得到不一样的结果。
实例如下:
更改事务隔离级别为read-committed读取未提交内容:
分别开始两个事务A、B,做如下操作:
事务A |
事务B |
Query OK, 0 rows affected (0.00 sec) mysql> select * from user; Empty set (0.00 sec) |
1、开始事务: mysql> start transaction; Query OK, 0 rows affected (0.00 sec) 2、查询user表的数据: mysql> select * from user; Empty set (0.00 sec) |
|
3、插入一条数据: mysql> insert into user values(1,'user1'); Query OK, 1 row affected (0.00 sec) 4、查询user表的数据: mysql> select * from user; +----+-------+ | id | name | +----+-------+ | 1 | user1 | +----+-------+ 1 row in set (0.00 sec) |
3、查询user表的数据: (在事务B插入数据但未提交时查看数据) mysql> select * from user; Empty set (0.00 sec) |
|
|
5、提交事务: mysql> commit; Query OK, 0 rows affected (0.01 sec) |
4、查询user表的数据: (在事务B提交后查看数据) mysql> select * from user; +----+-------+ | id | name | +----+-------+ | 1 | user1 | +----+-------+ 1 row in set (0.00 sec) |
|
从实例可以看到,事务A在事务B插入数据但未提交时,查询不到数据,当直到事务B提交事务之后,事务A才可以查询到新增的数据。
经过上面的实例可以得出结论,已提交读隔离级别解决了脏读的问题,但是出现了不可重复读的问题,即事务A在两次查询的数据不一致,因为在两次查询之间事务B更新了一条数据。
3、Repeatable Read(可重读)
这是MySQL的默认事务隔离级别,它确保同一事务的多个实例在并发读取数据时,会看到同样的数据行。通俗来讲,可重复读在一个事务里读取数据,怎么读都不会变,除非提交了该事务,再次进行读取。
实例如下:
更改事务隔离级别为repeatable-read读取未提交内容:
分别开始两个事务A、B,做如下操作:
事务A |
事务B |
1、开始事务: mysql> start transaction; Query OK, 0 rows affected (0.00 sec) 2、查询user表的数据: mysql> select * from user; +----+-------+ | id | name | +----+-------+ | 1 | user1 | +----+-------+ 1 row in set (0.00 sec) |
1、开始事务: mysql> start transaction; Query OK, 0 rows affected (0.00 sec) 2、查询user表的数据: mysql> select * from user; +----+-------+ | id | name | +----+-------+ | 1 | user1 | +----+-------+ 1 row in set (0.00 sec) |
|
3、插入一条数据: mysql> insert into user values(2,'user2'); Query OK, 1 row affected (0.00 sec) 4、查询user表的数据: mysql> select * from user; +----+-------+ | id | name | +----+-------+ | 1 | user1 | | 2 | user2 | +----+-------+ 2 rows in set (0.00 sec) |
3、查询user表的数据: (在事务B插入数据但未提交时查看数据) mysql> select * from user; +----+-------+ | id | name | +----+-------+ | 1 | user1 | +----+-------+ 1 row in set (0.00 sec) |
|
|
5、提交事务: mysql> commit; Query OK, 0 rows affected (0.01 sec) |
4、查询user表的数据: (在事务B提交后查看数据) mysql> select * from user; +----+-------+ | id | name | +----+-------+ | 1 | user1 | +----+-------+ 1 row in set (0.00 sec) |
|
5、事务A提交事务之后查询user表数据: mysql> commit; Query OK, 0 rows affected (0.00 sec) mysql> select * from user; +----+-------+ | id | name | +----+-------+ | 1 | user1 | | 2 | user2 | +----+-------+ 2 rows in set (0.00 sec) |
从实例可以看到,事务A在事务B插入数据但未提交时,查询不到数据,当直到事务B提交事务之后,事务A才可以查询到新增的数据。
经过上面的实例可以得出结论,已提交读隔离级别解决了脏读的问题,但是出现了不可重复读的问题,即事务A在两次查询的数据不一致,因为在两次查询之间事务B更新了一条数据。
4、Serializable(可串行化)
这是最高的隔离级别,它通过强制事务排序,使之不可能相互冲突,从而解决幻读问题。简言之,它是在每个读的数据行上加上共享锁。通俗地讲就是,假如两个事务都操作到同一数据行,那么这个数据行就会被锁定,只允许先读取/操作到数据行的事务优先操作,只有当事务提交了,数据行才会解锁,后一个事务才能成功操作这个数据行,否则只能一直等待。就像java中的锁,写数据必须等待另一个事务结束。也正因为这样,可能导致大量的超时现象和锁竞争。
实例如下:
更改事务隔离级别为serializable读取未提交内容:
分别开始两个事务A、B,做如下操作(两个实例):
第1个实例:
事务A |
事务B |
1、开始事务: mysql> start transaction; Query OK, 0 rows affected (0.00 sec) 2、查询user表的数据: mysql> select * from user; +----+-------+ | id | name | +----+-------+ | 1 | user1 | +----+-------+ 1 row in set (0.00 sec) |
|
|
1、开始事务: mysql> start transaction; Query OK, 0 rows affected (0.00 sec) 2、查询user表的数据: mysql> select * from user; +----+-------+ | id | name | +----+-------+ | 1 | user1 | +----+-------+ 1 row in set (0.00 sec) 3、插入一条数据: mysql> insert into user values(2,'user2'); ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction |
3、提交事务 mysql> commit; Query OK, 0 rows affected (0.01 sec) |
|
第2个实例:
事务A |
事务B |
1、开始事务: mysql> start transaction; Query OK, 0 rows affected (0.00 sec) 2、查询user表的数据: mysql> select * from user; +----+-------+ | id | name | +----+-------+ | 1 | user1 | +----+-------+ 1 row in set (0.00 sec) |
|
|
1、开始事务: mysql> start transaction; Query OK, 0 rows affected (0.00 sec) 2、查询user表的数据: mysql> select * from user; +----+-------+ | id | name | +----+-------+ | 1 | user1 | +----+-------+ 1 row in set (0.00 sec) 3、插入一条数据: mysql> insert into user values(2,'user2');
一直等待一直等待一直等待... |
3、提交事务 mysql> commit; Query OK, 0 rows affected (0.01 sec) |
|
Query OK, 1 row affected (0.00 sec) 4、查询user表的数据: mysql> select * from user; +----+-------+ | id | name | +----+-------+ | 1 | user1 | | 2 | user2 | +----+-------+ 2 rows in set (0.00 sec) |
从以上两个实例可以看到,事务B对user表的新增操作需要等事务A提交之后才可以操作成功,若事务A一直不提交,在等待一定时间之后事务B就会超时报错。从实例中可以看出serializable完全锁定字段,若后一个事务来查询同一份数据就必须等待,直到前一个事务完成并解除锁定为止,是完整的隔离级别,会锁定对应的数据表,因而会有效率的问题。
PS:实践发现,事务A查询user数据但未提交时,事务B增删改时需要等待,但查询数据不需要等待;事务A增删改数据但未提交时,事务B增删查改都需要等待(当然前提是有操作到同一行数据)。
相关推荐
深入理解Mysql事务隔离级别与锁机制 Mysql事务隔离级别与锁机制是数据库系统中非常重要的概念,它们都是为了解决多事务并发问题而设计的。下面我们将深入讲解这些机制,让大家彻底理解数据库内部的执行原理。 事务...
MySQL数据库的事务隔离级别是数据库管理系统中用于控制事务处理中并发操作的重要机制。在多用户同时访问数据库的情况下,事务隔离级别能够确保数据的一致性和完整性,避免并发操作带来的各种问题,如脏读、不可重复...
MySQL作为流行的开源关系型数据库管理系统,提供了对事务隔离级别的支持。本文主要讨论如何在MySQL中查看和修改事务隔离级别的操作方法和实例。 首先,要查看当前MySQL的事务隔离级别,可以使用show variables like...
在MySQL中,可以通过`SET TRANSACTION`语句来设定事务隔离级别。例如,将当前会话的隔离级别设置为可重复读: ```sql SET TRANSACTION ISOLATION LEVEL REPEATABLE READ; ``` 查看当前的事务隔离级别,可以使用...
根据不同的事务隔离级别,MySQL的锁策略也会有所不同。不同的隔离级别能够防止不同种类的数据不一致性问题,但同时也会影响数据库的性能。隔离级别从低到高包括:读未提交(Read Uncommitted)、读已提交(Read ...
本文档将介绍MySQL中InnoDB引擎事务隔离级别与锁之间的关系,并结合美团技术团队的经验分享,为大家提供一份详细的教程。 事务隔离级别是数据库事务处理的一个重要概念,它定义了事务与事务之间的隔离程度,主要...
本详细讲解将深入探讨MySQL事务处理的各个方面,并通过实例帮助你更好地理解和应用。 一、事务的基本特性 事务具有四大特性,也被称为ACID属性: 1. 原子性(Atomicity):事务中的所有操作要么全部完成,要么全部...
### MySQL事务及锁原理详解 #### 一、事务的基本概念 事务(Transaction)是数据库操作的基本单位,一组逻辑上完整的工作单元。在事务中,一系列的操作要么全部完成,要么都不进行,确保了数据的一致性和可靠性。...
本套课程主要是对mysql的事务隔离级别以及锁相关知识进行深入讲解其中包括:mysql四大特性的深入理解、四种隔离级别的实现原理、一致性读的实现原理、全局锁、表锁、行锁,间隙锁的介绍与分析,对于难点和重点会使用...
在MySQL中,有四种事务隔离级别,分别是读未提交(READ UNCOMMITTED)、读已提交(READ COMMITTED)、可重复读(REPEATABLE READ)和串行化(SERIALIZABLE)。不同的隔离级别会带来不同的并发问题,如脏读、不可重复...
5. **事务隔离级别**:MySQL支持四种隔离级别——读未提交(READ UNCOMMITTED)、读已提交(READ COMMITTED)、可重复读(REPEATABLE READ,MySQL的默认级别)和串行化(SERIALIZABLE)。不同的隔离级别会带来不同的...
2. 事务隔离级别:讲解四种事务隔离级别(READ UNCOMMITTED、READ COMMITTED、REPEATABLE READ、SERIALIZABLE)及其应用场景。 3. 死锁与解决:理解死锁问题,以及如何通过设置锁等待超时和死锁检测机制来避免死锁...
锁机制是实现事务隔离级别的基础。MySQL中的锁可以从多个角度进行分类: 1. 从数据操作的类型分类,分为锁定读(共享锁/读锁)和锁定写(排他锁/写锁)。共享锁允许多个事务同时读取同一数据,而排他锁则阻止其他...
动力节点的杜老师讲述的mysql教程,详细讲解了MySQL的相关知识,包括MySQL概述,MySQL应用环境,MySQL系统特性,MySQL初学基础,MySQL管理工具,如何安装MySQL及MySQL新特性,通过观看可掌握MySQL全套知识。
动力节点的杜老师讲述的mysql教程,详细讲解了MySQL的相关知识,包括MySQL概述,MySQL应用环境,MySQL系统特性,MySQL初学基础,MySQL管理工具,如何安装MySQL及MySQL新特性,通过观看可掌握MySQL全套知识。
动力节点的杜老师讲述的mysql教程,详细讲解了MySQL的相关知识,包括MySQL概述,MySQL应用环境,MySQL系统特性,MySQL初学基础,MySQL管理工具,如何安装MySQL及MySQL新特性,通过观看可掌握MySQL全套知识。
数据库事务隔离分为4个隔离级别。 MySQL的默认隔离级别就是Repeatable read。 由低到高: Read Uncommitted(读未提交) Read committed(读提交) Repeatable read(可重复读) Serializable(序列化 )。