一、事务是什么东西
有些场景中,某个操作需要多个sql配合完成:
例如:
李四这个月剩下的前不够交房租了,找张三借1000元急用:
(1)给张三的账户余额 减去1000元
updata 账户表 set money = money - 1000 where id = 2;
(2)给李四的账户余额 加1000元
updata 账户表 set money = money + 1000 where id = 1;
试想一下:
如果执行完第一条语句,执行第二条语句之前,出现了严重的问题了(程序破溃,服务器断电了),那不就完了吗,这谁还敢把钱存你这里
所以就引入事务:
所谓事务,就相当于把要执行的多个SQL语句打包成一个整体,这个“整体”在执行过程中就能够做到要么整个都指向完,要么一个都不执行,就可以做到避免上述例子的情况。
但是此处的“一个都不执行”不是SQL语句真的不执行,而是执行到一般如果出错了,数据库会自动进行“还原操作”,相当于把之前的SQL语句进行“撤销”,最终的效果看起来就像是一个都没有执行这样的效果。
我们把这样的机制称为“回滚(rollback)”,同时也把上述的特性称为“原子性”。
之前人们以为“原子”就是不可拆分的最小单位了,我们计算机这边就引入这样的名字
二、数据库怎么知道如何回滚?
不知道大家有没有跟我一样好奇,服务器都断电了,数据库咋知道如何进行“回滚”呢?数据库咋知道之前做出了什么样的修改?
数据库内部存在一系列的“日志体系”,记录到“文件中”,因为是保存到外存上的,既可以应对“程序崩溃”、就连“服务器掉电”也是可以做到回滚的。虽然掉电了,但是回滚日志还是存在的,下次数据库启动的时候就可以根据回滚日志的内容,进行回滚操作了。
那么drop database这样的操作能回滚回来吗?
答案是不能的,因为这样的操作不能放到事务中去执行,并且这也不算是执行出错,算是“正确执行的SQL语句”
三、语法
- 开启事务:start transaction;
- 执行多条SQL语句;
- 回滚或提交:rollback/commit;
注意:rollback代表SQL语句操作操作全部失败,commit即是全部成功。
当我们有这样一张账户表:
李四这个月买了新手机,但是发现房租还没有交,只好找好哥们张三借1000元急用。
结果:
如果是rollback结尾:
结果:
并没有任何变化
开启事务后执行:
开启事务后不执行:
四、事务的四个特性
1.原子性
原子性就是刚刚一直将的内容
2.一致性
描述的是,事务执行前和执行后,数据库中的数据,都是“合法状态”,不会出现非法临时结果的状态。
3. 持久性
事务执行完之后,就会更改硬盘上相应的数据,事务都是会永久生效的
4.隔离性
描述了多个事务并发执行的时候,相互之间产生的影响是怎样的。
并发执行是指:
Mysql是一个“客户端 - 服务器”结构的程序
一个服务器通常都是,给多个客户端提供服务的
那么多个客户端,就同时给这个服务器提交事务来执行,与之相对的,服务器就需要同时执行多个事务,此时就是“并发”执行
并且这些事务可能会对同一张表进行增删改查,此时就可能会有一些问题:
4.1 脏读
现在有两个事务:
事务A 和 事务B
其中事务A在针对某个表的数据进行修改
A执行过程中,B去读取这个表的数据
当B读完之后,A把表里的数据又改成别的;
这就会导致,B读到的数据,就不是合法的数据,而是读到了临时性的“脏数据”
那么我们就约定,在改数据的时候,不能读,也称为“给写操作加锁”
4.2 不可重复读
有三个事务: A B C
事务A执行一个修改操作。A执行完毕,提交数据;
接下来事务B执行,事务B读取刚才提交完的数据;
在B读取的过程中,又来了一个事务C,C又对刚才A的数据进行修改;
此时对于事务B来说,后续再读取这个数据,读取的结果就和第一次读到的结果是不一样的。这个过程就叫做“不可重复读”
但是需要注意,是对于事务B来说,多次读取的结果不一样。
但是又来了个事务D读取C改变后的数据,那这个是没有问题的
那么我们就约定:
一个事务在读取数据的过程中,其他的事务不能修改它正在读的数据,给读加锁
4.3 幻读
相当于不可重复读的“特殊情况”:
现在已经预定好了,改数据的时候不能读,读数据的时候不能改
现在有两个事务: A B
有一个事务A在读取数据,读的过程中,另外一个事务B,新增了/删除了一些其他数据
此时站在A的视角,多次读取的数据内容虽然一样,但是“结果集“不同,结果集不同,是否算是问题,视情况而定
如何解决?
还是继续约定,只要有事务读取,我就不做任何操作,这样的操作称为“串行化”,什么意思?
如多个客户端,同时提交了多个事务过来,但是服务器一个一个的执行事务(执行完第一个再执行第二个……)
五、隔离级别
在Mysql中提供了四个隔离级别。可以通过配置文件设置当前服务器的隔离级别是哪个级别。
设置不同的隔离级别,就会让事务之间的并发执行产生不同的差别,从而会影响到上述的三个问题的情况
1.read uncommitted
此情况下,一个事务可以读取另一个事务未提交的数据。
此时,就可能会 产生脏读、不可重复读、幻读 三种问题
但是此时,多个事务并发执行程度是最高的,执行速度也是最快的
2.read committed
这种情况下,一个事务只能读取另一个事务提交之后的数据(给写加锁了),此时,可能会产生 不可重复读,幻读问题 (脏读问题解决了),此时,并发程度会降低,执行速度会变慢,但是,事务之间的隔离性提高了
3.repeatable read
这个情况下,相当于是给 写操作 和 读操作 都加锁了.
此时,可能产生幻读问题,解决了脏读和不可重复读问题.
并发程度进一步降低,执行速度进一步变慢,事务之间的隔离性进一步变高了
4.serializable
此时,所以的事务都是在服务器上一个个执行的。
此时,解决了脏读、不可重复的、幻读问题
并发程度最低,执行速度最慢,隔离性最高,数据库最准确
那么就根据具体场景选择隔离级别, 对于钱等数据重要的场合就选择serializable,对速度要求高的,就根据需求选择更快的