MYSQL事务隔离机制及事务优化

在并发执行多个事务操作,对同一批数据进行操作时,会导致一些系列事务问题:脏写,脏读,不可重复读,幻读等。为解决这些事务问题,mysql 通过事务隔离机制,锁机制,MVCC及日志机制等来避免上述问题。本文就来讲讲其中的事务隔离机制。

事务特性:

事务有四个特性,简称ACID。

  • 原子性(Atomicity)  即,一个事务内的操作,要么全部成功,要么全部失败。在mysql 中使用undolog 保证事务的原子性。
  • 一致性 (Consistent) 即,事务操作操作完成后,其结果要保持一致。比如A事务对小张账户转账至小李账户100元,那么事务结束后,小张账户中应该减少100元,同时小李账户中应该多100元。其实,其他三种特性都是为了保证一致性而存在。
  • 隔离性Isolation) 即,事务与事务之间,在操作时,互不影响。该特性,mysql 主要以MVCC机制及锁来实现。
  • 持久性(Duration) 即,一旦事务将数据提交后,数据将被持久化至磁盘中,再也不会丢失。在mysql 中使用redo log(防数据丢失,顺序写提升性能 等等) 来支持该特性。

并发事务带来的问题:

  • 更新丢失或脏写  比如,事务a 读取了某一条数据,并打算在程序中对其进行操作后进行更新,事务b 同时也同样做此操作。那么很有可能a 读取到的数据 100+10 后进行数据库更新,b 读取到100+20 进行数据更新,那么此时a提交了数据 同时比也提交了数据,那么数据库中的数据可能就变成了120 ,b 更新的数据覆盖掉了a 更新的数据。
  • 脏读  一个事务读取到了另外一个事务未提交的数据。即,事务a 对数据操作后尚未提交,却被b 读取到了。
  • 不可重复读  在同一个事务中,对同一条数据,读取多次,出现了不同的结果。
  • 幻读  一个事务读取到了另外一个事务新增的数据。

事务隔离机制

以上并发问题,为事务一致性问题,数据库为其提供了隔离机制来处理上述问题。

  • 读未提交(Read uncommitted)该机制为对低的隔离级别,如果数据库隔离级别设置为此,那么以上并发事务问题将都会存在。其中最严重问题就是脏数据!为什么是脏数据呢?比如A事务读取到了B事务未提交的数据,但是B事务因为异常等原因回滚了数据。但是此时A已经拿到该数据进行了后续操作,但是这个数据是不存在的。
  • 读已提交(Read committed)相对于读未提交,该级别,明显无法读取到其他事务未提交的数据,也就是说该级别能够很好的解决脏数据问题。但是,不可重复读及幻读问题依然存在。
  • 可重复读(Read Repeatable)与读已提交相比,该级别拥有了mysql 数据库MVCC机制的加持,使得在一个事务中,select 语句读到的始终是事务开始时,简单的说,数据库此刻的快照数据。也就是说,不管读取多少次,对同一数据来说,读取到的和第一次读取到的始终是一致的。但是,如果一个事务在当前事务期间提交了一条新的数据,在当前事务中,我们去对该条数据进行‘盲改‘ 是完全可以成功的然后在执行原查询,我们发现我们突然查到了那条新数据。这就说明,我们读取到了其他数据新增的数据,幻读仍然没有得到解决。
  • 串行读(Serialiable)隔离级别最高级,也是并发程度最低的级别。所有操作都需串行化执行,所以该级别可以解决所有并行带来的事务问题。

以上,便为数据库事务隔离级别相关的介绍。在实际使用时,我们应该根据实际情况来选择使用。比如在一个只有查询的方法中,我们是否应该使用事务呢?假如该方法中只有一条select 语句,那么我们完全不需要加事务,因为我们无需考虑是否读取到多个版本的数据。但是如果该方法中有多个select语句,那么我们就要考虑。假如我们当前隔离级别为RR,那么我们是要添加事务的,因为只有在事务中,RR才会有效(RR是基于在事务对数据更新操作时,将不同时刻操作形成undo log 链基础上完成的)。但是如果我们使用的是RC,那么加与不加事务,我们都将可以读取到别的事务提交的数据,所以也无需添加。

事务优化:

在平常的开发中,如果一个方法在较大事务,那么很容易出现问题:

  • 并发情况下,数据库连接池容易被撑爆 如果事务过大,也就意味着事务中,存在大量的update insert 等语句,而每次update 操作都将对相应数据进行加锁操作,由于锁只有在事务commit 时才会被释放,就会导致在高并发情况下,大量事务因无法获取到对应的数据锁而阻塞,但是数据库连接池连接数是有限的,那么这种情况下就会导致连接池被撑爆。
  • 锁定太多的数据,造成大量的阻塞和锁超时 事务过大 ,高并发情况下对大量数据锁定的情况下,就会导致大量阻塞及锁等待。
  • 执行时间长,容易造成主从延迟
  • 回滚所需要的时间比较长 事务过大,那么一旦需要回滚,就需要根据undo log 版本链进行一个回滚,所以需要的时间也将较长
  • undo log膨胀 事务过大,在事务没有提交情况下,将不停生成undo log 日志,从而导致undo log 不能及时被清理而不断膨胀。
  • 容易导致死锁 因为每个新的事务都会锁定一定数据,但事务过大无法及时释放锁,就极有可能导致死锁。

如何优化?原则:

  • 将查询等数据准备操作放到事务外 对RC 级别,我们将其放在事务中与事务外都基本差不多,但是,没有事务,将提升较大性能。所以若非RR隔离级别,将查询操作放在事务外是个不错的选择。
  • 事务中避免远程调用,远程调用要设置超时,防止事务等待时间太久 尽量避免当前系统因为其他系统的问题带来的影响。
  • 事务中避免一次性处理太多数据,可以拆分成多个事务分次处理  一次性处理过多数据,会导致事务时间延长,从而导致连接数暴涨,死锁等一系列问题。所以在业务允许的情况下,尽可能对数据进行拆成多个事务进行处理。
  • 更新等涉及加锁的操作尽可能放在事务靠后的位置  更新操作涉及对已有数据做操作,所以放在靠近事务提交的位置,一旦处理完成就进行事务提交,可以大大降低其他事务操作相同数据的等待时间。
  • 能异步处理的尽量异步处理
  • 应用侧(业务代码)保证数据一致性,非事务执行  此方式能够大大提升并发,但是需要自己在业务处理中加入各种异常情况的处理,比如数据回滚等。所以如果业务较复杂,谨慎选择。

以上。

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值