3.事务隔离级别

10 篇文章 0 订阅
一. ACID解释

A: Atomicity 原子性
C: Consistencey 一致性
I: Isolation 一致性
D: Durability 持久性

二. 事务隔离级别
  1. 读未提交(read uncommited)一个事务还未提交,它的更改可以被其他事务读到。
  2. 读提交(read commited)只有一个事务提交了后,它的更改才可以被其他事务读到。
  3. 可重复读(repeatable read)一个事务执行过程中看到的数据,总是跟这个事务启动前看到的数据是一致的。当然在可重复读隔离级别下,未提交变更对其他事务也是不可见的。
  4. 串行化(serializable)对于同一行记录,读会加读锁,写会加写锁。当出现读写锁冲突的时候,后访问的事务必须等前一个事务执行完成。

知识点 读提交和可重复读的区别是:有两个事务A,B。读提交是:如果A事务在开启过程中,B事务对记录进行了更改并且提交了,A是可以读到的B事务更改后的记录。可重复读则是:就算A事务在开启过程中B事务对记录进行了更改并且提交了,A也是读不到B更改后的记录。A事务仍然读到的事它开启时记录最初的状态。只有当A事务进行提交后才能读到B更改后的记录。

Oracle默认的隔离级别是读提交
配置的方式是,将启动参数 transaction-isolation 的值设置成READ-COMMITTED。你可以用 show variables 来查看当前的值。

可重复读的应用场景

假设你在管理一个个人银行账户表。一个表存了每个月月底的余额,一个表存了账单明细。这时候你要做数据校对,也就是判断上个月的余额和当前余额的差额,是否与本月的账单明细一致。你一定希望在校对过程中,即使有用户发生了一笔新的交易,也不影响你的校对结果。

三. 事务隔离实现为什么要避免大量的大事务

在 MySQL 中,实际上每条记录在更新的时候都会同时记录一条回滚操作。记录上的最新值,通过回滚操作,都可以得到前一个状态的值。
假设一个值从 1 被按顺序改成了 2、3、4,在回滚日志里面就会有类似下面的记录。

graph RL
当前值4-->将4改成3; 
将4改成3-->将3改成2;
将3改成2-->将2改成1;

read_view_C-->无;
无-->read_view_B;
read_view_B-->read_view_A

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

当系统里没有比这个回滚日志更早的 read-view 的时候才删除日志。
长事务意味着系统里面会存在很老的事务视图。由于这些事务随时可能访问数据库里面的任何数据,所以这个事务提交之前,数据库里面它可能用到的回滚记录都必须保留,这就会导致大量占用存储空间。
除了对回滚段的影响,长事务还占用锁资源,也可能拖垮整个库。
在工作中有的公司代码中可能采用的AOP来进行事务管理,根据service层入口的方法名前缀来判断是否开启事务,经常能看到有的开发者为了不必要的麻烦所有都采用了开启事务,这是不合理的。

问题:如何避免长事务对业务的影响?

首先,从应用开发端来看:

  1. 确认是否使用了 set autocommit=0。这个确认工作可以在测试环境中开展,把 MySQL 的 general_log 开起来,然后随便跑一个业务逻辑,通过 general_log 的日志来确认。一般框架如果会设置这个值,也就会提供参数来控制行为,你的目标就是把它改成 1。
  2. 确认是否有不必要的只读事务。有些框架会习惯不管什么语句先用 begin/commit 框起来。我见过有些是业务并没有这个需要,但是也把好几个 select 语句放到了事务中。这种只读事务可以去掉。
  3. 业务连接数据库的时候,根据业务本身的预估,通过 SET MAX_EXECUTION_TIME 命令,来控制每个语句执行的最长时间,避免单个语句意外执行太长时间。(为什么会意外?在后续的文章中会提到这类案例)
    其次,从数据库端来看:
  4. 监控 information_schema.Innodb_trx 表,设置长事务阈值,超过就报警 / 或者 kill;
  5. 监控 information_schema.Innodb_trx 表,设置长事务阈值,超过就报警 / 或者 kill;
  6. 如果使用的是 MySQL 5.6 或者更新版本,把 innodb_undo_tablespaces 设置成 2(或更大的值)。如果真的出现大事务导致回滚段过大,这样设置后清理起来更方便。

阅读《MySQL实战45讲》

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值