MySQL学习系列之InnoDB下事务隔离机制

栏目: 数据库 · 发布时间: 6年前

内容简介:MySQL中存在四种隔离等级:这里思考一下,为啥阻止幻读的隔离级别比不可重复读的高,因为InnoDB是行级锁,不可重复读是针对于对于正在修改或删除的数据行加锁,但还是可以对表进行插入,所以可能出现幻读,要避免幻读就要把表的读写都变成表级锁,才能避免幻读,也因此变成了隔离等级为“可串行读”。PS:以上内容以

MySQL中存在四种隔离等级:

隔离级别 脏读 不可重复读 幻读
未提交读(Read uncommitted) 可能 可能 可能
已提交读(Read committed) 不可能 可能 可能
可重复读(Repeatable read) 不可能 不可能 可能
可串行读(Serializable) 不可能 不可能 不可能
  • 未提交读:允许脏读,可能会读取到别的事务中未提交的临时数据
  • 已提交读:只已经提交的数据,Oracle等数据库默认是这个级别
  • 可重复读:可重复读,InnoDB默认的等级是这个,可是还是出现幻读
  • 可串行读:完全串行化的读,表示每次加锁都是表级锁,而且读写相互阻塞,读是共享锁,写是排他锁

这里思考一下,为啥阻止幻读的隔离级别比不可重复读的高,因为InnoDB是行级锁,不可重复读是针对于对于正在修改或删除的数据行加锁,但还是可以对表进行插入,所以可能出现幻读,要避免幻读就要把表的读写都变成表级锁,才能避免幻读,也因此变成了隔离等级为“可串行读”。

PS:以上内容以 悲观锁 的概念可以更好理解,不过实际中出于性能考虑,是用以 乐观锁 为理论基础的MVCC(多版本并发控制,Multi-Version Concurrency Control )

什么是悲观锁,什么是乐观锁

悲观锁:

悲观锁,正如其名,具有强烈的独占和排他特性。它指的是对数据被外界(包括本系统当前的其他事务,以及来自外部系统的事务处理)修改持保守态度,因此,在整个数据处理过程中,将数据处于锁定状态。悲观锁的实现,往往依靠数据库提供的锁机制(也只有数据库层提供的锁机制才能真正保证数据访问的排他性,否则,即使在本系统中实现了加锁机制,也无法保证外部系统不会修改数据)。

即在事务A中数据在进行读取(共享锁)的时候,其他事务不能进行修改(排他锁),当在事务A的数据进行修改(排他锁)的时候,不能进行读取(共享锁)。

优点:可以独自占有,直到执行操作完成,然后释放锁,为数据处理的安全提供了保障。

缺点:但是在效率方面,处理加锁的机制会让数据库产生额外的开销,还有增加产生死锁的机会

乐观锁:

悲观锁大多数情况下依靠数据库的锁机制实现,以保证操作最大程度的独占性。但随之而来的就是数据库 性能的大量开销,特别是对长事务而言,这样的开销往往无法承受。

而乐观锁就很好的解决了这个问题,乐观锁可以有两种方式实现,一种是version,一种是时间戳,这里以version为例,假设数据一般情况下不会发生冲突,只有在提交的时候,才会进行加锁,并判断这提交的事务的版本与当前数据库的版本的对比,如果提交的数据版本号大于数据库表当前版本号,则予以更新,否则认为是过期数据,则把请求驳回。

优点:

乐观锁机制避免了长事务中的数据库加锁开销,大大提升了大并发量下的系统整体性能表现。

缺点:

如果是在高并发下,很多用户容易出现冲突,即请求容易驳回

InnoDB中MVCC的实现:

MVCC的实现没有固定的规范,每个数据库中都会有不同的实现,这里讨论InnoDB的MVCC,其内部原理是通过乐观锁,在InnoDB中,会在每行数据后面添加两个额外的隐藏的值来实现MVCC,一个是这个数据何时被创建,一个是这数据何时过期。在实际中,这里存储的是版本号,每开启一个新的事务,事务的版本号就回增加。在可重读Repeatable reads事务隔离级别下:

  1. SELECT时,读取创建的版本号<=当前数据库的版本。删除版本为空或>当前当前事务版本的
  2. INSERT时,保存当前事务版本为行的创建版本
  3. DELETE时,保存当前版本为行的删除版本
  4. UPDATE时,插入一条新数据。保存当前事务版本为行创建版本,同一时候保存当前事务版本到原来删除的行
  5. 通过MVCC,尽管每行记录都须要额外的存储空间,很多其它的行检查工作以及一些额外的维护工作。但能够降低锁的使用,大多数读操作都不用加锁,读数据操作非常easy,性能非常好,而且也能保证仅仅会读取到符合标准的行。也仅仅锁住必要行。

通过MVCC,虽然每行记录都需要额外的存储空间,更多的行检查工作以及一些额外的维护工作,但可以减少锁的使用,大多数读操作都不用加锁,读数据操作很简单,性能很好,并且也能保证只会读取到符合标准的行,也只锁住必要行。

我们不管从数据库方面的教课书中学到,还是从网络上看到,大都是上文中事务的四种隔离级别这一模块列出的意思,RR级别是可重复读的,但无法解决幻读,而只有在Serializable级别才能解决幻读。于是我就加了一个事务C来展示效果。在事务C中添加了一条teacher_id=1的数据commit,RR级别中应该会有幻读现象,事务A在查询teacher_id=1的数据时会读到事务C新加的数据。但是测试后发现,在 MySQL 中是不存在这种情况的,在事务C提交后,事务A还是不会读到这条数据。可见在MySQL的RR级别中,是解决了幻读的读问题的。参见下图

MySQL学习系列之InnoDB下事务隔离机制

MVCC中可能读取的之前版本的数据,要如何读取当前数据呢?

这里又引申出两个概念:快照读和当前读

快照读:就是普通的select

  • select * from table ...;

当前读:特殊的读操作(是要获取锁的),例如插入/更新/删除就是当前读

  • select * from table where ? lock in share mode;(共享锁)
  • select * from table where ? for update;(排他锁)
  • insert;
  • update;
  • delete;

事务的隔离级别实际上都是定义了当前读的级别,MySQL为了减少锁处理(包括等待其它锁)的时间,提升并发能力,引入了快照读的概念,使得select不用加锁。事务的隔离只定义了读数据的要求,而写的要求当然是"当前读"。

这里注意,InnoDB默认的是行级锁,行级锁都是基于索引的。在当前读的查询语句中,如果一条 SQL 语句用不到索引是不会使用行级锁的,会使用表级锁把整张表锁住,这点需要注意。

在MySQL中,insert、update、delete语句默认会对涉及的数据集加排他锁,在Read committed等级下select语句默认是不会加的,如果要加的话,则需要显示在后面加 lock in share mode。在Repeatable read以及在Serializable隔离机制下,select是加共享锁的。

我是MySQL菜鸟,如果对于本文中有什么疑问或者问题,欢迎互相讨论提高


以上所述就是小编给大家介绍的《MySQL学习系列之InnoDB下事务隔离机制》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Algorithms + Data Structures = Programs

Algorithms + Data Structures = Programs

Niklaus Wirth / Prentice Hall / 1975-11-11 / GBP 84.95

It might seem completely dated with all its examples written in the now outmoded Pascal programming language (well, unless you are one of those Delphi zealot trying to resist to the Java/.NET dominanc......一起来看看 《Algorithms + Data Structures = Programs》 这本书的介绍吧!

SHA 加密
SHA 加密

SHA 加密工具

Markdown 在线编辑器
Markdown 在线编辑器

Markdown 在线编辑器

RGB HSV 转换
RGB HSV 转换

RGB HSV 互转工具