MySQL进阶!深入探讨数据库事务的机制,强化学习,成就非凡

这几天粉丝问了多次数据库事务机制、隔离级别、乐观锁悲观锁类的问题,之前也有整理过MySQL相关,可能不够系统,今天就帮大家整理汇总一波。

什么是事务?

事务我理解的是一个完整的业务行为,一个业务行为可能包含多个动作,这个完整的动作就构成一个事务。比较经典的例子是银行转账,A账户转到B账户,需要两个动作:A账户减,B账户加,必须保证这两个动作要么都做,要么都不做。

事务具有ACID特征,具体包括:

  • 原子性(atomicity):原子性是说事务的不可分割,要么全成功,要么全失败,不可部分成功,部分失败。半途失败的情况下,需要打扫战场,也就是数据回滚。
  • 一致性(consistency):一致性是说事务的最后结果,要保证数据上没有异常。一致性是强调结果,是建立在原子性上实现的,也就是说能保证原子性,那就会有一致性的结果。
  • 隔离性(isolation):隔离性是说事务没有提交前对其他事务是不可见的,事务间数据是隔离的(当然不同级别隔离程度不一样)。
  • 持久性(durability): 事务提交后会持久化,可以长久保存。

事务隔离级别

了解事务的隔离级别之前,需要明白数据读取的几个概念:

  • 脏读:就是读到了别人还没提交的数据。
  • 可重复读:就是同一个事物内的两次查询,中间如果别人修改了查询内的记录且提交了,对第二次查询是不可见的,不会出现同条记录两次查询不一致问题。
  • 幻读:就是一个事物内的两次查询,中间如果别人增加了记录并且提交了,第二次查询能查询到的,会出现和第一次记录不一致的现象。

事务的控制分很多个级别,级别的高低决定隔离的程度,MySQL中分四个级别:

  • 读未提交:这种级别是最低的,A事务的修改没有提交对B事物是可见的,会出现数据的脏读,一般情况下不会用到此种类型。
  • 读已提交:A事物的修改提交后才对B可见,这种情况会出现数据的幻读的问题,两次查询的结果不一样。
  • 可重复读:是MySQL默认的级别,这种级别事物内的两次查询,中间其他修改了某条记录,对其他事务是不可见的,保证了重复查的情况下同条记录的一致性,但是对于新增的情况其他事务是可见的,所以还是会出现新增幻读的现象。
  • 可串行化:事务之间是串行执行的,对查询到的每条记录都加锁,会出现阻塞的情况,并发情况下会造成严重的性能问题,所以一般也不会用到这种类型。

MySQL进阶!深入探讨数据库事务的机制,强化学习,成就非凡_第1张图片

隔离级别一览图

事务的隔离实现

事务当中的隔离是通过两种方式控制:一种是锁的方式,通过时间上的挫开达到隔离;另一种是版本控制的方式,记录多个版本达到隔离。

1、锁

MySQL当中的锁分读锁和写锁,读锁因为是读取数据所以可以多个同时读取同一份数据,具有共享性质;写锁涉及到数据的变动,所以和其他写锁和读锁是相冲突的,具有排他性质。

从锁的粒度上分表级锁和行级锁,表锁一般发生在对表结构的修改或对全表更新的时候,会阻塞所有对这张表的读写操作;行级锁一般发生在指定记录更新的时候,只会锁定指定记录。锁的粒度越小并发度越高,能优先行级锁尽量不要表锁,和程序中锁的粒度是一样的原则。

2、多版本并发控制

MySQL为了性能考虑除了行级锁以外还是另外一种方式,多版本并发控制,这中控制是由存储引擎实现。

书中说明了InnoDB一种简单的实现方式,这种方式是采用一条记录多个版本的方式,每条记录上增加了两个隐藏列,一个是创建版本号,一个是删除版本号,每开启一个事务都会分配一个事务版本号,事务版本号是递增的,事务内操作都会根据这个版本号比较。具体如下:

  • 查询时:查询当前事务之前存在的记录和本事务创建的记录,且没有被删除的,即:创建版本号<=当前版本号 && (删除版本号为空 || 删除版本号 > 当前版本号)
  • 插入时:记录的创建版本号为当前事务版本号。
  • 删除时:更新记录的删除版本号为当前事务版本号。
  • 更新时:插入一条新记录,创建版本号为当前事务版本号,同时把原记录删除版本号改为当前事务版本号,代表已经删除。实际上这里的更新相当于删除再加一条记录。

3、乐观锁和悲观锁

锁从使用的角度又分悲观锁和乐观锁,悲观锁是持有很悲观的态度,认为我查到的数据都有可能被别人修改,所以查询的时候就把这一批数据锁起来,不让别人操作;乐观锁是持有很乐观的态度,认为我查到的数据基本不可能被别人修改,所以查询的时候不锁住这批数据,修改提交的时候再确认有没有被别人修改,有种亡羊补牢,为时不晚的意思。

乐观锁和悲观锁的实现:

  • 悲观锁可以在数据库层面很简单的解决,利用select ... for update,在查询的时候就锁定这部分数据。
  • 乐观锁的实现较悲观锁复杂,可以在数据库在一个版本号的列,更新的时候版本号都+1,以此来确认我查出来的数据后面有没有别人修改,已修改的不更新或程序抛一个异常。

使用乐观锁还是悲观锁:

从性能的角度考虑乐观锁性能更好,在查询到更新这段时间没有锁定操作,但是实现起来没有悲观锁简单,可能出错。所以要考虑的因素是系统的并发高不高?出现冲突的概率有多大?并发高的情况下选用乐观锁更好,反之选用悲观锁这种简单的方式更好。

经过这次整理,是不是豁然开朗多了?

喜欢小编请多多点赞评论转发,关注小编,后续小编会带来更丰富的Java架构学习内容,咱们共同进步。你们的支持就是小编最大的动力!!!

你可能感兴趣的:(架构,数据库,Java,MySQL,数据库,Java架构,编程开发,程序员)