事务与视图

99 阅读7分钟

本文已参与「新人创作礼」活动,一起开启掘金创作之路。

事务

事务能够简化编程模型,其本质上是为了当应用程序访问数据库的时候,帮助用户规避各种各样的潜在错误与并发问题。当我们使用事务时,要么提交,要么回滚,用户并不需要考虑网络异常,服务器异常等问题。

事务是由一组DML语句组成的,这一组DML语句在逻辑上存在关联性,它们要么全部成功,要么全部失败,是一个整体。

事务主要用于操作量大,复杂度高的数据,比如我们注销一个账号,服务器不仅仅需要删除我们的基本资料,还需要删除与我们相关的信息,例如邮箱,文章等等。而完成这一系列操作的语句的集合就构成了一个事务。

ACID

一般来说,事务必须满足ACID四个条件:

  • 原子性(Atomicity):一个事务中的所有操作,要么全部完成,要么全部不完成,不会在中间某个环节结束。事务在执行过程中发生错误则会发生回滚,即回到事务开始前的状态。
  • 一致性(Consistency):在事务开始之前与完成之后,数据库的完整性没有被破坏。这表示事务中的所有操作都符合预设的规则。
  • 隔离性(Isolation):数据库允许多个并发事务同时对其数据进行读写和修改的能力,隔离性可以防止多个事务并发执行时由于交叉执行而导致数据的不一致。
  • 持久性(Durability):事务处理结束后,对数据的修改就是永久的,即便系统故障也不会丢失。

隔离级别

要了解隔离级别首先就要深入理解一下隔离性

为什么会有隔离性?

  • 首先,MySQL服务可能会被多个客户端或线程访问,而为了规避潜在的诸多错误,访问通常是以事务的方式进行的。
  • 一个事务是由多条DML语句组成的,也就是说,事务在执行过程中,存在执行前、执行中、执行后三个阶段,而为了安全,通常只会令用户看到执行前或者执行后的状态。执行中的情况用户是不可知的,执行中出现问题,系统会自动帮我们回滚。也由此使单个事务表现出来原子性。
  • 由于执行过程可能会比较长,所以多个事务各自执行的时候就有可能出现相互影响的情况。
  • 为了保证事务执行过程中不会受到其他事务的影响,由此衍生出了一个重要特征:隔离性
  • 隔离性中,允许事务受到不同程度的干扰,而这些干扰程度便被称为:隔离级别

隔离级别通常分为读未提交、读提交、可重复读以及串行化四种。

与我们在多线程中保证操作的原子性的方法一致,隔离级别也是通过锁来实现隔离的,不同的隔离级别所使用的锁是不同的。常见的有表锁、行锁、读锁、写锁、间隙锁等等……

隔离级别简单释义:

  • 读未提交:所谓的读未提交,便如其名,所有的事务都可以看到其他事务尚在操作中没有提交的执行结果,也就是没有任何隔离性。会出现脏读、幻读、不可重复读等问题。
  • 读提交:大多数数据库都默认使用该隔离级别(很巧,MySQL不是),就如它的名字一般,一个事务只能看到其他的已经提交的事务所做出的改变。这种隔离级别会出现不可重复读以及幻读,简单来说,多次读取得到不同结果(我们后边还会将它与幻读拿出来再讲)。
  • 可重复读:这个才是MySQL默认的隔离级别,它保证了同一个事务在执行过程中,多次读取到同样的记录。但还是会产生幻读的问题。
  • 串行化:这是最高级别的隔离,也是基本上不会使用的隔离级别。它通过强制事务排序来保证事务与事务之间不可能起冲突,从而解决了幻读的问题。

脏读、幻读、不可重复读

这是与事务的隔离性息息相关的三种读取问题。

  • 脏读:读取未提交的数据。这个“脏”字实际上用的很传神,就像我们玩LOL或者看一些LOL视频的时候,如果打野在路过中路或某条路的时候偷偷的“摸”了两个小兵,就会有人调侃道,“你这小手可不干净啊”,“你这手怎么有点脏啊”。本来该属于人家操作,数据的原本使用者还没有放线的的时候,其他的事务就过来读取了人家的数据,想起来多少也是“小手有点脏”。
  • 不可重复读:同一个记录,同一条数据,两次读取读到的结果不一样。不可重复读的重点就在于,它是相对于同一个记录、同一条数据来说的。事务A通过某些条件筛查出一条数据,内容是10,但是过了一阵子后,再次筛查时,由于事务B在这段时间内对数据进行了更改,使得这次事务A读出的数据反倒成了20,两次读取找到的数据都只有这一条,但数据具体的内容发生了改变。
  • 幻读:与不可重复读不一样,幻读指的是前后多次读取时,读取到的数据总量不一致。就像有的时候你明明记得你写了六份作业,结果到了交作业的时候忽然发现,只写了五份,莫名奇妙少了一份,像是产生了幻觉一般。

隔离级别与对应问题

  • 读未提交:脏读、幻读、不可重复读
  • 读提交:幻读、不可重复读
  • 可重复读:幻读
  • 串行化:无

视图

MySQL种,视图指的是一种虚拟存在的表(为什么不叫虚表呢?我想如果那样的话学C++的我头发就掉的更快了)。与真实的表一样,视图也是由行和列构成的,但是它并不实际存在于数据库之中。

视图的行和列中的数据来自于定义视图时我们所用到的表,并且还是在使用视图的时候才动态生成的。也就是说,数据库种实际上只是存放了视图的定义,没有存放视图的数据,或者说是没有将视图的数据集中存放起来。视图本身定义时,其内的数据便是来自于其他真实存在的表,当我们不使用视图的时候,这些数据该在哪里,它就在原表中,而一旦我们使用到视图,通过视图来获取数据时,系统就会从对应的表中取出对应的数据来。

以此看来,视图实际上更像是C/C++中的指针,或者说一组指针,当我们使用视图来查询数据时,系统便会通过视图的定义找到这个定义所指向位置的数据。而且,与指针相似的是,视图的数据是依赖于真实表中的数据的,一旦真实表中的数据发生了改变,那么通过视图找到的数据也会发生改变。

视图可以从原有的表上选取对用户有用的信息,那些对用户没用,或者用户没有权限了解的信息,都可以直接屏蔽掉,作用类似于筛选。这样做既使应用简单化,也保证了系统的安全。