mysql事务的四个特性和隔离级别

本文详细介绍了MySQL中的事务ACID特性,重点解析了事务的隔离性,并探讨了不同隔离级别的概念和应用场景。脏读、不可重复读和幻读的区别也被清晰地阐述。文章还提到了MySQL中可重复读隔离级别的实现机制——多版本并发控制(MVCC),以及长事务可能带来的问题和如何避免。最后,建议合理使用事务,避免不必要的资源消耗。
摘要由CSDN通过智能技术生成

MYSQL官方文档:https://dev.mysql.com/doc/refman/8.0/en/mysql-acid.html

事务的四大特性(ACID)
  1. 原子性(Atomicity
    事务是一个不可分割的单位,事务中的所有SQL等操作要么都发生,要么都不发生。
  2. 一致性(Consistency
    事务发生前和发生后,数据的完整性必须保持一致。
  3. 隔离性(Isolation
    事务和事务之间应该有一定的隔离措施来确保数据安全,在不同的隔离级别下,隔离性有不同的表现。
  4. 持久性(Durability
    一个事务一旦被提交,它对数据库中的数据改变就是永久性的。如果出了错误,事务也不允许撤销,只能通过“补偿性事务”

对于 ACD 比较好理解,但是隔离性却要看不同的隔离级别。

隔离级别

事务的隔离级别指的是两个事务之间的隔离状态,显然也是两个会话。

查看mysql隔离级别:select @@tx_isolation

设置当前会话的的事务级别:

set session transaction isolation level read uncommitted

set session transaction isolation level repeatable read

隔离级别脏读不可重复读幻读
读未提交 RU(read-uncommitted)
读已提交 RC(read-committed)
可重复读 RR(repeatable-read)默认
串行化 SE(serializable)
名词解释
  1. 读未提交:一个事务还没有提交时,它做的变更就能被别的事务看到。
  2. 读已提交:一个事物提交之后,它做的变更才会被其他事务看到,推荐。
  3. 可重复读:一个事物执行过程中看到的数据,总是跟这个事务在启动时看到的数据是一致的,相当于事务启动时,就保存了当前数据库的快照,此事务读的时此快照而不是最新的数据。
  4. 可重复读的使用场景:假设你在管理一个个人银行账户表,一个表存了每个月月底的余额,一个表存了账单明细,这时候你要做数据校对,也就是判断上个月的余额和当前余额的差额,是否与本月的账单明细一致。你一定希望在校对过程中,即使有用户发生了一笔新的交易,也不影响你的校对结果。这时候使用“可重复读”隔离级别就很方便,事务启动时的视图可以认为是静态的,不受其他事务更新的影响。
  5. 串行化:在这种级别下事务之间绝对安全,对于同一行记录,写会加“写锁”,读会加“读锁”,当出现锁冲突时,后访问的事务需要等前一个事务执行完成,才能继续执行。比如事务A执行 select * from test,但是没提交,此时事务B向test表插入一条记录会报错,表被锁了插入失败。
  6. 脏读:事务A读取了事务B更新的数据(但是B未提交),然后B回滚操作,那么A读取到的数据是脏数据,脏读出现在RU级别,不被允许。
  7. 幻读:系统管理员A将数据库中所有学生的成绩从具体分数改为ABCDE等级,但是系统管理员B就在这个时候插入了一条具体分数的记录,当系统管理员A改结束后发现还有一条记录没有改过来,就好像发生了幻觉一样,这就叫幻读。
  8. 不可重复读的和幻读很容易混淆,不可重复读侧重于修改,幻读侧重于新增或删除。解决不可重复读的问题只需锁住满足条件的行,解决幻读需要锁表。
  9. 大部分应用中应该设置为read-committed,不可脏读,幻读也无所谓,然后就是不可重复读,这一点很重要,因为有的时候我们在一个事务中会先去插入或修改一些数据,然后在这个事务中又去读它们,所以需要不可重复读。
补充
  1. MYSQL MVCC实现机制参考链接:https://blog.csdn.net/whoamiyang/article/details/51901888
  2. 关于next-key 锁可以参考链接:https://blog.csdn.net/bigtree_3721/article/details/73731377
事务隔离的实现

理解了事务隔离级别,我们再来看看事务隔离具体是怎么实现的,这里我们展开说明“可重复读”。在MySQL中,实际上每条记录在更新的时候都会同时记录一条回滚操作,记录上的最新值,通过回滚操作,都可以得到前一个状态的值,上文说的快照就是这个意思。

假设一个值从 1 被按顺序改成了 2、3、4,在回滚日志里面就会有类似下面的记录。
在这里插入图片描述

当前值是 4,但是在查询这条记录的时候,不同时刻启动的事务会有不同的 read-view。如图中看到的,在视图 A、B、C里面,这一个记录的值分别是 1、2、4,同一条记录在系统中可以存在多个版本,就是数据库的多版本并发控制(MVCC)。对于 read-view A,要得到1,就必须将当前值依次执行图中所有的回滚操作得到。

同时你会发现,即使现在有另外一个事务正在将 4 改成 5,这个事务跟 read-view A、B、C对应的事务是不会冲突的。那么回滚日志什么时候删除呢?答案是,再不需要的时候才删除,也就是说,系统会判断,当没有事务在需要用到这些回滚日志时,回滚日志才会被删除。什么时候才是不需要了呢?就是当系统里没有比这个回滚日志更早的 read-view 的时候,一个事务对应一个 read-view ,事务一旦提交或回滚,对应的 read-view 就会被删除,而回滚日志是系统异步去清理的。

基于上面的说明,我们来讨论一下为什么建议你尽量不要使用长事务。

长事务意味着系统里面会存在很老的事务视图,由于这些事务随时可能访问数据库里面的任何数据,所以这个事务提交之前,数据库里面它可能用到的回滚记录都必须保留,这就会导致大量占用存储空间。

在MySQL 5.5 及以前的版本,回滚日志是跟数据字典一起放在 ibdata 文件里的,即使长事务最终提交,回滚段被清理,文件也不会变小,作者见过数据只有20GB,而回滚段有200GB 的库,最终只好为了清理回滚段,重新建个库。

除了对回滚段的影响,长事务还占用锁资源,也可能拖垮整个库,这个我们会在后面讲锁的时候展开。

合理使用事务

既然事务如此消耗资源,那么没必要开事务就别开了,我看到有些人查询操作也要在事务中进行,实际上只要极少数的场景可能需要这么做(比如:可重复读),但是也没必要,因为可以通过其他途径来解决。然后要设置 autocommit 为 1 ,减少事务的持续时间。

你可以在 information_schema 库的 innodb_trx 这个表中查询长事务,比如下面这个语句,用于查找持续时间超过 60s 的事务。

select * from information_schema.innodb_trx where TIME_TO_SEC(timediff(now(),trx_started))>60;

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值