目录
1.为啥引入
2.是啥
3.如何回滚(日志)
🔥4.面试题:谈谈事务的基本特性
(1)原子性
(2)一致性(收入和支出相匹配)
(3)持久性
🔥(4)隔离性
脏读问题
是什么?
如何解决?
不可重复读问题
是什么?
图解编辑
如何解决?
幻读问题
是什么?
如何解决?
1.为啥引入
多个SQL配合,出现特殊情况,前面执行了,后面没执行
日常开发中,有很操作,不是通过一个SQL就能完成的,往往需要多个SQL配合完成,当执行多个SQL操作的时候,如果中间出现了特殊的情况(程序崩溃,系统崩溃,网络断开,主机掉电了...),就可能会出现前面SQL执行成功,后面的SQL执行失败了
举例(具体场景):
库存表(id,count)
订单表(orderId,userId,goodId...)
进行下单操作,触发库表数据-1 && 订单表新增记录
经常会涉及到通过多个SQL配合,完成某个操作的
这个时候就需要使用到事务,来确保上述的操作,是可靠的,完整的
2.是啥
事务,就是把多个操作,打包成一个整体,就能够保证,这个整体要么都执行成功,要么就一个都不执行,有效避免部分执行,部分未执行,产生的一些”中间状态引起的问题“
原子性:把多个操作,打包成一个整体
Q1:什么叫”一个都不执行“的理解
A1:这里并不是真的没执行,事务中的若干个SQL必然是要一条一条执行的,事务能够保证,当执行到某一条的时候如果出现问题了(程序崩溃,系统崩溃,网络断开,主机掉电了...),数据库就能够自动的把前面SQL操作的影响,给恢复回去,看起来就好像一条SQL都没执行的样子
其实这里翻新的操作,成为”回滚“
数据库事务的原子性,核心就是通过”回滚“机制来保证的
3.如何回滚(日志)
为了实现回滚机制,数据库会在执行事务的时候,记录日志,数据库的日志是写入到硬盘的文件中
当事务最终都执行完毕,中间没有差错,这些记录的内容就可以不要了
但是如果执行事务的过程中,出现了问题,MySQL就可以根据日志中记录的内容,来进行恢复操作
1)之前进行了新增操作,就把数据删掉
2)之前进行了删除操作,就把数据新增上来
3)之前进行了修改操作,就把数据改回去
4)之前是查询操作,不影响,不需要任何恢复行为
只要数据库处于正常工作状态,就能够始终保证事务前面进行的操作被正确回滚(关系型数据库一般都能够支持这一点)
eg:
程序崩溃/网络断开:程序崩溃,但数据库是正常的,就直接进行回滚操作即可
系统崩溃/主机掉电了:这种情况MySQL服务器也挂了,那么无法立即回滚,过一段时间程序员把环境进行恢复的时候,数据库能重新启动的时候,就能够发现上次有个事务出问题了,按照前面记录的日志内容进行回滚
🔥4.面试题:谈谈事务的基本特性
(1)原子性
有回滚机制,能够触发还原
(2)一致性(收入和支出相匹配)
执行事务之前,和执行事务完毕之后,数据是一致的(不会出现这种”对不上“的情况)
也是和回滚有关的,一旦触发回滚了,回滚回去的数据得是对的;如果顺利执行,没有触发回滚,数据也是符合要求的
这个特性其实是对:”数据正确“的承诺
(3)持久性
执行事务对数据库产生的修改,就会在硬盘上持久保存,重启后仍然存在
但凡见到“持久性”就要联想到一件事:把数据存储在硬盘上
此处的持久,程序重启/主机重启。数据仍然能存在,但是如果存储在内存中,就不是持久的,内存中的数据会随着程序重启/主机重启而消息
Q1:数据不丢失和持久性一样吗
不丢失 = 持久性 + 一致性
🔥(4)隔离性
1)为何会有这个特性
如果多个客户端正好把事务赶到一块了,就需要数据库服务器都能给出处理,更糟糕的是,如果这多个事务都尝试操作同一个表,情况就会更复杂(并发处理的,这样速度比一个一个处理速度快)
2)如果一起同时处理,可能出现的问题
脏读问题
是什么?
数据库中,如果有事务A和事务B,事务A针对某个表做出了一些修改,在事务A提交之前(也就是commit告诉数据库服务器,事务完毕之前),事务B就对这里的数据进行了读取,最终就可能出现A后续操作又把上述数据进行了修改,导致最终B读到的数据和A提交的数据,是不同的
即事务B读到的是一个“临时数据”(也就是脏数据),临时数据可能会随时被修改掉
如何解决?
针对"写操作“加锁,A这边写的时候,B不能读,A写完了,B才能读
未加锁之前:执行A的同时,也能执行B(同时执行)
加锁后:执行A的过程中,B就不能执行了,要等待
这就相当于降低了”并发能力“,也就会降低数据库服务器的处理效率,提高了”隔离性“,也提高了数据的准确性
不可重复读问题
是什么?
存在三个事务.ABC.
事务A针对数据进行修改,提交;
接下来事务B进行读取数据(事务B这里的多个sql都要进行读操作);
在执行B的过程中又有一个事务C,又针对数据进行了修改
就会使B里面的不同读操作,读出来的结果不一样.
图解
开始读的数据和结束读的数据,存在差异
如何解决?
引入读加锁,就会使“并发程度”又进一步降低,效率也随之降低“隔离性”又进一步提高,数据的准确性也会提高.这个时候,ABC都不能并发了.
幻读问题
是什么?
事务A先修改并提交数据,事务B进行读数据.此时事务C,没有修改B读的数据,但是给对应的表进行了新增数据/删除数据等操作..导致事务B中,读到的数据集不同(已有的数据内容是一致的,数都是据的条数增加/减少)
可以视为是“不可重复读”的特殊情况
简单理解:读的数据内容不变,但是数据量多了
而不可重复读:读的数据内容变了
如何解决?
解决幻读的方式,“串行化”使所有的事务都严格的按照“一个接一个”的方式执行.完全没有并发了.此时执行效率是最低的,隔离性也是最高的,数据也是最准确的.
小结:
<1> 脏读:事务B读到了事务A中未提交的临时数据(脏数据) => 写加锁
<2> 不可重复读:事务B读的过程中,又有事务C对刚才事务A提交的数据进行了修改,使事务B内部不同的读操作读到的结果不同 =>读加锁
<3> 幻读:和不可重复读类似,事务B读的过程中,事务C没有修改数据内容,而是修改了”结果集“,导致B内不同的读操作读到的结果集合不同 => 串行化
解决上述问题过程中,要想让数据更准确,就需要牺牲一部分的并发/效率
3)MySQL事务隔离性具体怎么实现的
MySQL给程序员提供了四个隔离级别,可以在MySQL中配置文件中进行设置
<1> read uncommitted:允许读取其他事务未提交的数据
-> 脏读 + 不可重复读 + 幻读 (并发程度最高,隔离性最低)
<2> read committed:只能读取其他事务提交后的数据
-> 解决了脏读,存在不可重复读+幻读 (并发程度低,隔离性提高)
<3> repeatable read:针对读操作和写操作都加锁了
-> 解决了脏读 + 不可重复读,存在幻读 (并发程度又低,隔离性又提高)
<4> 串行化(serializable):所有的事务都是串行执行的
-> 解决了脏读 + 不可重复读 + 幻读 (并发基本没有,隔离性最高)
修改MySQL的配置文件,使MySQL服务器处于某个隔离级别中来运行
eg:
做一个和钱有关的系统,可以设置成串行化
做一个短视频点赞系统,就可以设置成读未提交,追求最大的效率