理解MySQL InnoDB事务隔离级别

本文为翻译的文章,作者ovais.tariq,原文:
http://www.ovaistariq.net/597/understanding-innodb-transaction-isolation-levels/#.WztrzfkzZPZ

隔离性是ACID性质中很重要的部分,它保证事务以一种可靠的方式进行处理。隔离性确保同时运行的事务不会相互干扰。隔离性保证数据的一致性。如果事务没有被隔离,那么某个事务可能会修改其它事务正在读取的数据,因而产生了数据的不一致。

既然我们理解了隔离性是什么,让我们来掌握隔离级别。隔离级别决定了事务之间如何被隔离,它可能是没有任何的隔离或者最高级别的让事务序列化执行的级别。选择合适的隔离级别确实依赖于程序的需求,但你首先必须理解所有的隔离级别以及选择任何一种所产生的后果。

InnoDB支持所有4种SQL标准的隔离级别,列举如下 :

READ UNCOMMITTED

READ-UNCOMMITTED隔离级别在事务之间根本没有太多的隔离性。事务可以看到其他事务还没有提交的数据修改。这意味着事务可能会读到不存在的数据,因为其它正在更新数据的事务回滚了,并没有提交。这就是脏读。很少有应用需要依赖脏读,实际上这不能算是一种隔离级别。简言之,它根本就没有任何的隔离性,这样的系统实际上也不能被称为一个事务性的系统。

READ COMMITTED

READ-COMMITTED隔离级别避免了脏读的现象,因为没有提交的修改对其它事务都是不可见的,直到修改被提交。在这样的隔离级别中,每个SELECT语句都使用在执行前已提交数据的快照。因为每个SELECT语句都有自己的快照,所以相同的SELECT语句在同一个事务的不同时间执行,可能会返回不同的结果集。这种现象被称为不可重复读。

REPEATABLE READ

REPEATABLE-READ隔离级别避免了不可重复读的现象。在事务执行的整个期间,相同的SELECT语句不管运行多少次都会返回相同的结果集。SELECT语句的快照在第一次执行的时候就记下来了,相同的SELECT语句在整个事务执行期间都使用同样的快照。在这种隔离级别中运行的事务,不考虑其它事务对数据所做的任何改变。这确保了数据读取总是一致的(可重复的)。它是InnoDB默认的隔离级别。尽管这种隔离级别解决了不可重复读的问题,但仍然可能有幻读的问题。

SERIALIZABLE

SERIALIZABLE隔离级别避免了幻读的现象。运行在这个隔离级别中的事务,会在所有访问的数据行上加锁,同时锁住资源,这样就不能向事务操作的表添加记录。事务在这种情况下是以序列化的方式运行。这种隔离级别是最强的。

SERIALIZABLE隔离级别几乎可以规避

就像上面描述的,SERIALIZABLE只能帮助你避免幻读的问题,在其它方面,SERIALIZABLE几乎与REPEATABLE-READ完全一样。InnoDB有一种特殊的被称为间隙锁的功能,能够帮助你避免幻读问题。间隙锁会在索引记录之间加锁,也可能是在第一条索引记录之前或最后一条索引记录之后的区间加上锁。为了避免幻读,所有你需要做的事情是使用SELECT…FOR UPDATE或者LOCK IN SHARE MODE来加上读锁。

锁与隔离级别

READ-UNCOMMITTED隔离级别加的锁最少,随后是READ-COMMITTED,它消除了大部分的间隙锁,因而产生死锁的情况较少,另外,READ-COMMITTED只在读取的索引记录上加锁,而不是在这些记录的前后区间加锁。REPEATABLE-READ与READ-COMMITTED相比,有更高层级的加锁。UPDATE,DELETE使用邻键锁,同时,读锁也使用邻键锁。SERIALIZABLE有最高级别的锁,所有简单的SELECT语句都自动转化成SELECT…LOCK IN SHARE MODE,因而所有的记录都有共享锁。

复制与隔离级别

MySQL默认的复制类型是基于语句的复制,这种复制类型会在从服务器上重新执行在主服务器执行过的语句,通过这种方式同步数据更新。这需要更严格的隔离级别(使用更多的锁),把同样的SQL在从服务器上执行,以这样的方式来达到数据更新的一致性。就像上面提到的,READ-COMMITTED制造了不可重复读的场景,因而它对于基于语句的复制是不安全的。所以,要使用REPEATABLE-READ或者SERIALIZABLE隔离级别来进行基于语句的复制。如果你的MySQL版本大于等于5.1,那么你可以使用READ-COMMITTED来进行基于行的复制,因为基于行的复制的话,每一行数据改变的确切信息你都有。

性能与隔离级别

正如我在“锁与隔离级别”章节中提到的,SERIALIZABLE和REPEATABLE-READ使用了大量的锁,从而产生更多死锁的情况,反而过来降低了性能。事实上,SERIALIZABLE是性能最差的隔离级别,因为它甚至把普通的读取都转换成了加锁读。REPEATABLE-READ在锁和死锁方面要好一些,但READ-COMMITTED更好,因为它有更少的间隙锁。但是锁和死锁不是性能方面唯一要都考虑的事情,互斥锁争用的问题也需要考虑。Mark Callaghan有一篇文章,在互斥锁争用的上下文中比较了REPEATABLE-READ和READ-COMMITTED。

结论

我尝试对四种隔离级别,以及如何使用它们的前因后果进行了详尽地描述,我也提到了与隔离级别有关的锁和性能。现在,实际上你只有READ-COMMITED和REPEATABLE-READ这两种选择。这种选择实际上基于你的程序的类型。我不认为任何通用的基准能帮助你在这两个当中做出选择。所以在做出任何决定前,先运行程序特定的基准然后再尝试做出决定。我也由衷地希望你运行的MySQL版本大于等于5.1,因为在其它任何版本上,REPEATABLE-READ几乎是你唯一的选择。

欢迎关注微信公众号,获取更多信息。
理解MySQL InnoDB事务隔离级别_第1张图片

你可能感兴趣的:(MySQL)