@TOC


事务

1. 概念

事务指逻辑上的一组操作,组成这组操作的各个单元,要么全部成功,要么全部失败

在不同的环境中,都可以有事务。对应在数据库中,就是数据库事务。

事务就是把若干个SQL的操作打包成了一个整体,实际执行的时候,如果这个整体执行了一半,就遇到了突发情况,MySQL就能保证突发情况恢复之后,数据库的数据没有受到破坏.

​ 不是说一定能保证所有的操作都能一口气执行完,实际上是通过"回滚"(Roll Back)机制来对数据进行还原.MySQL中有一个机制 binlog,记录了每一个MySQL的具体操作,就可以依据这个历史记录,把执行一半的事务给还原回去.

举例:

在引入事务的概念之前,我们先来设想一下这样的场景,假如数据库中存有两个人,A和B,他们的账户余额分别为1000和800,现在A给B转账50元.

转账这个操作,可以分为两步:

① 先把A的余额减50

② 再把B的余额加50

本来转的好好的,但是忽然出现了断电/断网/程序崩溃/主机重启等等情况,俗话说,技术再牛逼,也抵不过挖掘机的一铲子啊

此时只执行了 ①没有执行② 这个时候该怎么办呢?

为了解决上面的问题,于是引入了事务.


2. 事务的特性(原子性,一致性,持久性,隔离性)

1.原子性

​ 举个栗子:

创建一个学生表,同时往里插入一些记录,此处在业务中认为创建学生表并插入10个记录,这是一件事,就可以通过事务把多个SQL打包成一个.

​ 如果这其中的代码执行了一半,Java程序崩溃了,此时就相当于事务没有完全执行完,MySQL就会自动把前面已经执行了的操作进行还原,还原到事务执行之前的模样.

这么做的目的就是为了保证这些SQL是一个整体,要么全都执行,要么全都不执行,不能存在执行一半,剩一半没执行的中间状态.

2. 一致性

保证数据库的数据在执行事务之前和之后,都是合理的.

​ 合理性通过人工约定的,比如可以通过约束实现.假如A只有100块,但是要给B转账200,这个时候就是不合理的,就会回滚到转账之前的状态.

3. 持久性

一旦事务执行成功,此时这样的数据就是持久保存在磁盘上,就算重启主机,也不会改变.

4. 隔离性

考虑多个事务并发执行的时候,多线程.

​ MySQL也是支持并发的,可以有多个客户端同时来执行一些SQL,如果是普通的SQL,MySQL自身可以保证并发执行的结果是对的.如果是多个事务(包含多个SQL),此时并发执行事务,就可能出现一些问题,我们下面就来介绍具体都会出现哪些问题.


3. 并发执行事务可能遇到的问题

3.1 脏读

比如说:我们此时坐在同一个考场进行笔试。我们在写一到编程题。
当我们写代码的过程中,我们创建一个表,参数(…)。
就在我们写的时候,有个人偷偷在瞄我们的代码。
他就看到我们是怎么去创建这个表的。然后他就咔咔飞速的写起来。
但是呢!我们觉得这个表不合适,把它擦掉改了。
等交的时候,他一看我们的代码怎么改了??
然后他就GG了。

image-20220503144920427


也就是说这个人,他瞄到的数据 不是我们的最终结果,而是我们中间过程的数据
这个中间过程的数据是能会被改的。

此时的情况,就是脏读
放在数据库中:事务A在对某个数据进行修改的同时,事务B 去 读取了 这个 数据。
此时,事务B 读到的很可能是一个“脏数据”(这个数据是一个临时的结果,而不是最终的结果)
再举一个例子:测体温,填体温表。
我们测完体温之后,去填体温表。
不知道哪根筋断了,把 36.7, 写成了 46.7。
此时被后方的同志看到了,立马就去打报告了。
等我们将体温改回来的时候。
发现此时,我们被人围住。。。
结果可想而知,不管你是不是真的填错了,都要抓你去隔离了。**

此时的 46.7 就是一个“脏数据”


脏读问题 的处理方法

就是给 写操作 加上锁。
意思就是 在 写的过程中,别人看不到的。(加锁的状态)
写完之后,别人看到了。(解除枷锁)


举个例子:

还是前面的例子,只不过改一下。笔试偷窥不太好。
假设 我们和自己的朋友一起学习代码,遇到他们一道题他们不会,想借鉴一下我们的代码思维。
但是我们和朋友不要急,等我写完,你们再来看。
免得你们看的都是错误的,
现在我们在写代码的时候,就不会有人回过来看。
等我们写完了,才有人来看我们代码。
此时我们就避免了脏读的发生。别人看到的是最终结果,而不是中间数据。

放在数据库中,事务A 在 访问 一个数据的时候,将这个数据上锁,一旦上锁之后,其它事务就不能访问这个数据了,意味着事务之间的隔离性提高了。
并发发生的概率就降低了。【简称并发性降低】


疑问:当我们给数据上锁了,那么我们的事务就没有问题了吗?

答案:不是的!


3.2 不可重复读

image-20220503145401550


不可重复读问题 的处理方法

有了脏读的处理经验,不可重复读的问题也就很好处理。
即:当读者进行阅读博客的时候,身为作者的我们不能改动博客。
反过来说:当读者看完了我们的博客,我们才能对其进行改动。
综合来讲:给 读操作也上锁。
也就是说:现在的 情况就是 我们写博客的时候,读者不能阅读;读者阅读的时候,我们不能改博客。
意味着我们必须得等到读者读完了,才能进行修改。
因此通过给读操作也加上锁,就可以解决不可重复读的问题。

image-20220503145523776

小结

并发性 与 隔离性。

简单来说:并发就是多个事务一起运行。
          隔离就是 处理完一个事务后,再处理它的事务(就是一个个来)。


3.3 幻读

image-20220503145656122

幻读问题的解决办法

想要解决幻读问题的办法至于一个,彻底 串行化 执行。

也就是说:读者在读我们的博客的时候,叫我们摸鱼。不要写任何博客了!让我们来卷一卷。
简单来说,就是一个个来,出完一个事务,再处理一下事务,以此类推。

(期间不能做任何其他的事务操作。)

这种执行方式,隔离性最高,并发性最低,数据最可靠,速度最慢。

隔离性 总结

以上我所讲的这些,就是关于隔离性相关问题。
并发执行的速度很快;隔离执行的数据最为准确,但是两者是不可以兼得的。
需要根据实际情况进行调整隔离的级别。
通过不同的隔离级别,就控制了事务之间的隔离性,也就是控制了并发程度。
从而在 快与 准之间,找到一个平衡点。


MySQL中事务的隔离级别

1、read uncommitted :允许读取未提交的数据,并发程度最高,隔离最低,

会带有脏读 + 不可重复读 + 幻读问题

2、read committed:只允许读取 提交之后的数据,相当于写加锁。并发性降低,隔离性提高。

解决脏读,但带有 不可重复读 + 幻读问题

3、repeatable read:相当于给读和写操作都上锁了,并发性进一步降低,隔离性进一步提高。

解决脏读、不可重复读,但带有 幻读问题。

4、serializable串行化,并发性降到最低(串行执行),隔离程度最高。

解决了脏读、不可重复读、幻读问题。但是运行的速度是最低的。

MySQL 可以通过修改 配置文件(my.ini) 来进行设置当前的隔离级别。

这样就可以根据实际情况,来决定使用哪种隔离级别。