深入解析Mysql中事务的四大隔离级别及其所解决的读现象

本文详细介绍四种事务隔离级别,并通过举例的方式说明不同的级别能解决什么样的读现象。并且介绍了在关系型数据库中不同的隔离级别的实现原理。

在DBMS中,事务保证了一个操作序列可以全部都执行或者全部都不执行(原子性),从一个状态转变到另外一个状态(一致性)。由于事务满足久性。所以一旦事务被提交之后,数据就能够被持久化下来,又因为事务是满足隔离性的,所以,当多个事务同时处理同一个数据的时候,多个事务直接是互不影响的,所以,在多个事务并发操作的过程中,如果控制不好隔离级别,就有可能产生脏读、不可重复读、丢失修改、或者幻读等读现象

在数据库事务的ACID四个属性中,隔离性是一个最常放松的一个。可以在数据操作过程中利用数据库的锁机制或者多版本并发控制机制获取更高的隔离等级。但是,随着数据库隔离级别的提高,数据的并发能力也会有所下降。所以,如何在并发性和隔离性之间做一个很好的权衡就成了一个至关重要的问题。

在软件开发中,几乎每类这样的问题都会有多种最佳实践来供我们参考,很多DBMS定义了多个不同的“事务隔离等级”来控制锁的程度和并发能力。

ANSI/ISO SQL定义的标准隔离级别有四种,从高到底依次为:可序列化(Serializable)、可重复读(Repeatable reads)、提交读(Read committed)、未提交读(Read uncommitted)。

下面将依次介绍这四种事务隔离级别的概念、用法以及解决了哪些问题(读现象)

未提交读(Read uncommitted)

未提交读(READ UNCOMMITTED)是最低的隔离级别。通过名字我们就可以知道,在这种事务隔离级别下,一个事务可以读到另外一个事务未提交的数据。

未提交读的数据库锁情况(实现原理)

事务在读数据的时候并未对数据加锁。事务在修改数据的时候只对数据增加行级共享锁

现象:

事务1读取某行记录时,事务2也能对这行记录进行读取、更新(因为事务一并未对数据增加任何锁)

当事务2对该记录进行更新时,事务1再次读取该记录,能读到事务2对该记录的修改版本(因为事务二只增加了共享读锁,事务一可以再增加共享读锁读取数据),即使该修改尚未被提交。

事务1更新某行记录时,事务2不能对这行记录做更新,直到事务1结束。(因为事务一对数据增加了共享读锁,事务二不能增加排他写锁进行数据的修改)

举例

事务一 事务二
/* Query 1 */ SELECT age FROM users WHERE id = 1; /* will read 20 */
/* Query 2 */ UPDATE users SET age = 21 WHERE id = 1; /* No commit here */
/* Query 1 */ SELECT age FROM users WHERE id = 1; /* will read 21 */
ROLLBACK; /* lock-based DIRTY READ */

下面还是借用我在数据库的读现象浅析一文中举的例子来说明在未提交读的隔离级别中两个事务之间的隔离情况。

事务一共查询了两次,在两次查询的过程中,事务二对数据进行了修改,并未提交(commit)。但是事务一的第二次查询查到了事务二的修改结果。在数据库的读现象浅析中我们介绍过,这种现象我们称之为脏读。

所以,未提交读会导致脏读

提交读(Read committed)

提交读(READ COMMITTED)也可以翻译成读已提交,通过名字也可以分析出,在一个事务修改数据过程中,如果事务还没提交,其他事务不能读该数据。

提交读的数据库锁情况(实现原理)

事务对当前被读取的数据加行级共享锁(当读到时才加锁)一旦读完该行,立即释放该行级共享锁;

事务在更新某数据的瞬间(就是发生更新的瞬间),必须先对其加行级排他锁,直到事务结束才释放

现象:

事务1在读取某行记录的整个过程中,事务2都可以对该行记录进行读取(因为事务一对该行记录增加行级共享锁的情况下,事务二同样可以对该数据增加共享锁来读数据。)。

事务1读取某行的一瞬间,事务2不能修改该行数据,但是,只要事务1读取完改行数据,事务2就可以对该行数据进行修改。(事务一在读取的一瞬间会对数据增加行级共享锁,任何其他事务都不能对该行数据增加排他锁。但是事务一只要读完该行数据,就会释放行级共享锁,一旦锁释放,事务二就可以对数据增加排他锁并修改数据)

事务2更新某行记录时,事务1不能对这行记录做读取或更新,直到事务2结束。(事务2在更新数据的时候,会对该行数据增加排他锁,直到事务结束才会释放锁,所以,在事务2没有提交之前,事务1都不能对数据增加共享锁进行数据的读取。所以,提交读可以解决脏读的现象

举例

事务一 事务二
/* Query 1 */ SELECT * FROM users WHERE id = 1;
/* Query 2 */ UPDATE users SET age = 21 WHERE id = 1; COMMIT; /* in multiversion concurrency control, or lock-based READ COMMITTED */
/* Query 1 */ SELECT * FROM users WHERE id = 1; COMMIT; /*lock-based REPEATABLE READ */

在提交读隔离级别中,在事务二提交之前,事务一不能读取数据。只有在事务二提交之后,事务一才能读数据。

但是从上面的例子中我们也看到,事务一在一次事务中两次读取的结果并不一致,所以提交读不能解决不可重复读的读现象。

简而言之,提交读这种隔离级别保证了读到的任何数据都是提交的数据,避免了脏读(dirty reads)。但是不保证事务重新读的时候能读到相同的数据,因为在每次数据读完之后其他事务可以修改刚才读到的数据。

可重复读(Repeatable reads)

可重复读(REPEATABLE READS),由于提交读隔离级别会产生不可重复读的读现象。所以,比提交读更高一个级别的隔离级别就可以解决不可重复读的问题。这种隔离级别就叫可重复读。

可重复读的数据库锁情况

事务在读取某数据的瞬间(就是开始读取的瞬间),必须先对其加行级共享锁,直到事务结束才释放;

事务在更新某数据的瞬间(就是发生更新的瞬间),必须先对其加行级排他锁,直到事务结束才释放。

现象

事务1在读取某行记录的整个过程中,事务2都可以对该行记录进行读取(因为事务一对该行记录增加行级共享锁的情况下,事务二同样可以对该数据增加共享锁来读数据。)。

事务1在读取某行记录的整个过程中,事务2都不能修改该行数据(事务1在读取的整个过程会对数据增加共享锁,直到事务提交才会释放锁,所以整个过程中,任何其他事务都不能对该行数据增加排他锁。所以,可重复读能够解决不可重复读的读现象

事务1更新某行记录时,事务2不能对这行记录做读取、更新,直到事务1结束。(事务1在更新数据的时候,会对该行数据增加排他锁,知道事务结束才会释放锁,所以,在事务2没有提交之前,事务一都能不对数据增加共享锁进行数据的读取。所以,提交读可以解决脏读的现象

举例

事务一 事务二
/* Query 1 */ SELECT * FROM users WHERE id = 1; COMMIT;
/* Query 2 */ UPDATE users SET age = 21 WHERE id = 1; COMMIT; /* in multiversion concurrency control, or lock-based READ COMMITTED */

在上面的例子中,只有在事务一提交之后,事务二才能更改该行数据。所以,只要在事务一从开始到结束的这段时间内,无论他读取该行数据多少次,结果都是一样的。

从上面的例子中我们可以得到结论:可重复读隔离级别可以解决不可重复读的读现象。但是可重复读这种隔离级别中,还有另外一种读现象他解决不了,那就是幻读。看下面的例子:

事务一 事务二
/* Query 1 */ SELECT * FROM users WHERE age BETWEEN 10 AND 30;
/* Query 2 */ INSERT INTO users VALUES ( 3, ‘Bob‘, 27 ); COMMIT;
/* Query 1 */ SELECT * FROM users WHERE age BETWEEN 10 AND 30;

上面的两个事务执行情况及现象如下:

1.事务一的第一次查询条件是age BETWEEN 10 AND 30;如果这是有十条记录符合条件。这时,他会给符合条件的这十条记录增加行级共享锁。任何其他事务无法更改这十条记录。

2.事务二执行一条sql语句,语句的内容是向表中插入一条数据。因为此时没有任何事务对表增加表级锁,所以,该操作可以顺利执行。

3.事务一再次执行SELECT * FROM users WHERE age BETWEEN 10 AND 30;时,结果返回的记录变成了十一条,比刚刚增加了一条,增加的这条正是事务二刚刚插入的那条。

所以,事务一的两次范围查询结果并不相同。这也就是我们提到的幻读

可序列化(Serializable)

可序列化(Serializable)是最高的隔离级别,前面提到的所有的隔离级别都无法解决的幻读,在可序列化的隔离级别中可以解决。

我们说过,产生幻读的原因是事务一在进行范围查询的时候没有增加范围锁(range-locks:给SELECT 的查询中使用一个“WHERE”子句描述范围加锁),所以导致幻读。

可序列化的数据库锁情况(实现原理)

事务在读取数据时,必须先对其加表级共享锁 ,直到事务结束才释放;

事务在更新数据时,必须先对其加表级排他锁 ,直到事务结束才释放。

现象

事务1正在读取A表中的记录时,则事务2也能读取A表,但不能对A表做更新、新增、删除,直到事务1结束。(因为事务一对表增加了表级共享锁,其他事务只能增加共享锁读取数据,不能进行其他任何操作)

事务1正在更新A表中的记录时,则事务2不能读取A表的任意记录,更不可能对A表做更新、新增、删除,直到事务1结束。(事务一对表增加了表级排他锁,其他事务不能对表增加共享锁或排他锁,也就无法进行任何操作)

虽然可序列化解决了脏读、不可重复读、幻读等读现象。但是序列化事务会产生以下效果:

1.无法读取其它事务已修改但未提交的记录。

2.在当前事务完成之前,其它事务不能修改目前事务已读取的记录。

3.在当前事务完成之前,其它事务所插入的新记录,其索引键值不能在当前事务的任何语句所读取的索引键范围中。



四种事务隔离级别从隔离程度上越来越高,但同时在并发性上也就越来越低。之所以有这么几种隔离级别,就是为了方便开发人员在开发过程中根据业务需要选择最合适的隔离级别。

原文地址:https://www.cnblogs.com/kyoner/p/11366805.html

时间: 2024-10-29 19:12:17

深入解析Mysql中事务的四大隔离级别及其所解决的读现象的相关文章

MYSQL数据库事务4种隔离级别及7种传播行为

事务的特性: 原子性:事务的不可分割,组成事务的各个逻辑单元不可分割. 一致性:事务执行的前后,数据完整性保持一致. 隔离性:事务执行不应该受到其他事务的干扰. 持久性:事务一旦结束,数据就持久化到数据库中. 查看/设置隔离级别 查看:SELECT @@tx_isolation  设置:set tx_isolation='xxx' 事务的隔离级别 如果不考虑隔离性,引发一些安全问题 隔离性:一个事务的执行,不应该受到其他事务的干扰. 脏读:一个事务读到了另一个事务未提交的数据,导致查询结果不一致

mysql中事务的四大特性

原子性(Atomicity) 事务就像“原子”一样,不可被分割,组成事务的DML操作语句要么全成功,要么全失败,不可能出现部分成功部分失败的情况. 一致性(Consistency) 一旦事务完成,不管是成功的,还是失败的,整个系统处于数据一致的状态. 隔离性(Isolation) 一个事务的执行不会被另一个事务所干扰.比如两个人同时从一个账户从取钱,通过事务的隔离性确保账户余额的正确性. 持久性(Durability) 也称为永久性,指事务一旦提交,对数据的改变就是永久的,不可以再被回滚. My

事务四大隔离级别

2.事务四大隔离级别 1.如果是两个线程并发修改,一定会互相捣乱,这时必须利用锁机制防止多个线程的并发修改 2.如果两个线程并发查询,没有线程安全问题 3.如果两个线程一个修改,一个查询......会有3种问题: 1).脏读:一个事务读取到另一个事务未提交的数据 2).不可重复读:在一个事务内读取表中的某一行数据,多次读取结果不同 --- 行级别的问题(改) 3).虚读(幻读):是指在一个事务内读取到了别的事务插入的数据,导致前后读取不一致 --- 表级别的问题(增删) 四大隔离级别: Read

『浅入深出』MySQL 中事务的实现

在关系型数据库中,事务的重要性不言而喻,只要对数据库稍有了解的人都知道事务具有 ACID 四个基本属性,而我们不知道的可能就是数据库是如何实现这四个属性的:在这篇文章中,我们将对事务的实现进行分析,尝试理解数据库是如何实现事务的,当然我们也会在文章中简单对 MySQL 中对 ACID 的实现进行简单的介绍. 事务其实就是并发控制的基本单位:相信我们都知道,事务是一个序列操作,其中的操作要么都执行,要么都不执行,它是一个不可分割的工作单位:数据库事务的 ACID 四大特性是事务的基础,了解了 AC

数据库事务的四大特性以及事务的隔离级别-与-Spring事务传播机制&隔离级别

本篇讲诉数据库中事务的四大特性(ACID),并且将会详细地说明事务的隔离级别. 如果一个数据库声称支持事务的操作,那么该数据库必须要具备以下四个特性: ⑴ 原子性(Atomicity) 原子性是指事务包含的所有操作要么全部成功,要么全部失败回滚,这和前面两篇博客介绍事务的功能是一样的概念,因此事务的操作如果成功就必须要完全应用到数据库,如果操作失败则不能对数据库有任何影响. ⑵ 一致性(Consistency) 一致性是指事务必须使数据库从一个一致性状态变换到另一个一致性状态,也就是说一个事务执

MySQL事务四个隔离级别

MySQL事务隔离级别详解             MySQL数据结构SQL SQL标准定义了4类隔离级别,包括了一些具体规则,用来限定事务内外的哪些改变是可见的,哪些是不可见的.低级别的隔离级一般支持更高的并发处理,并拥有更低的系统开销. Read Uncommitted (读取未提交的内容)  在该隔离级别,所有事务都可以看到其他未提交事务的执行结果.本隔离级别很少用于实际应用,因为它的性能也不比其他级别好多少.读取未提交的数据,也被称之为脏读(Dirty Read).Read Commit

MySQL数据库中的四种隔离级别

原文:MySQL数据库中的四种隔离级别 事务的隔离性比想象的要复杂,在 SQL 标准中定义了四种级别的隔离级别.通常而言,较低级别的隔离通常可以执行更高的并发,系统的开销也更低 READ UNCOMMITTED 该级别为未提交读.在该级别中,事务中的修改即使没有提交,对其他事务也都是可见的.事务可以读取未提交的数据,这也被称为脏读.这个级别会导致很多的问题,从性能上来说,它不会比其他级别好太多,但缺乏其他级别的很多好处.除非真的有非常必要的理由,在实际应用中一般很少使用. READ COMMIT

SQL 标准中的四种隔离级别

READ UNCOMMITED(未提交读) 在RERAD UNCOMMITED级别,事务中的修改,即使没有提交,对其他事务也都是可见的.事务可以读取未提交的数据,这也成为脏读(Dirty Read).这个级别会导致很多问题,从性能上说READ UNCOMMITED 不会比其他的级别好太多,但缺乏其他级别的好多好处,除非有非常必要的理由,在实际的应用中一般很少使用READ UNCOMMITED. READ COMMITED (提交读) 大多数数据库系统的默认隔离级别都是READ COMMITED

mysql ACID与四种隔离级别归纳总结

关于数据库的ACID特性已经有很多的介绍,这里再重新归纳总结一下: A(atomicity)原子性: 即事务要么全部做完,要么全部不做,不会出现只做一部分的情形,如A给B转帐,不会出现A的钱少了,B的钱却没有增加的情况 C(consistency)一致性: 指的是事务从一个状态到另一个状态是一致的,如A减少了100,B不可能只增加30. I(isolation)隔离性: 即一个事务在没有完成数据的提交修改时,对其它事务是不可见的.当然这里有个隔离级别的概念,在不同隔离级别下,这里会有不同的表现形