目录
一、概念理解:
二、回滚(rollback)
三、事务的四大特性(ACID):
1)原子性(Atomicity)
2)一致性(Consistency)
3)隔离性(Isolation)——重点
1、并发
2、并发事务问题(三类):
1》脏读(Dirty Read)
脏读示例——“我在银行卡BUG”:
解决对策
2》不可重复读(Non-repeatable Read)
解决对策:
3》幻读(Phantom Read)
解决对策:
再谈隔离性:
4)持久性(Durability)
一、概念理解:
事务是一组可以作为最小逻辑工作单位的原子。
这些事务要么完全执行,要么完全不执行。
这样说起来还是比较抽象,我们用一个实际例子来解释——微信转账
微信转账就是一个事务。
如图,假设张三要转账500元给李四:
微信转账的逻辑可以分为这几步:
1.张三(1000-500)
2.李四(500+500)
以上这一块逻辑就可以打包成一个事务,他要么所有都执行,要么全部不执行。
不能出现张三钱少了500,但是李四却没有收到钱的情况,这会造成严重后果。
数据库中,引入事务,就可以避免这种尴尬的情况发生。
二、回滚(rollback)
刚才讲过,事务要么全部执行,要么都执行。
那么数据库是怎么实现这样的机制的呢?
首先,如果事务都执行,很简单,直接全部执行,中间没有意外中断。
但是,如果在执行一个事务期间,出现了意外,但是已经执行了一些事物的逻辑,这时候就会触发回滚。
回滚就是把事务已经执行的所有操作(sql)进行还原撤销,最终达到一个好像没有执行过这个事务的效果。
回滚是一个很强大的机制,即使是程序崩溃、主机断电,它也可以把正在执行的事务全都还原回去。
因为回滚的底层是每执行一次sql,就会把它记录到日志文件当中,在发生意外之后,重启数据库,参考之前的记录进行还原即可。
三、事务的四大特性(ACID):
1)原子性(Atomicity)
把一组逻辑打包成事务,他就想一个单一逻辑(原子),要么成功要么失败,即使执行中途出现意外,也可以回到原来没有执行过这个事务(或者一个整体逻辑)的状态。
除了最典型的转账外,还有网站下单、新生登记等都算是一个事务。
2)一致性(Consistency)
这里的一致性不是说数据保持一致。
指的是在事务执行前后必须符合指定的规则,满足逻辑的一致性。
例如,张三(有1000元)转账500元给李四(有500元)。
不能说操作完之后,张三少了500元但是李四仍然只有500元,这样逻辑就不一致了。
一致性保证转账完成后(事务执行完),张三的账户余额是500元,李四的账户余额是1000元。
3)隔离性(Isolation)——重点
这个性质,解释起来有点费时,但是非常重要。
可以先把 4)持久性看了,再来慢慢学习隔离性。
我们先来讲一下需要先理解的知识。
1、并发
并发是指同一段时间有多个事务需要在数据库中执行。并发性允许多用户同时使用和查看和修改数据库中的数据,这样可以提高系统运行效率。
总之,记住并发可以同时进行多个事务,提高运行效率
并发虽然可以提高运行效率,但也有缺点:
由于多个事务可以同时修改数据库中的数据,并发就可能会导致一些问题。
举个例子。
网购相信大家都习以为常了。咱们去购买一件商品,就算是一个事务。
有时一件商品可能会被多个用户下单,尤其是特殊季节,比如双十一,以及即将到来的618。
为了防止巨量请求导致的卡顿,可以使用并发,既多个事务同时访问修改数据库,但这样可能就会导致一些bug。
比如:明明显示还有存货,但是点击下单,却提示库存已空。
之所以会出现这种情况,就是并发导致的。既自己在访问数据库时,显示还有库存,但是过了一会儿有的人刚好把商品买空(把数据库内容修改了),这时候,你自然就无法成功下单。
哈哈,终于直到这些APP的底层逻辑了,感觉自己又行了。
2、并发事务问题(三类):
并发会出现三类问题,有时候这些问题可以忽略,有时候这些问题不能忽略,视业务场景而定。
注意:
并发会引起问题的前提是不同事务,针对同一个数据库、同一个表、同一个数据而言的。
刚才讲的购物的问题知识并发引起的问题的一种,就下来系统介绍一下并发实事务问题。
1》脏读(Dirty Read)
可以把它理解成——提前读
脏读示例——“我在银行卡BUG”:
小海的账户初始余额是一千元。
小海A从账户取出元600元(记为事务A),
然后小海又从账户中把剩下的400元取出(记为事务B)。
1、A事务:
* 开始执行A事务;
* 扣除100元(此时账户实际余额为900元);
* 事务A还没有提交数据;
2、B事务:
由于A、B是并发执行的,B事务执行的时候A事务连账户余额都没有来得及修改。
因此B事务查询到的余额仍然是1000元。
小海这时想,哎呀WOC,手里揣着600元,还能在取1000元,发了发了🤩🤩🤩。
结果小海真的从一张只有1000元的银行卡中,取出了1600元。
至此,他好像打开了财富密码,每天晚上都偷偷在ATM机旁边卡BUG,取空了一个,又换另一个。
就这样,他走上了违法犯罪的不归路~~
可见脏读的脏不是说数据是“脏的”,而是说读取到错误的、临时的数据。
解决对策
想要解决脏读(提前读)也很简单,就不让他提前读呗。
在一个事务修改数据的时候,其他事务不能提前去读。
既“给些操作加锁”——这里不展开讨论,是多线程相关的知识
值得注意的是:
即使给写加上锁,好像每个事务都是一个一个执行的了,但实际上在MySQL中事务之间仍然是并发执行的。
只是确保了每一个事务可以读取到有效数据,并不是完全没有并发。
2》不可重复读(Non-repeatable Read)
一个事务先后读取同一条数据,两次读取的数据并不相同,这就是不可重复读。
解决对策:
之所以出现两次读取数据不匹配,原因很简单,就是其他事务对这条数据进行了修改。
因此解决方法也很简单——“给读加锁”
也就是一个事务在读取一个数据的时候,其他事务不能够对这条数据进行更改。
3》幻读(Phantom Read)
幻读实际上就是不可重复读的特殊情况。
在事务A执行的时候查询到对应数据行不存在,但在插入数据时,又发现数据行存在了,好似出现了幻影。(反之亦然)
出现这种原因是因为其他事务在A事务执行过程中,插入或者删除了对应行数据所导致的。
相当于是不可重复度的特殊情况,
不可重复读是其他事务修改了对应A事务查询的数据。
幻读是直接把对应的数据删除或者插入了。
解决对策:
只要A事务在执行,其他事务就不要做任何操作.
这样的操作叫做“串行化”。——既没有了并发,而是一个事务一个事务去执行。
再谈隔离性:
隔离性就是确保事务与事务之间并发进行(速度更快),但是他们又相互独立,互不干扰(保证数据准确)。
但是鱼和熊掌不可兼得,上面所述属于理想状态。
我们刚才讲过,并发虽然快,但是会不可避免的引发三种问题。
因此:
要么牺牲运行速度,一个事务一个事务执行,确保精确性。(隔离级别高)
要么牺牲数据的精确性,提高运行速度。(隔离级别低)
既然如此,拿到就没有更好的解决办法了吗?
当然不是。
解决方式:
在实际业务中,有的业务要求数据必须精确,例如涉及金钱交易的,那么就是用隔离级别高的方式进行。
如果是像短视频点赞这种,实际上并不用要求计算精确的点赞数量,给个大概就好,主要在于运行效率,提高用户交互体验,这时候就可以采用隔离级别低的方式进行。
数据库中有四种隔离级别:
两张图意思一样,第一张是英文,第二张是中文。
越往下,隔离越严格,数据越精确,速度越慢。
根据具体业务对数据质量的要求选择合适的隔离等级。
MySQL默认的隔离等级是Repeatable Read(可重复读)
4)持久性(Durability)
一个事务一旦提交或者回滚,它对数据库中的数据的更改是永久的。