欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

深入解析MySQL的事务隔离及其对性能产生的影响

程序员文章站 2024-02-22 17:02:34
 sql标准定义了4类隔离级别,包括了一些具体规则,用来限定事务内外的哪些改变是可见的,哪些是不可见的。低级别的隔离级一般支持更高的并发处理,并拥有更低的系统开销...

 sql标准定义了4类隔离级别,包括了一些具体规则,用来限定事务内外的哪些改变是可见的,哪些是不可见的。低级别的隔离级一般支持更高的并发处理,并拥有更低的系统开销。
read uncommitted(读取未提交内容)
       在该隔离级别,所有事务都可以看到其他未提交事务的执行结果。本隔离级别很少用于实际应用,因为它的性能也不比其他级别好多少。读取未提交的数据,也被称之为脏读(dirty read)。
read committed(读取提交内容)
       这是大多数数据库系统的默认隔离级别(但不是mysql默认的)。它满足了隔离的简单定义:一个事务只能看见已经提交事务所做的改变。这种隔离级别 也支持所谓的不可重复读(nonrepeatable read),因为同一事务的其他实例在该实例处理其间可能会有新的commit,所以同一select可能返回不同结果。
repeatable read(可重读)
       这是mysql的默认事务隔离级别,它确保同一事务的多个实例在并发读取数据时,会看到同样的数据行。不过理论上,这会导致另一个棘手的问题:幻读 (phantom read)。简单的说,幻读指当用户读取某一范围的数据行时,另一个事务又在该范围内插入了新行,当用户再读取该范围的数据行时,会发现有新的“幻影” 行。innodb和falcon存储引擎通过多版本并发控制(mvcc,multiversion concurrency control)机制解决了该问题。
serializable(可串行化) 
       这是最高的隔离级别,它通过强制事务排序,使之不可能相互冲突,从而解决幻读问题。简言之,它是在每个读的数据行上加上共享锁。在这个级别,可能导致大量的超时现象和锁竞争。
         这四种隔离级别采取不同的锁类型来实现,若读取的是同一个数据的话,就容易发生问题。例如:

  •          脏读(drity read):某个事务已更新一份数据,另一个事务在此时读取了同一份数据,由于某些原因,前一个rollback了操作,则后一个事务所读取的数据就会是不正确的。
  •          不可重复读(non-repeatable read):在一个事务的两次查询之中数据不一致,这可能是两次查询过程中间插入了一个事务更新的原有的数据。
  •          幻读(phantom read):在一个事务的两次查询中数据笔数不一致,例如有一个事务查询了几列(row)数据,而另一个事务却在此时插入了新的几列数据,先前的事务在接下来的查询中,就会发现有几列数据是它先前所没有的。

         在mysql中,实现了这四种隔离级别,分别有可能产生问题如下所示:

深入解析MySQL的事务隔离及其对性能产生的影响
mysql事务隔离级别对其性能的影响
mysql默认工作在级别三下。我们知道事务隔离是为了避免并发操作相互影响而导数据的不一致性。所以为了保证数据的一致性,就引入了事务隔离的功能。以上四个级别的对数据的一致性保护是逐步提高的。级别4对事务的隔离效果最好,但是性能最差,一般不再生产环境中使用。
下面通过实例来检验不同级别下mysql性能收到的影响。我的实验环境是:redhat5.8+mysql5.5
首先我们这里启用两个session:
1、验证级别一的特性
我们在session a上进行的操作为:

深入解析MySQL的事务隔离及其对性能产生的影响

在session b上的操作同session a,这里不再附上截图。
       接下来我们就通过一系列的实验来观察read-uncommitted到底是什么,它到底有什么特性,对我们的操作到底有什么影响。首先,我们可以看到表中的初始数据如下:

深入解析MySQL的事务隔离及其对性能产生的影响

接下来我们在sessiona上更改其中的一条记录,更改结果如下:

深入解析MySQL的事务隔离及其对性能产生的影响

注意:我们在上面启用了事务,但是我们在这里并没有进行commit操作。
 
接下来我们在sessionb中对刚才改过的表进行select查询,查询结果如下:

深入解析MySQL的事务隔离及其对性能产生的影响

我们可以清楚的看到,虽然我们并没有对session a的结果进行commit,但是结果确实已经改变。因此在这种级别下,没有提交的操作会对数据的一致性有影响。因此,如果我们此时在session a上对上述操作进行回滚,我们会发现此时session b上的结果又回到原来最初的结果,这样就造成了数据的不一致性,这也称为数据的幻读现象,看起来是很诡异的事情。因此在某些场景下,我们应该避免这种现象的产生。但是这种级别也不是没有它的用武之地,比如当我们有大量数据需要写入,而读操作很少的时候,就适合用这种模式。
可以看到session a回滚后,session b中的数据又变成最初的样子,这也称为幻读:

深入解析MySQL的事务隔离及其对性能产生的影响

2、验证级别read committed特性
       首先把session a和session b的隔离级别都改为read-committed,并且全部都开启事务,操作如下:

深入解析MySQL的事务隔离及其对性能产生的影响

接下来我们查看tutors表的初始状态信息:

深入解析MySQL的事务隔离及其对性能产生的影响

然后我们依然是对数据进行更新操作,更新之后仍然没有commit。我们可以看到在sessiona中,结果已经发生改变:

深入解析MySQL的事务隔离及其对性能产生的影响

此时我们在session b中查看,发现结果依然维持不变:

深入解析MySQL的事务隔离及其对性能产生的影响

但是,如果我们此时在session a中进行commit操作,我们就会发现,sessionb此时查询就会发生改变,这样也造成了数据的前后不一致性,也是数据的幻读:

深入解析MySQL的事务隔离及其对性能产生的影响

3、数据的可重读
       数据的可重读,也叫作repeatable-read,这是mysql默认采用的事务隔离级别,有其优势,但是仍然没有从根本上解决数据的一致性问题。首先,还是让我们来测试一下,在这种级别下mysql到底是如何工作的,又有哪些特性,我们又该怎样去操作。
       我们先把repeatable-read的环境设置好,具体的操作方法如下:

深入解析MySQL的事务隔离及其对性能产生的影响

然后我们在查看其初始数据,其结果如下:

深入解析MySQL的事务隔离及其对性能产生的影响

我们在session a中修改数据,并进行commit,修改后的结果如下:

深入解析MySQL的事务隔离及其对性能产生的影响

然后我们在session b中进行查看发现结果仍然没有任何改变:

深入解析MySQL的事务隔离及其对性能产生的影响

这就是可重读的特性,只要本次会话不提交,尽管对方修改,但是结果仍然不变,只有在session b中也进行commit操作,所作的修改才会在sessionb中生效。
 
4、seriabliable
这个级别是事务隔离安全性最好的,但是也是性能最差的,因为这个级别所有的操作都是串行进行的。一个操作没有提交,另一个受到影响的操作会处于阻塞状态。
为了验证这种效果,我们先把环境设置好,具体为在session a和session b同时设置如下:

深入解析MySQL的事务隔离及其对性能产生的影响

在session a 中对其任意字段进行修改,并且没有进行commit操作。此时挥发现sessionb中的查询操作会一直处于阻塞状态:

深入解析MySQL的事务隔离及其对性能产生的影响

这就设串行化隔离的效果,也是为什么串行化隔离并发能力差的原因。