SQL事务之什么是事务★★★
事务
什么是事务
本部分参考文章链接为 :(1条消息) 事务ACID理解_dengjili的博客-CSDN博客_acid
仅用作个人学习,特此声明
要么都成功,要么都失败
比如说两个sql同时执行
———————————
SQL1执行:A给B转账
SQL2执行:B收账A
———————————
这个过程中钱是不能少的,也就是说A+B总数必须是不变的
所谓的事务其实就是将一组SQL放到一个批次中去执行
- 原子性(Atomicity)
原子性是指事务是一个不可分割的工作单位,事务中的操作要么都发生,要么都不发生。 - 一致性(Consistency)
事务前后数据的完整性必须保持一致。 - 隔离性(Isolation)
事务的隔离性是多个用户并发访问数据库时,数据库为每一个用户开启的事务,不能被其他事务的操作数据所干扰,多个并发事务之间要相互隔离。 - 持久性(Durability)
持久性是指一个事务一旦被提交,它对数据库中数据的改变就是永久性的,接下来即使数据库发生故障也不应该对其有任何影响
下面举几个简单的例子来理解事务的四个特点
1、原子性(Atomicity)
针对同一个事务
这个过程包含两个步骤
A: 800 - 200 = 600
B: 200 + 200 = 400
原子性表示,这两个步骤一起成功,或者一起失败,不能只发生其中一个动作
就像是银行转账一样
2、一致性(Consistency)
针对一个事务操作前与操作后的状态一致
操作前A:800,B:200
操作后A:600,B:400
一致性表示事务完成后,符合逻辑运算
无论怎么转账,A+B=1000是不变的
3、持久性(Durability)
表示事务结束后的数据不随着外界原因导致数据丢失
操作前A:800,B:200
操作后A:600,B:400
如果在操作前(事务还没有提交)服务器宕机或者断电,那么重启数据库以后,数据状态应该为
A:800,B:200
如果在操作后(事务已经提交)服务器宕机或者断电,那么重启数据库以后,数据状态应该为
A:600,B:400
4、隔离性(Isolation)
针对多个用户同时操作,主要是排除其他事务对本次事务的影响
事务一)A向B转账200
事务二)C向B转账100
两个事务同时进行,其中一个事务读取到另外一个事务还没有提交的数据,执行步骤如图所示,按照数字顺序执行
隔离性用于解决以上问题
下面我们来了解一下隔离所导致的一些问题
5、隔离导致的一些问题
参考文章链接
md文档/狂神说java/03.MySql/MySQL基础.md · 大垚大摆/java笔记 - Gitee.com
(1条消息) 数据库第一类第二类丢失更新_爱米粒Emily的博客-CSDN博客_第一类更新丢失
1.脏读
脏读又称无效数据的读出,是指在数据库访问中,事务T1将某一值修改,然后事务T2读取该值,此后T1因为某种原因撤销对该值的修改,这就导致了T2所读取到的数据是无效的,值得注意的是,脏读一般是针对于update操作的。
1、在事务A执行过程中,事务A对数据资源进行了修改,事务B读取了事务A修改后的数据。
2、由于某些原因,事务A并没有完成提交,发生了RollBack操作,则事务B读取的数据就是脏数据。
这种读取到另一个事务未提交的数据的现象就是脏读(Dirty Read)。
2.不可重复读
事务B读取了两次数据资源,在这两次读取的过程中事务A修改了数据,导致事务B在这两次读取出来的数据不一致。即在一个事务内读取表中的某一行数据,多次读取结果不同。(这个不一定是错误,只是某些场合不对)
这种在同一个事务中,前后两次读取的数据不一致的现象就是不可重复读(Nonrepeatable Read)。
3.虚读(幻读)
事务B前后两次读取同一个范围的数据,在事务B两次读取的过程中事务A新增了数据,导致事务B后一次读取到前一次查询没有看到的行。
幻读和不可重复读有些类似,但是幻读强调的是集合的增减,而不是单条数据的更新。
4.第一类更新丢失
事务A和事务B都对数据进行更新,但是事务A由于某种原因事务回滚了,把已经提交的事务B的更新数据给覆盖了。这种现象就是第一类更新丢失。
观察下图发现:A事务在撤销时,“不小心”将B事务已经转入账户的金额给抹去了。
5.二类更新丢失
其实跟第一类更新丢失有点类似,也是两个事务同时对数据进行更新,但是事务A的更新把已提交的事务B的更新数据给覆盖了。这种现象就是第二类更新丢失。
上面的例子里由于支票转账事务覆盖了取款事务对存款余额所做的更新,导致银行最后损失了100元,相反如果转账事务先提交,那么用户账户将损失100元。第二类丢失更新,实际上和不可重复读是同一种问题。
有个很生动的例子来描述这种情况
财务系统加工资,若公司本次调薪决定给员工张三加1k人民币,财务部两名操作人员A和B,过程情况若是这样的:
1)A操作员在应用系统的页面上查询出张三的薪水信息,然后选择薪水记录进行修改,打开修改页面但A突然有事离开了,页面放在那没有做任何的提交。
2)这时候B操作员同样在应用中查询出张三的薪水信息,然后选择薪水记录进行修改,录入增加薪水额1000,然后提交了。
3)这时候A操作员回来了,在自己之前打开的薪水修改页面上也录入了增加薪水额1000,然后提交了。
其实上面例子操作员A和B只要一前一后做提交,悲剧就出来了。后台修改薪水的sql:update 工资表 set salary = salary + 增加薪水额 where staff_id = ‘员工ID’。这个过程走下来后结果是:张三开心了这次涨了2k,操作员A和B都郁闷了。
6、解决方法:隔离级别
为了解决以上的问题,主流的关系型数据库都会提供四种事务的隔离级别。事务隔离级别从低到高分别是:读未提交,读已提交,可重复读,串行化。事务隔离级别越高,越能保证数据的一致性和完整性,但是执行效率也越低,所以在设置数据库的事务隔离级别时需要做一下权衡,mysql默认是可重复读
-
读未提交
读未提交(Read Uncommitted),是最低的隔离级别,所有的事务都可以看到其他未提交的事务的执行结果。只能防止第一类更新丢失,不能解决脏读,可重复读,幻读,所以很少应用于实际项目。
-
读已提交
读已提交(Read Committed),在该隔离级别下,一个事务的更新操作只有在该事务提交之后,另外一个事务才可能读取到同一笔数据更新后的结果。可以防止脏读和第一类更新丢失,但是不能解决可重复和幻读的问题。
-
可重复读(重要)
可重复读(Repeatable Read),mysql默认的隔离级别。在该隔离级别下,一个事务多次读同一个数据,在这个事务还没有结束时,其他事务不能访问该数据(包括了读写),这样就可以在同一个事务内两次读到的数据是一样的。可以防止脏读、不可重复读、第一类更新丢失,第二类更新丢失的问题,不过还是会出现幻读。
-
串行化
串行化(Serializable),这是最高的隔离级别。它要求事务序列化执行,事务只能一个接着一个的执行,不能并发执行。在这个级别,可以解决上面提到的所有并发问题,但是可能导致大量的超时现象和锁竞争,通常不会用这个隔离级别。
-
总结
扩展知识:回滚机制
在mysql中,恢复机制是通过回滚日志(undo log)实现的,所有的事务进行的修改都会先记录到这个回滚日志中,然后在堆数据库中的对应进行写入。
mysql的事务是有 redo 和 undo 的,redo 操作的所有信息都是记录到重做日志(redo_log)中,也就是说当一个事务做commit操作时,需要先把这个事务的操作写到redo_log中,然后在把这些操作flush到磁盘上,当出现故障时,只需要读取redo_log,然后在重新flush到磁盘就行了。
而对于undo就比较麻烦,mysql在处理事务时,会在数据共享表空间里申请一个段即segment段来保存undo信息,当在处理rollback,不是完完全全的物理undo,而是逻辑undo,也就是说会将之前的操作进行反操作(对于每个insert,回滚时会执行delete;对于每个delete,回滚时会执行insert;对于每个update,回滚时会执行一个相反的update,把数据改回去。),但是这些共享表空间是不进行回收的。这些表空间的回收需要由mysql的master thread进程进行回收。