1.事务是干啥的?(原子性切入)
2.事务的特性有啥?
3.事务的隔离性的理解
4.MySQL的隔离级别,如何针对选择对应的隔离级别
事务,它能把多个要执行的事整合成一件事,比如说你打算进行 洗澡吃饭学习 三个操作,事务就能打包好这三个操作,让它变成一件事,让你雷打不动的去完成这件事
万一你洗好澡,吃好饭,准备学习,但是你吃完饭后就跑去打游戏了,这打破了你学习的计划!
所以你(洗澡吃饭学习)这个任务就失败了
再比如说,七夕了,你打算去约会,计划就是,你给你的女神发信息邀请,然后去带女生去吃饭,接着带女生去看电影
假如女生说不,她七夕有约,那你就不用去做你计划的事了,又比如说女生同意你的邀请,然后你带她去吃饭了,结果她说她有事要走了,所以你带她看电影的计划就落空了~
这就引发出了事务的原子性,原子在以前人们刚发现的时候,被认为是不可分割的,是最小单位,
事务也是一样,把所有计划化多为一,要么你全部执行,要么就一个都不要执行,原子性就是保证全部都执行的~ 事务就是用来保证原子性的,确保你能完整的 洗澡吃饭学习 , 而不是在准备执学习的时候,被游戏打断, 确保你能 完整的和女神约会,而不是在约会过程中出现什么岔子,所以说,要不就全部一起执行,不被干扰,要不就一个都不执行,之后再说~
数据库里面,也有一些操作,希望能够按照“原子”(不可分割,不被干扰,化多为一)的方式来执行,这种情况下就可以使用 事务 来实现~ 事务是可以保证原子性的~
举个例子,比如说转账操作
给一个账户数据库,里面有用户的信息和存款数额
假如说现在张三要给李四转账500块钱,那么数据库的操作就是,让张三的存款数额减去500,让李四的存款数额加上500,这就是转账成功的一个完整过程~
如果正常情况的话,数据库执行这两个SQL是没有问题的
但是,假如张三的存款数额减去500了以后,服务器突然挂了!!!!此时张三扣款成功,李四却没有到账!!
这时候就出岔子了~
所以我们希望上述的两个SQL是按照原子的方式来完成了,也就是把两个SQL能形象的整合为一个,假如这个“整合SQL”执行失败,那么张三也不会扣款,李四也不会到账~
所以说用原子的方式来完成,要全部都执行,要么一个都不执行(这就是形象的整合)但这里不是说真的没有执行张三的扣款操作,而是在执行到一半,也就是张三扣款了,还没给李四到账的时候,就出现了问题,此时就能自动恢复到最开始的状态 ——》张三没扣款,李四也没到账~
如果用事务就能解决这个问题,事务可以将扣款到账两个操作打包成一个操作,当执行过程中出现了问题的时候就能自动把前面的SQL执行的效果,回复如初~~(回滚 rollback)
用回滚操作就可以了,当执行到一半的时候,出现问题,就回滚到原来的状态,这时候虽然已经执行了张三的扣款操作,但是回滚到最初了,也就相当于没有扣款~
回滚的机制就相当于,你打原神的秘境的时候,你被打死了,然后又从出生点复活了,一个道理~
(在事务执行的过程中,MySQL会记录每一步都执行了啥,一旦出现了问题,就可以根据记录来回滚~~~)
事务的特性
事务最核心的就是 原子性~
当然,事务除了 原子性以外,还有另外三个特性~
(1)原子性:事务的根本所在(事务存在的意义)。能够把多个SQL打包成一个整体,要么都执行完,要么一个都不执行,(如果执行过程出现问题,则自动回滚~)
(2)一致性:事务执行前后,数据处在“一致”的状态(数据对的上,合情合理)
(3)持久性:事务进行的改动,都是写到硬盘里面的,不会随着程序或者主机的重启而丢失
(4)隔离性:(最复杂)多个事务,并发执行的时候,事务中间能保持“隔离”,不互相干扰~~
介绍过原子性,下面重点来说说事务的隔离性~
隔离性存在的意义,就是让并发执行事务的过程中,尽量不出问题(问题在可控范围内)
MySQL的本体就是一个服务器,执行SQL语句,本质也是客户端把SQL发给服务器,让服务器去执行~
事务也是一样发给服务器来执行
假如同一个时刻,多个客户端都给服务器法送事务,那么服务器此时就会 “并发执行” (简单理解为同时做多件事)
如果这些并发执行的事务,是不同的数据库或者是不同的表,那还好说,但是如果都是相同的数据库相同的表,那此时就会产生矛盾了……
接下来我们想象一些场景,来看看所产生的矛盾~
# 假设服务器在执行两个操作,一个是写数据操作,一个是读数据操作,假设小菠萝在写数据,假设小香蕉在读数据 #
(1)脏读
当小菠萝在写数据的时候,小香蕉就把小菠萝写的数据给读走了,小香蕉读取了数据之后,小菠萝把数据改了!!那么此时小香蕉就拿到了一个“脏”数据,也就是无效的数据,因为有效的数据以小菠萝最终写的数据为主
这就好比,你想抄学霸的卷子,学霸故意把错的答案写在卷子上等你抄完,抄完之后学霸再偷偷把正确答案给改了,那么你的答案都是错的!!!
这个在写的时候就读数据,万一读完后数据改了,你读到的就是无效数据了,这一个过程就是脏读~~~
那么我们怎么解决脏读问题?
解决办法:小菠萝和小香蕉做好一个约定,在小菠萝写数据的时候,小香蕉不许读数据,必须等到小菠萝把写数据操作进行完后,小香蕉才能去读数据~~~
这就相当于对 写操作,进行加锁!
在没有写加锁之前,小菠萝的写数据,和小香蕉的读数据是完全并发的,此时并发性最高,隔离性最低!!
当写加锁之后,小菠萝写数据的时候,小香蕉不能去读,此时并发性降低,隔离性提高了(准确性提高了~~)
(2)不可重复读
我们为了避免脏读,进行了写加锁操作~
小菠萝和小香蕉也约定好,写的时候不能读(仅此而已)
小菠萝把数据写完后,喊:小香蕉,你来进行读操作吧~
这时候小香蕉屁颠屁颠的跑去读数据了~
在小香蕉读数据的时候,小菠萝突然灵光一动!又有了新写法,然后小菠萝也屁颠屁颠的去修改数据了!!!注意:此时小香蕉还在读数据,小菠萝就去改数据了!
这时候小香蕉读着读着代码突然就变了!!
这就造成了不可重复读~
原因是,虽然规定了,写的时候不能读,但没规定你读的时候我不能写啊!
所以不可重复读:在一个事务A中,多次读取同一个数据,发现不一样!!(读的时候被改了)
那么我们怎么解决不可重复读问题?
解决办法:使用读加锁~
我们已经约定了写的时候不能读,现在我们再来一个读加锁,规定读的时候,不能写!
因此小菠萝和小香蕉再次约定:小香蕉在读数据的时候,小菠萝不能突然去改动数据~
随着引入读加锁,并发程度又进一步降低了(效率降低了),隔离性又提高了(数据准确性又提高了~)
(3)幻读
已经约定了读的时候不能写,写的时候不能读了!
此时小菠萝又想了想:你读你的呗,我去改别的数据总可以了吧?你读的数据我不动,我去动其他的数据,这样对小香蕉来说,并不会出现脏读和不可重复读~
当小香蕉读第一份数据的时候,小菠萝不会去动第一份数据,小菠萝而是去写第二份数据!
虽然小香蕉第一次读数据的时候没觉得有什么变化,但当它再次去读数据的时候,虽然第一份数据没有变动,但是多出来了一份数据,也就是结果集变了!
(小香蕉第一次看是一个.java文件,第二次再看就变成了两个.java文件了)
这种情况称为 “幻读”
幻读可以理解为是 不可重复读 的 特殊情况
为了解决幻读,方案是串行化~
只要小香蕉在读数据的时候,小菠萝就动都不许动,无论小香蕉要读多少次,小菠萝都要等小香蕉确定不读了,才去写数据~
此时,并发程度最低(串行执行,效率最低),但此时的隔离性最高,准确性最高~~
上述的 脏读,不可重复读,幻读 都是在并发执行的事务中可能带来的影响~
但这些影响不一定就是BUG,是根据场景来的,或者是产品经理的要求来
比如说转账这中操作,就必须要非常的准确,不能出现一点问题
对于什么视频点赞收藏转发啊啥的,没有比钱重要,也就不一定说必须要非常的准确~
因此,MySQL给我们提供了 “隔离级别” 选项,让我们根据实际需求来选择不同的隔离挡位~
根据情况来选择挡位,来平衡效率和准确性~
好,长文结束!