事务
情景带入: A给B转账,B这边没收到,而A这边已经扣除了.不符合实际.转账的时候显然是对于数据进行操作,会执行sql语句.
事务就是用来解决上述问题的.
事务的本质就是把多个sql语句打包成一个整体,要么全执行成功,要么全都不执行.
换言之,就是把你的操作统一化,要么所有操作都成功,要么就都不成功,如果执行中有某一项操作失败,其之前所有的操作都回滚到未执行这一系列操作之前的状态。
事务的四大特性:
- 原子性
- 一致性
- 持久性
- 隔离性
如何开启事务?
- 开启事务
start transaction;
- 需要执行事务的sql语句
- 结束事务
commit;
原子性
上述将数据打包成一个整体,操作统一化.这就是原子性. 也就是说将转账过程中的一系列操作看做一个整体.
一致性
事务执行前后,数据是按要求改变的.符合逻辑的. 即A给B转账,A这边应扣除m,B这边应增加m.是一一对应的.
持久性
一个事务一旦提交,它对数据库中数据的改变就应该是永久性的. 事务修改的内容是写到硬盘上的,持久存在的.重启也不丢失.
隔离性
在实际生活中,一个服务器是面向好多个客户端的,当多个客户端进行请求的时候,服务器就要同时处理这些客户端请求.
将服务器同时处理多个客户端请求称为并发
.
对于隔离性,就是为了解决并发执行事务所引起的问题.
并发执行事务引起的问题:
- 若修改同一个表/同一个数据,就可能会出现问题.
对于问题,分为以下几种:
- 脏读
- 不可重复读
- 幻读
脏读
事务A正在对数据进行修改的过程中,事务B对该数据进行读取. 此时B的读操作就是脏读问题
,读到的数据称为"脏数据".在如下场景里面:
上述针对同一个账户,当事务A在存款的过程中,事务B进行取款.A先提交事务使得账户存款修改为2000,但B后提交事务对数据进行了再次修改,使得最终账户余额记录为0.
解决方法:
- MySQL引入 “写操作加锁” 这样的机制 . 即事务A写数据的时候,事务B不能进行读取操作
特点:
- 意味着写操作和读操作不能同时进行, 即不能并发执行
- 降低并发程度(降低效率),提高隔离性(提高了数据的准确性)
不可重复读(前后多次读取,数据内容不一致)
情景带入: A同学发布了一篇文章(版本1),并把文章分享给B同学.在B同学读的过程中,A同学对文章进行修改,随后发布版本2.
于是在B同学读的过程中,就会发现文章和之前有点不一样了.即两次读文章发生了不一样的内容.这就叫做不可重复读.
不可重复读:
- 事务A已经提交了数据,此时事务B开始读取数据.在读取过程中,事务A又一次提交了数据.
- 这时,意味着事务B多次读取数据,读出来的结果不一样. 就叫做不可重复读.
- 换言之,
不可重复读: 第二次读取的结果不能复现第一次的结果(两次结果不一样)
解决方法:
- 在事务B读取的过程中也进行加锁.(读加锁) 读完就解锁
特点:
- 降低并发程度(降低效率)
- 提高隔离性(提高数据准确性)
幻读(前后多次读取,数据总量不一致)
情景带入: 在读加锁,写加锁的前提下,A同学又发布了一篇文章,在B读取的过程中,发现多了一篇文章.这就是幻读.
在读加锁和写加锁的前提下,一个事务两次读取同一个数据,发现读取的数据值是一样的,但是结果集不一样. 称为幻读
即,A同学第一篇文章内容和之前是一样的,但多了一篇文章,文章总量与读之前是不一样的.
解决方法:
- 数据库使用"串行化"这样的方式来解决幻读.彻底放弃并发执行,一个接一个的串行处理事务
特点:
- 并发程度最低(效率最低)
- 隔离性高(数据最准确)
总结:
- 针对脏读: 采用写加锁.
- 针对不可重复读: 采用读加锁
- 针对幻读: 采用彻底串行化.
针对上述问题,数据库提供了四种隔离级别,分别对应上述情况.
数据库四个隔离级别
对应解释(MySQL默认采用第三种隔离级别):
至于选用哪种隔离级别,根据业务来定.它们没有好坏之分.