5°

MySQL事务隔离

事务就是一组数据库操作要么全部成功,要么全部失败。

事务的四大特性:ACID(Atomicity、Consistency、Isolation、Durability,即原子性、一致性、隔离性、持久性)

MySQL中事务支持是在引擎层实现的,而MySQL的原生MyISAM引擎就不支持事务,所以被InnoDB取代。

隔离性与隔离级别

数据库中多个事务同时执行时,可能出现以下问题:

  • 脏读:读取到其他事务未提交的数据;
  • 不可重复读:读取到其他事物已提交的数据,即本次事务多次读取的数据不一致;
  • 幻读:本次事务修改后又被其它事务改回去,误以为本次修改未生效。

为了解决这些问题,SQL标准规定了以下四个事务隔离级别;隔离得越严实,效率就会越低。。

  • 读未提交:一个事务还未提交时,它做的变更就能被别的事务看到;隔离级别最低,会导致脏读、幻读以及不可重复读。
  • 读提交:一个事务提交之后,它做的变更才会被其他事务看到;避免了脏读,但仍会导致幻读和不可重复读。
  • 可重复读:一个事务执行过程中看到的数据,总是跟这个事务在启动时看到的数据是一致的;能够避免脏读和不可重复读,但仍会导致幻读。
  • 串行化:对于同一行记录读写都会加锁,当出现读写锁冲突的时候,后访问的事务必须等前一个事务执行完成,才能继续执行;能够避免以上三个问题,但执行效率最低。

下面通过举例事务A/B说明这四种事务隔离级别:

  • 如果隔离级别是读未提交:那么V1、V2、V3的值为2;虽然事务B还未提交,但结果已被事务A看到了。
  • 如果隔离级别是读提交:那么V1的值为1,V2、V3的值为2;只有在事务B提交后,V2才能读取到变更的值2。
  • 如果隔离级别是可重复读:那么V1、V2的值为1,V3的值为2;即无论事务B是否提交,事务A在执行期间看到的数据前后必须是一致的。
  • 如果隔离级别是串行化:那么事务A在查询到值后便会加读锁,直到事务A提交(释放读锁)后,事务B才能查询到结果并修改(增加读写锁);所以V1、V2都是1,V3是2。

InnoDB的事务隔离实现

MySQL的默认隔离级别为可重复读,并且还解决了幻读问题;下面我们来讨论可重复读。

MVCC多版本并发控制

在InnoDB中给每行增加两个字段来实现MVCC,分别是创建的事务版本号和删除的版本号,每开启一个事务,事务的版本号就会递增。

  • SELECT:创建版本号<=当前版本号 AND (删除版本号 IS NULL OR 删除版本号 > 当前版本号)
  • INSERT:将当前事务版本号保存至新行的创建版本号
  • UPDATE:新插入一行,并且将当前事务版本号作为新行的创建版本号;将原纪录行的删除版本号设置为当前事务版本号
  • DELETE:将当前事务版本号保存至该行的删除版本号
快照读/当前读、一致性非锁定读/锁定读
  • 快照读:读取的是快照版本,也就是历史版本。
  • 当前读:读取的是最新版本。
  • 一致性非锁定读:不会给它所访问的表加任何形式的锁,其它事务可以同时并发的修改它们。
  • 锁定读:SELECT ... LOCK IN SHARE MODE 给记录添加共享锁,其它事务只能读取不能修改;SELECT ... FOR UPDATE给索引记录加锁,加锁情况桶UPDATE。

在MySQL默认的可重复读中,普通的SELECT是一致性非锁定的快照读,读取的是事务创建时的快照;而UPDATE、DELETE、INSERT、SELECT ... LOCK IN SHARE MODE、SELECT ... FOR UPDATE是锁定的当前读。假设当前事务执行了锁定的当前读,那么其它事务将不能进行修改直到当前事务提交。

长事务

事务在提交之前会保留回滚记录,导致大量占用存储空间,并且还会占用锁资源,所以尽量避免使用长事务;如果无法避免,应保证逻辑日志空间足够用,并且支持动态日志空间增长。监控Innodb_trx表,发现长事务报警。

本文由【哥哥曾啊】发布于开源中国,原文链接:https://my.oschina.net/qgmzzmn/blog/3158146

全部评论: 0

    我有话说: