面试官:谈谈你对MySQL事务的理解
目录
1、什么是事务?
2、为什么需要事务?
3、事务的四大特性(重点)
3.1 原子性
3.2 一致性
3.3 持久性
3.4 隔离性(难点)
3.4.1 脏读数据
3.4.2 不可重复读
3.4.3 幻读问题
3.4.4 MySQL 提供的四个隔离级别
1、什么是事务?
事务指逻辑上的一组操作,组成这组操作的各个单元,要么全部成功,要么全部失败,在不同的环境中,都可以有事务。对应数据库中,就是数据库事务!
简单来说:事务是属于能够把多个 SQL 给打包到一起的,变成一个整体。
这里举一个很简单的例子,篮球哥总在学校操场看到有小伙子跟妹子表白,大概就是地上铺上花瓣,摆好爱心蜡烛,买上一束美丽的鲜花,最后跟妹子深情表白。
那么请问,这个小伙子最终的目的是啥?当然是跟妹子谈恋爱!!!
于是铺鲜花,摆爱心蜡烛,送鲜花,以及深情表白,最终的目的就是希望妹子成为他女朋友,当小伙子鲜花铺好了,蜡烛摆好了,送鲜花给妹子的时候,妹子不要,转身就走!
那么这种情况下,如果送花失败了,前面的铺垫都没任何意义,更别说后面深情表白了,简直说话机会都不给呀!
所以也就相当于,上述铺鲜花,摆蜡烛,送鲜花,深情告白,这几个步骤缺一不可!也就对应俩结果,要么成功,要么失败!事务也是大概的逻辑,要么操作成功,要么操作失败,绝不可能搞一半!
2、为什么需要事务?
张三是个标准男友,今天是情人节,于是跟往常情人节一样,定个 5:20 的闹钟,准时起床给小美发个 1314 的大红包!可是意外发生了!!!
张三给小美转账 1314,假设在底层数据库中执行下述代码:
update accout set money = money - 1314 where name = '张三';
update accout set money = money + 1314 where name = '小美';
上述代码很容易理解,就是两条 sql,分别是使张三的账户减少 1314 元,小美的账户增加 1314 元,可是在执行完第一条 sql 后,对应数据库服务器挂了,或者网络出错了,悲剧发生了!
由于网络故障,张三的钱会减少 1314,而小美钱却没有增加!(没有收到转账)
上述这种情况如何解决?这时就可以使用事务来控制了,保证以上两条 sql 要么全部执行成功,要么全部执行失败!不能执行到一半就不执行了!
全部执行失败是啥意思?执行了一条,再让我全部执行失败?那第一条不是已经执行了吗?
注意:这里确实已经执行了第一条,全部执行失败,是指的会恢复成执行之前的样子,看起来就好像一个都没执行一样,这涉及到一个关键操作 "回滚",把执行过的操作逆向恢复回去,类似于我们常用的 ctrl + z
于是就可以通过事务把这两条 sql 打包成一个整体,这个操作就称为 "原子性",同时 "原子性" 也是事务最最核心的特性,后续多线程章节加锁也会涉及到相关知识!
原子性:物质由分子构成,分子由原子构成,原子之下?还可以再分吗?虽然现在研究出来了原子还可再分,毕竟当时的人们是认为原子是不可再分的,觉得原子就是世界最小的单位了!因此有很多地方就使用 "原子" 来指代不可再切分的最小操作,事务的原子性也是同理!
解决方案:
注意下述操作,中间的这些 sql 不会立即就执行,而是先攒着,等 commit 再统一执行!
start transaction;
update accout set money = money - 1314 where name = '张三';
update accout set money = money + 1314 where name = '小美';
commit;
- 开启事务:start transaction;
- 中间放置要执行的 sql
- 回滚或提交:rollback/commit;
注意:rollback 表示全部失败,commit 表示全部成功!
3、事务的四大特性(重点)
3.1 原子性
原子,不可再分割了,放在一段代码里看,这段代码,要不全部执行成功,要不就全部执行失败,绝不可能出现只执行几行就不执行了!有着 "同生共死" 的感觉,一旦原子操作开始了,就不会受其他线程的印象(学完多线程,共容易理解),原子性是事务的初心!
3.2 一致性
事务执行前或都执行后,都得是数据合法的状态,如果像刚才转账,如果因为过程出错,导致钱丢了的情况!(一致性也是一来原子性来实现的)
3.3 持久性
内存是持久的吗?并不是,关机内存上的数据就会丢失,持久是体现在硬盘上的,硬盘上的数据是持久存在的,而数据库的数据是存储在硬盘上,事务产生的修改,都是会写入硬盘的,即使服务器重启了,要不进行回滚,要不就保证修改成功!
3.4 隔离性(难点)
隔离从字面上理解,就是隔离开来,确实也是类似的效果,后续内容也是围绕隔离性展开的。
MySQL 服务器,要同时给多个客户端提供服务的,可能会有多个客户端同时发起事务,尤其是多个事务在操作同一个数据库的同一个表的时候,可能会引起一些麻烦(了解了多线程后,更容易理解这个点)
MySQL 给我们提供了4个档位的隔离,要理解这几个隔离的效果,先需要理解下面几个特性的含义:
脏读数据,不可重复读,幻读
当把上述三点理解清楚概念,再看来四个隔离级别,就会更好的理解。
3.4.1 脏读数据
一天下课的时候,张三拿起一页空白的纸,在上面写道:我喜欢小明....刚把 "明" 这个字写完,小王突然走到张三旁边,一看,震惊的捂着嘴巴赶紧跑开了!张三还没注意呢,接着写完后面的字:" 我喜欢小明的铅笔盒,你可以给我也买一个吗?",写完后就把纸递给他女朋友小花...
此时就可以把张三想象成事务A,正在写数据,把小王想象成事务B,正在读数据呢!
此时的小王,就把这个读到的数据:"我喜欢小明",在班上传开了,张三也百口难辨啊...
这种情况就称为 "脏读" 问题,也叫做 "脏读数据",不是说这个数据多不好,而是说这个数据是一个有问题的数据,不是一个最终的数据,或是读到了只写了一半的数据,可想而知,脏读是多么危险的一件事!
在上述场景中,张三和小王是俩事务,是完全并发的,没有任何限制,这种前提下,就有可能会出现脏读问题!如何解决脏读问题呢?可以降低并发性,提高隔离性,具体来说就是 "写加锁"。
什么是写加锁?就是当张三在写纸条的时候,搞一把伞,把自己全部遮住,不让别人看,等张三写完了:" 我喜欢小明的铅笔盒,你可以给我也买一个吗?",写完了之后,才能给别人看!
这就相当于对写操作进行了加锁,张三写数据的时候,别人就不能去读数据了,相当于降低了并发程度,提高了隔离性,但降低了一定的效率(小王要等张三写完才能去读),同时也提高了准确性!
3.4.2 不可重复读
此时张三已经警惕了不少,已经针对写加锁了(写数据的时候,别人不能读我写的数据)。
假设有这样一种情况,张三写完纸条后,跟小王说,你可以看了,小王于是看到了张三写的纸条了,但是张三一惊,坏了,我怎么能让我女朋友给我买铅笔盒呢?肯定是我给她买呀!
于是在小王看纸条数据的时候,张三当着小王的面给擦了,改成了:" 我喜欢小明的铅笔盒,我也给你买一个吧 ",这是小王很纳闷,我两次读到的数据都不一样啊?哪个才是正确的呢?这一下把小王给搞糊涂了...
在一个事务中,连读两次读到的数据,结果不一致,这就是不可重复读!
如何解决呢?给读操作也加锁,就好比小王看纸条的时候拿在手上看,不让张三修改内容了!
那么这样一来,两个事务之间的并发程度又降低了,隔离性又进一步提高了,运行速度又变慢了,数据的准确性也提高了!(至少小王不会弄混淆,将错就错吧)
3.4.3 幻读问题
当前上述已经针对了读和写加锁,张三写数据的时候,小王不能读,小王读数据的时候张三不能写(针对同一张纸 -> 文件)。
假设张三看到小王在读他写的纸条了,这时候他也不能对纸条增加内容,于是又拿了一张纸条,写上:" 小美,我爱你!",此时读纸条的小王虽然纸条的内容没有改变,但是咋又多了一张呢?
放在计算机文件来说,此时张三可以删除或增加其他文件,只是不能对小王正在操作的文件进行其他操作。
这个问题就称为幻读问题,在同一个事务中,两次读到的结果集不同,称为幻读
要想解决幻读,只能串行化,彻底舍弃并发!只要小王在读纸条,我只能坐着不动。
对于什么是并发,什么是串行,后续多线程会讲解,此处想了解的小伙伴可以查阅其他资料。
3.4.4 MySQL 提供的四个隔离级别
● read uncommitted
这个级别不做任何限制,事务之间都是随意执行的,并发程度最高,隔离性最低,会产生脏读+不可重复度+幻读的问题
● read committed
对写操作加锁了,并发程度降低了,隔离性提高了,解决的脏读的问题,但可能存在不可重复读+幻读的问题
● repeatable read (默认)
对写和读都加锁了,并发程度又降低了,隔离性又提高了,解决了脏读+不可重复度的问题,但可能会出现幻读问题
● serializable
严格串行化,并发成都最低(串行执行),隔离性最高,解决了脏读+不可重复读+幻读问题,执行速度也相对上面更慢。
总结:
隔离性越高,意味着事务的并发程度越低,执行效率是越慢,但数据的准确性是越高的(跟钱有关的业务)
隔离性越低,意味着事务的并发程度越搞,执行效率是越快 ,但数据的准确性是越低的(文章浏览量,某音点赞数)
开发中,就可以根据需求场景,来确定使用哪个隔离级别,可以通过 mysql 配置文件来进行调整
下期预告:【MySQL】JDBC 编程