谈谈数据库隔离级别以及mysql MVCC机制

1.隔离级别介绍

  隔离级别并不是某个SQL数据库所特有的,而所有SQL数据库都要实现的一种并发事务隔离机制。隔离性其实比想象的要复杂。在SQL标准中定义了四种隔离级别,每一种隔离级别都规定了一个事务中所作的修改,哪些在事务内和事务间是可见的,哪些是不可见的。较低的级别的隔离通常可以执行更高的并发,系统的开销也更低,然而数据的改变在事务间几乎是透明,也更容易引发各种无法预估的问题。下面简单介绍下四种隔离级别:

Read Uncommitted(未提交读)

  在Read Uncommitted级别,事务的修改,即使没有提交,对其他事务也是可见的。事务可以读取其它事务未提交的数据,这也被称为脏读(Dirty Read)。这个级别会导致很多问题,从性能上说,Read Uncommitted不会比其他级别好太多,但缺乏其他级别的很多好处。除非真的有非常必要的理由,在实际应用中一般很少使用。

Read Committed(已提交读)

  大多数数据库的默认隔离级别都是Read Committed(但MySQL 不是)。Read Committed满足隔离性的简单定义:一个事务开始时,只能看见已经提交的事务所作的修改。换句话说,一个事务从开始知道提交之前,所作的任何修改对其它事务是不可见的。这个级别有时候也叫做不可重复读(Nonrepeatable Read),因为事务可以读到另一个事务提交的数据,可能存在先后两次读取到的数据不一致(中间存在另一个事务提交的数据),执行两次同样的查询,得到的结果不一致。

Repeatable Read(可重复读,MySQL默认隔离级别)

  Repeatable Read解决了不可重复读的问题。该级别保证了在同一个事务中多次读取同样的记录的结果是一致的。但是理论上,可重复读隔离级别还是无法解决另外一个幻读问题。所谓幻读,指的是当某个事务在读取某个范围内的记录时,另一个事务又在这个范围内插入了新记录,当之前的事务再次读取该范围的记录,会产生幻行。也就是说可重复读只会在修改事务有效,比如一个事务先后读取同一个范围的记录,而在这中间另一个事务对某一条记录做了修改,当前事务两次读取到的结果是一样的,但是如果是新增数据就会产生幻读的现象。为了解决在可重复读级别下发生的幻读的问题,MySQL的InnoDB和XtraDB存储引擎通过多版本并发控制机制(MVCC)解决了幻读的问题。

Serializable(可串化读)

  Serializable是最高的隔离级别。它通过强制事务串行执行,避免幻读问题。简单来说Serializable会在读取的每一行上加锁,所以可能导致大量的超时和锁竞争问题。实际开发中也很少用到这个隔离级别,只有在非常需要保证数据的一致性而且可以接受没有并发的情况下,才考虑采用该级别。

2.隔离级别演示

  以mysql演示前三个隔离级别(串读个人觉得没啥必要)。以表user(id,name,salary)为例。

Read Uncommitted(未提交读)演示

 设置隔离级别为Read Uncommitted,并开启两个事务A,B。事务A先开启事务并查询user,数据为空。

mysql> set session transaction isolation level read uncommitted; --设置隔离级别
Query OK, 0 rows affected

mysql> start transaction; --事务A
Query OK, 0 rows affected

mysql> select * from user;--查询user表,数据为空
Empty set  

再开启事务B,新增一条记录不提交。

mysql> set session transaction isolation level read uncommitted; --设置隔离级别
Query OK, 0 rows affected

mysql> start transaction; --事务B
Query OK, 0 rows affected

mysql> insert into user(name,salary) values ('jack',1000); --插入一条数据未提交
Query OK, 1 row affected
mysql> 

 这时事务A再查user表能查到数据,查到B未提交的数据

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

mysql> start transaction;
Query OK, 0 rows affected

mysql> select * from user;
Empty set

mysql> select * from user; --事务A再查询,能查到数据
+----+------+--------+
| id | name | salary |
+----+------+--------+
|  1 | jack | 1000   |
+----+------+--------+
1 row in set

  

猜你喜欢

转载自www.cnblogs.com/wy697495/p/11025929.html