mysql中级篇之事务

1. 事务的介绍

  事务(Transaction)在mysql里属于TCL语言,也就是事务控制语言,那么什么是事务?
  答:事务是一个一组sql语句组成的一个执行单元(操作的集合),这个执行单元要么全部执行,要么全部不执行
  在第二段我解释了什么是事务,说白了就是我把多条sql语句看成一个整体,那么这多条sql语句就会相互依赖,一旦有一个出现错误或异常,就会导致其它语句不能正常执行,包括在它前面本来执行成功的语句也会受到影响,操作变为无效。同时要注意,我在第二段说的是一个或一组sql语句,也就是说,一条sql语句也是一次事务,这个sql语句就是DML语句(因为DML语句是和数据库中表的数据有关的)。
  最经典的例子就是转账的例子,不管怎么样,转账必定会涉及到收款和扣款,那么相应的,收款和扣款就分别对应两条sql语句,比如,我们先在数据库里新建一个账户表,并添加数据,如下:
在这里插入图片描述  那么如果ting要向qi转账500块钱,那么如果转账成功是不是ting就剩下500块,而qi就有1500块了呀,对应的sql语句如下:

update account set money=500 where username="ting";
update account set money=1500 where username="qi";

  我们发现,扣款的操作和收款的操作是不是对应着两条sql语句?既然这样做,有什么缺陷?假设在执行完ting的扣款操作后,就出现了异常,是不是就会导致qi的收款操作不能正常执行?是不是就造成了ting的钱减了,而qi的钱并没加,在现实当中,我们允许出现这种情况吗?肯定是不允许的,所以我们要引入一个东西就叫事务,也就是说,我们要把以上两条sql语句看成一个整体,放到事务环境内,要么全部成功,要么全部失败,是不是就解决了我上面所说的问题呀!也可以说,事务的存在可以保证数据的完整性和安全性。

2. 事务的四大特性

  好,既然在转账的过程中我们要把扣款和收款看成一个整体来操作,也就是要把扣款和收款要放到一个事务环境内,暂且不说事务如何开启,如何提交,就说你作为一个合格的事务要满足哪些特点?如下:
  1)原子性(Atomicity):原子性是指事务是一个不可分割的工作单元,事务中的操作要么都发生,要么都不发生。
  2)一致性(Consistency):事务开始和结束,数据都必须保持一致。还是上面那个例子,ting一开始是1000块,qi也是1000块,总和是不是2000块,那么我ting转500块给qi,我虽然少了500块,但是qi多了500块,总和是不是还是2000块呀,这就是一致性。
  3)隔离性(Isolation):事务的隔离性是指一个事务的执行不能被其他事务干扰,即一个事务内部的操作及使用的数据对并发的其它事务是隔离的,并发执行的各个事务之间不能互相干扰。注意它们的干扰程度就是后面要学的隔离级别。举个例子:ting转500块给qi,但同时有另外一个人,叫jack,也发500块给qi,是不是可以理解成有两个事务在并发执行?那么对于ting而言,他就希望两个事务相互独立,相互隔离。
  4)持久性(Durability):持久性是指一个事务一旦被提交(提交到磁盘),它对数据库中数据的改变就是永久性的,接下来的其它操作和数据库故障都不会对其有任何影响。比如有一条普通的删除语句,虽然只有一条,但也能构成一个事务(我前面已经说了),所以当你用删除语句去删除某一行时,就不能撤回了,除非你用插入语句重新插入,也就是重新开辟一个新的事务。
  说到这,你是不是对事务有了进一步的理解,并且在上面说的这四个特点,我们可以简称为ACID。那么在这四个当中,最需要注意的就是隔离性,因为隔离性涉及到多个事务的并发执行,就会导致问题的产生,主要就只有三个问题,哪三个?请看下一节。

3. 事务并发带来的问题

  对于同时运行的多个事务,当这些事务访问数据库中相同的数据时,如果没有采取必要的隔离机制,就会导致各种并发问题。
  1)脏读:对于两个事务T1,T2。T1读取了已经被T2更新但还没提交的数据,之后,若T2回滚,T1读取的内容就是临时且无效的。
  2) 不可重复读:对于两个事务T1,T2。T1读取了一个字段,然后T2更新(或更新并提交,注意此处的更新指的是修改或者删除)了该字段,之后,T1再次读取同一个字段,值就不同了。大白话讲,就是在你的事务内,多次查询的结果都不一样,说明你的数据被他人篡改了,大概就是这个道理。
  3) 幻读(虚读):对于两个事务T1,T2。T1从一个表中读取了一个字段,然后T2在该表中插入了一些新的行,之后,如果T1再次读取同一个表,就会多出几行。
  上面三个问题,最容易搞混的就是不可重复读和幻读,你只要记住,不可重复读针对的是修改和删除,而幻读针对的是添加。
  上面列出来的三个问题都会导致事务的隔离性问题,那么针对这种情况,我们怎么避免呢?那这又要引出一些概念了,也就是隔离级别,就是来解决并发过程中可能会出现的问题的,是一种解决方案,对应mysql而言,它支持四种解决方案,但是对于oracle而言,只支持两种解决方案,那好,我们就先把四种解决方案列出来吧,请看下一节。

4. 事务的隔离级别

隔离级别描述
read uncommitted(读未提交数据)允许事务读取未被其它事务提交的变更,脏读,不可重复读和幻读的问题都会出现,只能保证持久性
read committed(读已提交数据)只允许事务读取已经被其它事务提交的变更,可以避免脏读,但不可重复读和幻读的问题仍然可能出现
repeatable read(可重复读)确保事务可以多次从一个字段中读取相同的值,在这个事务持续期间,禁止其他事务对这个字段进行更新,可以避免脏读和不可重复读,但幻读的问题仍然存在
serializable(串行化)确保事务可以从一个表中读取相同的行,在这个事务持续期间,禁止其它事务对该表执行插入,更新和删除操作,所有并发问题都可以避免,但性能十分低下

  以上四个隔离级别从上到下是越来越高的,但并发性也越来越低,所以性能也越来越低。对应mysql,以上四种都支持,但是我们在用的时候不会去用read uncommitted和serializable,毕竟第一个什么都不能解决,最后一个性能也太低了,mysql本来并发性就不好,所以我们也不会去用。mysql默认的隔离级别是repeatable read,而oracle的默认隔离级别则是read commited。
  既然知道了以上四种解决方案,虽然你看的时候很懵逼,但没关系,我们通过案例来演示一下,案例演示请看下一节。

5. 案例演示的准备工作

  在演示之前,我们要先做一件事情,我记得上面说过,一条sql语句也是一次事务,也就是说,如下:

update account set money=500 where username="ting";

  该语句就是一次事务,所以说,不要以为事务就是由2个或2个以上的语句构成的,一个语句也可以构成一次事务,只是我们在执行的时候并没有体会到而已,但它确确实实就是一次事务,并且它是不是对数据库里的数据进行实实在在的更改了呀,是不是永久性的,其实这其中的更改包含两部分,哪两部分?第一部分,就是到内存中进行更改数据,然后第二部就是你要么提交,要么回滚。也就是说,到了第二部就开始决定你是否要将数据更新到你的数据库里,如果要,就是commit;,如果不要,你还可以回滚,就是撤销嘛,把在内存中你更改的数据清除掉。所以说,等一下我们在演示案例的时候,我们就要指定一个事务的开始和关闭,关闭就是我刚刚说的要么提交要么回滚,这点要注意,说白了,就是你一旦提交或回滚就表示你结束了当前事务,进入下一个事务里了(一个事务的结束就会自动进入下一个事务里,所以,事务的开启我们默认可以不指定,同时,任何一条DML语句的执行,都表示事务的开启),不知道你们能不能明白。
  好,很明显,上面的更改语句是没问题的,也确实更新到数据库里了,是不是也说明了它到了第二部的时候执行了commit;操作了呀,那有人问了,我怎么没看到commit;,它在哪?诶,其实它是自动帮我们提交的,自动?我怎么知道它是自动提交的呢?请看如下语句:
在这里插入图片描述  ON代表开启,所以,它是自动开启提交的,但是在演示案例前我要把它关了,为什么,因为一条DML语句是不是就是一次事务,待会的案例可是有多条语句的哦,我是要把这些多条语句看成一个事务,同时为了不干扰最终的效果,我是不是必须把它的自动提交给关了呀,自动不行,就手动呗,那好,如何关闭?如下:
在这里插入图片描述  注意语句set autocommit=0指令是针对本次会话的,不是永久性的,你下一次重新打开,又是自动提交的,好,然后我们看,它是不是就改过来了,OFF代表关闭,已经禁掉了。
  既然已经禁掉了,那么们就可以学习学习如何开启事务了,指令如下:

start transaction;

或:

begin;

  上面两种方式都是开启一个事务的,默认可以不写。接下来是关闭事务,如下:

commit; #提交事务
rollback; #回滚事务

  提交和回滚我上面已经说明白是什么意思了,所以我也不再说。
  上面你既然已经禁掉了自动提交了,也知道结束一个事务要怎么结束了,就可以开始往下学习了。

6. 四种解决方案的案例演示(要在Innodb引擎下去玩事务)

1. read uncommitted:
  在演示前,先查看当前是哪一种解决方案,看看是不是repeatable read,因为我前面说过,对于mysql而言它是默认的解决方案,怎么看?如下:
在这里插入图片描述  那么我们怎么改它的隔离级别呢?改为read uncommitted,如下:
在这里插入图片描述  sql语句如下:

set session transaction isolation level read uncommitted;

  为了演示两个事务并发运行,我们再新建一个会话,这个刚刚新建好的会话你也要保证自动提交是禁掉的,还有隔离级别也是一样的,这样好演示,然后我们看看两个会话它们的账户表如下:
在这里插入图片描述  ok,我们可以学习第一个隔离级别了,read uncommitted,通过上面那张表,我们可以知道读未提交是什么都不能避免的,而且通过它的字面意思可以知道,它是可以读取对方未提交的数据的,如下:
在这里插入图片描述  对于幻读,把左边这个更新语句update改为insert即可,就是幻读。
2. read committed:
  进行下一个隔离级别的演示,注意两个会话都要提交或者回滚表示当前事务结束,进入下一个事务,同时双方都要把隔离级别改为read committed,如下:
在这里插入图片描述  开始案例演示,如下:
在这里插入图片描述
3. repeatable read:
  还是别忘了提交结束上次事务,因为我们要进入下一个案例演示了。这次演示的是可重复读,所以要记得把双方的隔离级别改过来。我就不把代码贴出来了,毕竟我上面也贴过一次了。
在这里插入图片描述  那么它为什么不能解决幻读呢?如下:
在这里插入图片描述
4. serializable:
  在serializable的级别下,事务A和事务B只能排队执行,所以说是串行化的,如下:
在这里插入图片描述

7. 回滚点

表示回滚到某一个阶段,如下:
在这里插入图片描述也就是说,它最后并没有把id为7的记录删除,为什么?因为它执行了rollback to a指令,回滚到了a这个节点,那么本来id为7的记录就死而复生。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值