2021-08-08

一、是什么?

事务就是一组原子性的sql查询,或者说一个独立的工作单元。事务内的sql语句要么全部执行成功,要么全部失败。

二、经典应用场景:银行转账

A账户向B账户转账200元,至少需要3个步骤:

  1. 检查A账户余额 >200元。
  2. A账户余额 -200元。
  3. B账户余额 +200元。

翻译成sql如下:

  1. begin;
  2. select balance from t_wallet where id=1001;
  3. update t_wallet set balance=balance-200 where id=1001;
  4. update t_wallet set balance=balance+200 where id=1002;
  5. commit;

上述3个步骤必须打包在一个事务中,任何一个步骤的失败则必须回滚所有的事务。

三、标准特征:ACID

  1. 原子性(atomicity)
    一个事务必须被视为一个不可分割的最小工作单元,整个事务中的所有操作要么全部提交成功,要么全部失败回滚,对于一个事务来说,不可能只执行其中的一部分操作,这就是事务的原子性。
  2. 一致性(consistency)
    数据库总是从一个一致性的状态转换到另外一个一致性的状态。在前面的例子中,一致性确保了即使在执行3、4两条语句之间系统崩溃,支票账户中也不会损失200元,因为事务没有提交,所以事务中所做的修改也不会保存到数据库中。
  3. 隔离性(isolation)
    通常来说,一个事务所做的修改在最终提交以前,对其他事务是不可见的。在前面的例子中,当执行完第3条语句,第4条语句还未开始时,此时有另外一个账户汇总程序开始运行,则其看到的支票账户的余额并没有被减去200元。后面隔离级别会详细介绍。
  4. 持久性(durability)
    一旦事务提交,则其所做的修改就会永久保存到数据库中。即使此时系统崩溃,修改的数据也不会丢失。持久性是个有点模糊的概念,因为实际上持久性也分很多不同的级别。有些持久性能提供非常强的安全保障,而有些则未必。而且不可能有做到100%的持久性保证的策略。

一个实现了ACID的数据库,相比没有实现ACID的数据库,需要更强的CPU,更大的内存,更多的存储空间。用户根据业务是否需要事务处理,可以选择合适的存储引擎。

四、四种隔离级别

mysql是我们常用的数据库软件,查看隔离级别:SELECT @@tx_isolation;
在这里插入图片描述
1. READ UNCOMMITED(未提交读)
设置数据库隔离级别为:read uncommitted
mysql>set global transaction isolation level read uncommitted;
在这里插入图片描述

事务A并未提交,此时事务B读却读到了事务A并未提交的数据。假设此刻事务A由于某种原因回滚事务,此时事务B拿到的age=19这条数据肯定得不到正确的结果。

2. READ COMMITED(提交读)
读提交正是为了解决上面的问题。大多数数据库系统的默认级别是READ COMMITTED(mysql不是)。读未提交满足前面提到的隔离性简单定义:一个事务开始时,只能看见已经提交的事务所做的修改。换句话说,一个事务从开始知道提交之前,所做的修改对其它事务都是不可见的。这个级别也叫做不可重复读,因为执行两次同样的查询,可能会得到不一样的结果。

设置数据库隔离级别为:read committed
mysql>set global transaction isolation level read committed;
在这里插入图片描述

3. REPEATABLE READ (可重复读)
该级别保证了在同一个事务中多次读取同样记录的结果是是一致的,解决了不可重复读的问题。
设置数据库隔离级别为:REPEATABLE READ
mysql>set global transaction isolation level REPEATABLE READ;在这里插入图片描述
但是理论上该级别还是无法解决幻读问题。所谓幻读,指的是当某个事务在读取某个范围内的记录时,另外一个事务又在该范围内插入了新的记录,当之前的事务再次读取该范围的记录时,会产生幻行。
我们先看Mysql在RR级别下会不会出现幻读?
在这里插入图片描述

注意:此刻事务B的插入一直在等待,直至事务A提交,或者事务A超时。并没有出现我们所谓的幻读。为什么呢?请注意我们现在的引擎是 InnoDB,级别是RR,mysql使用了一种next-key的算法。官方文档如下:
在这里插入图片描述
大意就是说:next-key=index-row locking(记录锁)+gap locking(间隙锁),记录锁不让其它的事务,在这条记录上修改,删除;间隙锁负责不让其它的事务在索引的间隙插入数据,当这俩哥们组合在一起的时候,便可以解决幻读。上述的事务B的insert没有插入成功,就是gap locking生效了。我们关于Mysql锁的相关知识,请看我的另一篇博客。
如果我们想演示下幻读现象呢?仔细阅读官方文档,细心的小伙伴可以发现一句话:
在这里插入图片描述
在这里插入图片描述

大意是说,Gap locking 可以被禁止,但是这样会让其它事务在间隙中插入数据,从而产生幻读问题。再去寻根溯源,讲了一句很有意思的话,当你明确把隔离级别设置成RC时,Gap locking就被禁止掉了。哈哈哈,我只想测试下RR级别下没有加Gap locking,看是否出现幻读。其实经过上面的“没有出现幻读”演示,小伙伴想必也可以反推出幻读的场景。还好有其它方式:可以禁止间隙锁。请百度之,如何禁止间隙锁不在本文讨论重点。可参考博客:如何开启和关闭MySQL 间隙锁(gap lock)
当我们在RR级别下禁止了Gap locking,我们就看看到底什么是幻读?
在这里插入图片描述
注意!!! 注意!!!重点来了,标红部分,RR级别下是不是多出了一行,和刚开始查询的不一样。这就是所谓的幻读,结合我们上述幻读的定义,是不是一目了然?

4. SERIALIZABLE(可串行化)
SERIALIZABLE是最高的隔离级别。它通过强制事务串行执行,避免了前面所说的幻读的问题。简单来说,SERIALIZABLE会在读取的每一行数据上都加锁,所以可能导致大量的超时和锁竞争问题。实际应用中也很少用到这个隔离级别,只有在非常需要确保数据的一致性而且可以接受没有并发的情况下,才考虑采用该级别。

ANSI SQL隔离级别

隔离级别脏读(Dirty Read)不可重复读(NonRepeatable Read)幻读(Phantom Read)
未提交读(Read uncommitted)可能可能可能
已提交读(Read committed)不可能可能可能
可重复读(Repeatable read)不可能不可能可能
可串行化(Serializable )不可能不可能不可能

五、总结

  1. 本文着重讲了事务的隔离级别和现象演示。相信很多同学会有疑问。
  • MySQL RR级别next-key为什么解决了幻读?
  • select 和select for update 之间区别?以及它们在不同事务级别下的表现?

带着问题去寻找答案,是一种很好的学习方式,我们一起学习进步。下篇博客我们一起探讨下MySQL锁和隔离级别之间的关系,也许在这里会有你想要的答案。

六、参考来源

  1. 《高性能MySQL》
  2. MySQL官方文档
  3. MySQL事务隔离级别和实现原理(看这一篇文章就够了!)
  4. 如何开启和关闭MySQL 间隙锁(gap lock)
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值