• MySQL四种隔离级别和MVCC


    事务在一个数据库中的地位尤为重要,尤其是高并发的场合。保证数据库操作的原子性和错误出现情况下的回滚,对数据的安全性和可靠性提供了保障。事务有四大原则,即ACID原则。网上关于这个问题的文章有很多,读者可以到网上看看相关的文章,我这里就不赘述了。但是需要注意的是,MySQL默认是不开启事务的,默认情况是autocommit自动提交,而如果想开启事务,需要数据库管理员或者开发者手动输入begin来开启事务。

    本文主要介绍四大原则中的I原则,即隔离级别。并在讲述I原则的时候,顺带讨论MVCC。因为MVCC通常和隔离级别是讨论到一块的。

    事务的隔离级别其实是SQL语言的标准,这里我就以自己比较常用的MySQL数据库为例进行介绍。

    客户端输入命令:

    SET [SESSION | GLOBAL]  TRANSACTION  ISOLATION  LEVEL

     {READ UNCOMMITTED | READ COMMITTED | REPEATABLE READ | SERIALIZABLE}

    小提示:也可以使用小写,MySQL是不分大小写的。

    在MySQL Workbench输入命令查询系统默认的隔离状态

    MySQL默认情况下是repeatable read。

    设置当前会话隔离级别

    设置当前会话(客户端)的隔离级别,当客户端关闭后重新进入,隔离级别会恢复到系统的全局隔离状态。

    理解‘当前会话’:

    MySQL中认为,打开一个客户端就是开启一个会话。其实质是建立一个网络连接。这个网络连接是有状态性的,一旦关闭,此会话级别中设置的变量就会恢复为系统的变量。

     

    设置全局隔离级别

    设置全局隔离级别的时候,只对全局进行修改,当前会话还是会保持原来的设置,退出重新登录才会遵循全局的设置。

    在MySQL Workbench中输入命令:

     或者在终端输入

    mysql> show variables like '%isolation';
    +-----------------------+----------------+
    | Variable_name         | Value          |
    +-----------------------+----------------+
    | transaction_isolation | READ-COMMITTED |
    | tx_isolation          | READ-COMMITTED |
    +---------------- ------+----------------+
    2 rows in set, 1 warning (0.00 sec)

    另外一种方法:

    在ini文件(Linux为conf文件)设置全局的隔离级别

    [mysqld]

    添加:

    transaction-isolation = READ-COMMITTED

    不过这种情况需要重启服务器,所以不太建议使用这种方式,除非是几乎没有请求的深夜时候进行。

    C:WINDOWSsystem32> net stop mysql
    
    MySQL 服务正在停止..
    
    MySQL 服务已成功停止。
    
     
    
    C:WINDOWSsystem32> net start mysql
    
    MySQL 服务正在启动 .
    
    MySQL 服务已经启动成功。

     关于以上命令,有一些需要理解的点:

    1)隔离级别优先级顺序 read  uncommittd < read  committed < repeatable  read < serializable

    我的理解是每一个级别都是在上一级别的基础上增加了表操作的限制。读者往下看就可以体会到这种限制的加强。

    2)session和global

    1. 默认行为(不带session和global)是为下一个(未连接)会话设置隔离级别。

    2. 如果使用GLOBAL关键字,此命令语句在全局中对从那点开始创建的所有新连接设置事务级别。

    3. 使用SESSION 关键字为当前连接上执行的事务设置默认事务级别。

    4. 任何客户端在任何时候都能自由改变会话/全局隔离级别(甚至在事务的中间),或者为下一个事务设置隔离级别。

    实际测试中,由于是本地连接,一些情况无法模拟出来,而且手动也很难做一些模拟高并发的情形,可以借助一些测试工具来进行。例如sysbench,《高性能MySQL》中也有一章专门写了基准测试。

    下面是对四个隔离级别的介绍

    1. Read Uncommitted   读取未提交的事务

    一个数据库连接实例中,它的未提交事务执行了对数据库的增删改操作,这些操作对数据的更改是没有提交到服务器磁盘的,但是在高并发的情况下,本连接的事务会不断更新数据库以获取其他连接的事务对数据库进行的最新的更改。那么第一个事务查询操作读取的数据可能是不真实的,人家都还没有提交!所以这种情况下就会出现了所谓的脏读。一般,在现实中很少使用这个级别,除非是对数据的真实性要求不高,只想获得最新的数据的情况。想想在一些金融领域的电子现金系统或者商城购物车系统使用这个级别会是怎样一个情形?

    2. Read Committed    读取已经提交的事务

          此级别在本会话中对数据库进行查询,会读取已经提交(commit)了SQL语句的事务结果,而未提交的事务的操作对数据库所造成的影响是不会读取的。可以这样理解,这个级别更像是‘事务级别’,即不同事务间内部的操作互不影响,只有提交了事务才会对其他事务产生影响。

    Read  committed级别同时也是nonrepeatable read级别(是相对下面的Repeatable  Read而言的),即不要求重复读。一个事务前后两次的读取内容可以是不同的,即允许在本事务查询的过程中其他事务对本事务查询的数据进行其他操作,这样就会出现前后两次读取的数据的不一致性。此处涉及到MVCC和乐观锁、悲观锁的概念,后面会讲述。我们先讨论这种机制的运行逻辑。

       想象一下下面这种情形:高并发情况下,多个数据库连接同时对数据库提交操作。本事务由于SQL语句比较多,执行起来比较慢。而在这期间(有时可能是1s内),有另外两个连接同时在执行操作,一个连接对一行数据的某一列进行增加2操作,而另外一个连接对同一行数据的这一列数据进行减3操作,它们在对数据库的实现上可能相差的时间很小,可能仅在毫秒之间这个时候就会出现两个vesion版本的结果,一个是执行了一个事务的版本,另一个是执行了两个事务的版本。那么本事务究竟读取哪一个版本呢?答案是指读取执行了两条事务的版本。因为在本事务内,会不断去‘查看’其他事务的提交情况,并将最新的提交情况‘反馈’在本事务中。

    3. Repeatable  Read   可重复读

          Repeatable  Read正好与上面的Read  Committed相反,本事务会读取第一个version的数据。因为在进行第一次查询操作的时候,可能第一个事务已经提交了,而第二个事务还没提交。而无论后面第二个事务甚至更多个事务提交了,但本事务中再次读取的数据时只会读取是第一读取的相同数据,即‘无视’其他事务对此行数据的更改。可以理解,在这里是悲观锁发挥了作用,即锁住了本事务,其他事务的数据的更改就无法影响到本事务。而在Read  Committed级别中,发挥作用的是乐观锁,其实就是什么也不锁定,只在更新数据的时候锁定(下面有更详细描述)。所以在本事务的前后两次范围查询中会出现数据不一致的幻读现象。

    4. Serilizable 串行化

           数据库最高级别的隔离限制,事务与事务之间串行化的。即一个事务在执行任何操作的时候,都不允许其他事务对本事务查询范围内的数据有任何操作,这里会引入一个共享锁的概念,即本事务的查询操作别读取的数据锁住了,必须等本事务完成之后才允许其他事务获取这个共享锁进行其他操作,就好比如排着队一个个执行。

    MVCC

    MVCC, Multiversion Concurrency Control多版本并发控制。MVCC是行级锁的一个变种,但是它在很多情况下避免了加锁操作, 因此服务器的开销更低(减少了锁的生产和分配)。虽然实现机制有所不同, 但大都实现了非阻塞的读操作,写操作也只锁定必要的行。

    在实现上,MySQL通过三个列实现对版本的控制,即6字节的事务ID(DB_TRX_ID)字段,7字节的回滚指针(DB_ROLL_PTR)字段 ,6字节的DB_ROW_ID字段。更多相关内容推荐看这篇文章,里面讲得很详细 https://juejin.im/entry/5a4b52eef265da431120954b

    而实际上InnoDB并非完全意义上的MVCC,因为没有实现多版本并存。关键在于并存两字,即在事务执行对数据操作时同时存在多个版本。而无论如何MySQL在事务执行的时候是串行化的,即使这两个事务几乎同时发生。其实这正是对数据安全性的一个保障。

    MVCC只在 READ  COMMITTED 和 REPEATABLE  READ 两个隔离级别下工作。其他两个隔离级别和MVCC不兼容。

    悲观锁:

    悲观锁的特点是先获取锁,再进行业务操作,即“悲观”的认为获取锁是非常有可能失败的,因此要先确保获取锁成功再进行业务操作。

    乐观锁:

    乐观锁的特点先进行业务操作,不到万不得已不去拿锁。即“乐观”的认为拿锁多半是会成功的,因此在进行完事务操作需要实际更新数据的最后一步再去拿一下锁就好。

    在实战中怎么使用,还是要看具体的数据量和业务逻辑来进行选择。

    更新说明:随着自己对MySQL数据库隔离级别的更多理解,本文在之前的内容基础上进行重新整理,并且添加上了MVCC的内容。18.12.25

    参考文章:

    https://www.cnblogs.com/phpper/p/7345332.html

    https://juejin.im/entry/5a4b52eef265da431120954b

  • 相关阅读:
    vue vant h5
    linux安装Loki+grafana日志收集系统(二进制安装)
    Map开发实例
    SQL如何获得本季度第一天、一年的第一天、本月的最后一天
    sql中union 和 union all的区别
    引用一个网络图片作为样式的致命悲剧
    SQL2005四个排名函数(row_number、rank、dense_rank和ntile)的比较
    那些代码中我们常犯的错误你有木有。。。
    SQLServer获取每组前10%的数据
    iADB隐私政策
  • 原文地址:https://www.cnblogs.com/thomson-fred/p/9764266.html
Copyright © 2020-2023  润新知