目录
3)repeatable read(可重复读)(MySQL默认情况)
一、事物的概念
什么是事务
事务就是把多个sql语句包装成一个”整体“,执行的话就一起执行!
比如说:
假设我要有个转账的操作:A账户转给B账户500块。
那么有两步:
(1)A-500,(2)B+500
这时候就需要2个sql语句,我们包装成一个整体,一起执行。
问题:之间分为2条语句依次进行不就好了吗?
答:不合适。假设我这时候突然”程序卡死“,”电脑掉电“.....等等,A都-500执行完了,已经扣钱了,但是我B却没有收到钱,这就是一个巨大的事故!!!
所以事务还有一个特点:要么一起执行,要么都不执行!
一起执行很好理解,就是多条sql语句一起执行,但是”都不执行“却有些不一样。这里的”都不执行“ 指的是:假设我执行完A-500了,这时候突然电源被拔了,已经执行完了A-500,就会发生”回滚“,撤销刚刚执行的sql语句。
什么是回滚
回滚,就是撤销刚刚执行的sql语句
那电脑怎么知道我执行了什么操作?
答:在事务开启的时候,会开启日志(这东西相当于日记,做了什么就写在上面),把你刚刚执行的sql语句都给记录上去,假设这时候”电脑掉电“什么什么的,这份日志会保存着,就会自动给你撤销掉日志中保存的sql语句。
二、事务的用法
事务用法很简单,具体用法如下:
start transaction;--开始事务
--中间插入sql语句
commit;--结束事务
比如:
start transaction;
-- 阿里巴巴账户减少2000
update accout set money=money-2000 where name = '阿里巴巴';
-- 四十大盗账户增加2000
update accout set money=money+2000 where name = '四十大盗';
commit;
事务执行程序:
(1)开启事务:start transaction;
(2)执行多条SQL语句
(3)回滚或提交:rollback/commit;
三、事务的四大核心特性
四大核心特性包括: 原子性,一致性,持久性,隔离性。
原子性
原子性是事务最核心最重要的特性,它就是把sql语句整合成一个“整体”,使得他们可以一起执行。
为什么叫原子性呢?
答:我们知道,原子是物体最小的单位,没有什么比原子更小的,比喻他们不可拆分,是一个个体。(其实现在也已经发现比原子更小的单位了,但sql是一种很老语言,或许那时候还没发现比原子还要小的单位)
一致性
一致性可以理解为”对账“,就是最后的数据一定是要跟执行的结果是一样的。
比如说:A的账户有1000RMB,B的账户有500RMB。
若A给B转账500,所以A-500=500,B+500=1000;你要确保最后A账户有500,B账户1000。多一分少一分都不行,这就是一致性。
持久性
这个好理解,顾名思义:执行后的结果会一直保留。
比如:还是转账,A转后剩500,B有1000,这个结果会变成数据,存在并且持久,直到下一次的修改。
隔离性
指的是多个事务 并发执行 的时候,相互之间产生的影响是怎样的
什么是并发执行?
答:因为我们sql是一个“客户端——服务器”结构的程序。所以肯定不止有一个客户,服务器需要给多个客户提供服务,如果这时候同时执行多个事务,那就叫做并发执行。比如:你和你朋友同时去餐馆吃饭,他要茄子煲,你要酸菜鱼,这时候后厨可能会一起给你做了。
假设这时候,我们并发执行事务的时候,恰好的对于同一个表,可能就会有一些问题:
1)脏读
事务阅读到的数据可能是“脏数据”(过期,过时的数据)
比如说:
假设有两个事务A和事务B,它们都在对数据库中的同一条记录进行操作。
事务A开始:
事务A修改了一条记录,比如将某个员工的工资从3000元改为4000元,但此时事务A尚未提交这个修改。
事务B读取:
在事务A提交修改之前,事务B读取了同一条记录。由于隔离级别是“读未提交”,事务B能够读取到事务A尚未提交的修改,即看到员工的工资为4000元。
事务A回滚:
随后,由于某种原因(如发现修改错误或需要撤销操作),事务A决定回滚其所有修改。这意味着事务A之前对工资所做的修改(从3000元改为4000元)将被撤销,工资将恢复到3000元。
事务B的“脏数据”:
然而,对于事务B来说,它之前已经读取到了工资为4000元的“新”数据。由于事务A的回滚,这个数据实际上是一个永远不会成为最终状态的数据,即“脏数据”。
怎么避免这个问题呢?
答:给写的操作加锁,也就是事务A在修改的时候,事务B不能查看。
2)不可重复读
是指同一个事务内多次读取同一条数据时得到的结果不一样
比如说:
事务A开始:
事务A修改了一条记录,比如将某个员工的工资从3000元改为4000元,此时事务A提交这个修改。
事务B读取:
在事务A提交修改之前,事务B读取了同一条记录。由于隔离级别是“读已提交”,事务B能够读取到事务A交的修改,即看到员工的工资为4000元。
事务A再去修改数据
随后,由于某种原因事务A决定修改数据。这意味着事务A之前对工资所做的修改(从3000元改为4000元)数据又被改回去了3000元
事务B的再次进行读取:
然而,对于事务B来说,它之前已经读取到了工资为4000元的“新”数据。由于事务A的修改,这个数据实际上是一个不在存储的数据,即旧数据。再打个比方:你刚冲上了话费,结果事务B读到的是你之前没冲话费的数据,让你再去充值,你受得了吗?
不可重复读是对于同一个事务内多次读取同一条数据时得到的结果不一样,假设是不同事务多次读取同一条数据时得到的结果不一样,那么这就是正常的~~
怎么避免这个问题呢?
答:给读的操作加锁,也就是事务B在读取的时候,其他事务不能修改。
脏读与不可重复读的区别:
脏读,对B来说,读到的不是我最终提交的数据, 不可重复读,对B来说,读到的不是我提交过后又修改之后的数据
3)幻读
指的是事务查询到的结果集不同。(结果集就是一个数据库里面的表的总和)
比如说:
我已经禁止了脏读和不可重复读(我读或者写的时候不能修改或阅读) ,这时候我正在查询student这个表,这时候别的事物突然创建/删除了teacher这个表。我看到的,虽然数据内容不变,但是多了或者少了一个表,这就是幻读。(这算不算问题,也得视情况而定)
怎么避免这个问题呢?
答:只要有人在读数据,那么其他事务就啥也不干。这也叫“串行化”。这个和并发执行相悖,就是当我客户端提交了多个事务过来,我就一个一个执行,一个执行完毕了,我再执行下一个事务。
四、事务的四大隔离级别
对于上述的三个问题,MySQL提供四种不同的隔离级别,我们可以通过配置文件来进行设置。
设置不同的隔离级别,就会使事务之间的并发执行的影响产生不同的差别,从而会印象到上述的三个问题的情况。并非不是说事务修改的时候不会更新数据,而且说限制了其他事务对这个数据的访问能力。
1)read uncommitted(读未提交)
这种情况下,一个事务可以读取另一个事务未提交的数据。
此时,就会可能产生脏读,不可重复读,幻读三种问题。
但是此时,多个事务并发执行程序是最高的,执行速度也是最快的。
2)read committed(读已提交)
这种情况下,一个事务只能读取另一个事务提交之后的数据(给些操作加锁了)。
可能会产生不可重复读,幻读问题(脏读问题解决了)。
此时,并发程度会降低,执行速度会变慢,同时也称为,事务之间的隔离性提高了(事务之间的相互影响变小,得到的数据也更准确)
3)repeatable read(可重复读)(MySQL默认情况)
这个情况下,相当于给写操做和读操作都枷锁了。
此时,可能产生幻读问题,解决了脏读和不可重读问题,并发程度进一步降低,执行速度进一步变慢,事务之间的隔离性进一步提高了
4)serializable(串行化)
此时,所有的事物都是在服务器上一个接一个执行的。
此时,解决了脏读,不可重复读,幻读问题,并发程度最高,执行速度最慢,隔离性最高,数据也最准确
隔离级别 | 脏读 | 不可重复读 | 幻读 |
read uncommitted | √ | √ | √ |
read committed | × | √ | √ |
repeatable | × | × | √ |
serializable | × | × | × |
总之:可以根据实际需要,需要速度快,还是希望数据比较准,来选择你需要的隔离级别。