在日常开发中我们经常会遇到需要同时处理多个操作的情况,比如在购物时,我们需要同时完成支付和更新库存两个操作。这时,如果其中一个操作失败了,我们就需要进行回滚,以保证数据的一致性。
那么,如何在MySQL中实现这样的功能呢?答案就是——事务。下面我们就来介绍一下MySQL事务是什么?它是如何使用的?
## 一、什么是事务?
### 事务定义
- 事务是一个最小的不可再分的工作单元;通常一个事务对应一个完整的业务(例如银行账户转账业务,该业务是一个最小的工作单元)。
- 一个完整的业务需要批量的DML(insert、update、delete)语句共同联合完成。
- 事务只和DML语句有关,或者说DML语句才有事务。这个和业务逻辑有关,业务逻辑不同,DML语句的个数不同。
### 事务是什么?
往通俗的讲就是,事务就是一个整体,里面的内容要么都执行成功,要么都不成功。不可能存在部分执行成功而部分执行不成功的情况。
就是说如果单元中某条sql语句一旦执行失败或者产生错误,那么整个单元将会回滚(返回最初状态)。所有受到影响的数据将返回到事务开始之前的状态,但是如果单元中的所有sql语句都执行成功的话,那么该事务也就被顺利执行。
比如有一个订单业务:
>1.订单表当中添加一条记录 2.商品数量数据更新(减少) 3…
当多个任务同时进行操作的时候,这些任务只能同时成功,或者同时失败。
## 二、事务的特性
事务有四个特性:一致性、持久性、原子性、隔离性。下面分别来解释一下这四个特性都有什么含义。
### 原子性
事务是一个不可分割的工作单位,要么同时成功,要么同时失败。例:当两个人发起转账业务时,如果A转账发起,而B因为一些原因不能成功接受,事务最终将不会提交,则A和B的请求最终不会成功。
### 持久性
一个事务一旦提交成功,它对数据库中数据的改变将是永久性的,接下来的其他操作或故障不应对其有任何影响。
### 隔离性
一个事务的执行不能被其他事务干扰,即一个事务内部的操作及使用的数据对并发的其他事务是隔离的,并发执行的各个事务之间不能互相干扰。
### 一致性
事务执行接收之后,数据库完整性不被破坏。
注意:只有当前三条性质都满足了,才能保证事务的一致性。
![image.png](https://static.golangjob.cn/231201/2f00f477a009aa9d231f2a8e9b8a6bd2.png)
**刷脏:** Mysql为了保证存储效率,于是每次将要读写的文件是先存储在缓存池中,对于数据的操作是在缓存池中,而mysql将会定期的刷新到磁盘中。
## 三、事务的使用
事务是如何保证操作的完整性的呢?
其实事务执行中间出错了,只需要让事务中的这些操作恢复成之前的样子即可, 这里涉及到的一个操作,回滚(rollback)。
事务处理是一种对必须整批执行的 MySQL 操作的管理机制,在事务过程中,除非整批操作全部正确执行,否则中间的任何一个操作出错,都会回滚 (rollback)到最初的安全状态以确保不会对系统数据造成错误的改动。
相关语法:
```
-- 开启事务
start transaction;
-- 若干条执行sql
-- 提交/回滚事务
commit/rollback;
```
注意:在开启事务之后,执行sql不会立即去执行,只有等到commit操作后才会统一执行(保证原子性)。
>你还在苦恼找不到真正免费的编程学习平台吗?可以试试[云端源想](https://ydcode.cn/memberIndex?sourceId=365)!课程视频、在线书籍、在线编程、实验场景模拟、一对一咨询……你想要的全部学习资源这里都有,重点是统统免费![点这里即可查看](https://ydcode.cn/memberIndex?sourceId=365)
示例:
首先创建一个账户表并初始化数据,
![image.png](https://static.golangjob.cn/231201/febbc5512d18d52c09bfb91dafb74262.png)
首先看正常情况下的转账操作,
![image.png](https://static.golangjob.cn/231201/c50c811bad65f445ddbac9284eea2937.png)
如果操作中出现异常情况,比如sql语句中所写的注释格式错误导致sql执行中断。
![image.png](https://static.golangjob.cn/231201/0dbf2643c3074fb4c2ef911b7c3898d0.png)
观察结果发现了张三的账户少了2000元,但李四的账户余额并没有增加,在实际操作中这种涉及钱的操作发生这种失误可能会造成很大的损失。
为了防止这种失误的出现我们就可以使用事务来打包这些操作。
![image.png](https://static.golangjob.cn/231201/657ba2fffe7a199daf22f3753b1e9f26.png)
观察这里的结果发现在当前的数据库用户查询到的account表中的账户余额发生了变化,但开启了事务之后在commit之前只是临时的预操作并不会真的去修改表中的数据。
可以退出数据库再打开重新查询表中数据或者切换用户去查询去验证表中数据是否发生改变,这里就不作演示了。
发现操作结果异常之后,当前用户需要恢复到事务之前的状态,即进行回滚操作。
![image.png](https://static.golangjob.cn/231201/3636037b7eb83d5c05146a7a16ce1333.png)
如果开启事务之后发现预操作的结果是预期的效果,此时我们就可以提交事务, 当我们提交完事务之后, 数据就是真的修改了,也就是硬盘中存储的数据真的改变了。
![image.png](https://static.golangjob.cn/231201/cf58fca6bc1d031326ecd4f08164c459.png)
要注意事务也不是万能的,不能保证你删表删库之后可以完全恢复,只是在适量的数据和操作下使用事务可以避免一些问题。
回滚(rollback)操作,实际上是我们把事务中的操作再进行逆操作,前面是插入, 回滚就是删除…
这些操作是有很大开销的,可以保存,但不能够无限保存,最多是将正再执行的事务保存下来,额外的内容就不好再保存了;数据库要是有几十亿条数据, 占据了几百G硬盘空间,不可能去花费几个T甚至更多的空间用来记录这些数据是如何来的。
## 四、事务的并发异常
但是呢,因为某一刻不可能总只有一个事务在运行,可能出现A在操作text表中的数据,B也同样在操作text表。
那么就会出现并发问题,对于同时运行的多个事务,当这些事务访问数据库中相同的数据时,如果没有采用必要的隔离机制,就会发生以下各种并发问题。
### 1、脏读(读未提交)
脏读:事务A读取到了事务已经修改但未提交的数据,这种数据就叫脏数据,是不正确的。
![image.png](https://static.golangjob.cn/231201/6483e22dc82d04b34b428fbfea0e68ed.png)
### 2、不可重复读(读已提交)
不可重复读:对于事务A多次读取同一个数据时,由于其他是事务也在访问这个数据,进行修改且提交,对于事务A,读取同一个数据时,有可能导致数据不一致,叫不可重复读。
![image.png](https://static.golangjob.cn/231201/74f0e01f867c97a2252bc3b6f5fc8f3b.png)
### 3、幻读(可重复读)
幻读:因为mysql数据库读取数据时,是将数据放入缓存中,当事务B对数据库进行操作:例如删除所有数据且提交时,事务A同样能访问到数据,这就产生了幻读。
![image.png](https://static.golangjob.cn/231201/8e7128921b77231ad93b5f56afd339ed.png)
解决幻读问题的办法是串行化,也就是彻底的舍弃并发,此时只要李四在读代码,张三就不能进行任何操作。
### 4、串行化
串行化:事务A和事务B同时访问时,在事务A修改了数据,而没有提交数据时,此时事务B想增加或修改数据时,只能等待事务A的提交,事务B才能够执行。
![image.png](https://static.golangjob.cn/231201/67f42a0873f94eac22be66c726253807.png)
所以,为了避免以上出现的各种并发问题,我们就必然要采取一些手段。mysql数据库系统提供了四种事务的隔离级别,用来隔离并发运行各个事务,使得它们相互不受影响,这就是数据库事务的隔离性。
## 五、MySQL的四个隔离级别
MySQL中有 4 种事务隔离级别, 由低到高依次为:读未提交 Read Uncommitted、读已提交 Read Committed、可重复读 Repeatable Read、串行化 Serializable。
Description
1. read uncommitted(读未提交数据)
允许事务读取未被其他事务提交的变更。(脏读、不可重复读和幻读的问题都会出现)。
2. read committed(读已提交数据)
只允许事务读取已经被其他事务提交的变更。(可以避免脏读,但不可重复读和幻读的问题仍然可能出现)
3. repeatable read(可重复读)
确保事务可以多次从一个字段中读取相同的值,在这个事务持续期间,禁止其他事务对这个字段进行更新(update)。(可以避免脏读和不可重复读,但幻读仍然存在)
4. serializable(串行化)
确保事务可以从一个表中读取相同的行,在这个事务持续期间,禁止其他事务对该表执行插入、更新和删除操作,所有并发问题都可避免,但性能十分低下(因为你不完成就都不可以弄,效率太低)
一个事务与其他事务隔离的程度称为隔离级别。数据库规定了多种事务隔离级别,不同隔离级别对应不同的干扰程度,隔离级别越高,数据一致性就越好,但并发性就越差。
串行化的事务处理方式是最安全的,但不能说用这个就一定好,应该是根据实际需求去选择合适的隔离级别,比如银行等涉及钱的场景,就需要确保准确性,速度慢一点也没什么;
而比如抖音、B站、快手等上面的点赞数,收藏数就没必要那么精确了,这个场景下速度提高一点体验会更好一些。
总结
MySQL事务具有原子性、一致性、隔离性和持久性四大特性,通过合理地管理事务,能够帮助我们保证数据的完整性和一致性。希望通过这篇文章,大家对MySQL事务有了更深入的了解,也希望大家在今后的工作中能够更好地运用事务来处理数据。##
有疑问加站长微信联系(非本文作者))