一、事务简介
事务,简短的说就是一组操作要么全部完成,要么全部不做,绝不允许只做其中的一部分操作。
比如银行转账,A账号转给B账号1000元,其中包括两个操作A-1000、B+1000,要么这两个操作全部完成,要么全部不做,决不允许只执行其中一个操作(你可以想想想如果A-1000,但是B没有+1000会有什么后果?
事务回滚,当一个事务执行过程中发生了异常、错误,则重新回到最先未开始执行的过程。
比如上面那个银行转账过程,假设A-1000操作已经完成,但是在执行B+1000操作时,系统发生位置错误,这时需要回到未执行该转账操作之前的状态,即A、B原来多少钱还是多少钱,一分不能少。
事务提交,当一个事务执行过程没有发生任何异常、错误,这时我们要保存这个事务的修改。
比如上面的银行转账过程,假设A-1000、B+1000操作全部完成,没有出现任何异常、错误,这时需要保存事务执行状态修改(A减少了1000元,B增加了1000元),即事务提交。
二、事务的特性(ACID)
事务具有四大特性,如下:
1、原子性(Atomicity):整体 【原子性是指事务包含的所有操作要么全部成功,要么全部失败】
2、一致性(Consistency):数据 【事务提交后的状态合集称为一致,也就是数据库只包含事务提交的状态】
3、隔离性(Isolation):并发 【对于任意两个并发的事务A和B,在事务A看来,B要么在A开始之前就已经结束,要么在A结束之后才开始,这样每个事务都感觉不到有其他事务在并发地执行。】
4、持久性(Durability):结果 【持久性是指一个事务一旦提交了,就保存到硬盘上,对数据库中的数据的改变就是永久性的】
三、数据隔离性引发的问题
1、脏读:读取到了未提交的数据(如果事务这时候回滚了,那么第二个事务就读到了脏数据)
| 时间线 | 事务A | 事务B |
|---|---|---|
| 1 | 开启事务 | |
| 2 | 开启事务 | |
| 3 | 更新字段值(update user set age=18 where id=1) | |
| 4 | 查询字段值(select * from user where id=1)(结果是18) | |
| 5 | 执行rollback回滚事务 | |
| 可以看到事务A此时读取id=1的age=18就是脏读 |
2、不可重复读:同一个事务中,对于同一数据,执行完全相同的select语句时可能看到不一样的结果。
| 时间线 | 事务A | 事务B |
|---|---|---|
| 1 | 开启事务 | |
| 2 | select age from user where id=1(结果16) | 开启事务 |
| 3 | 更新字段值(update user set age=18 where id=1) | |
| 4 | 提交事务;commit | |
| 5 | select age from user where id=1(结果18) | |
| 可以看到事务A中两次读取的age值不一样,到底是16还是18? |
3、幻读:当用户读取某一范围的数据行时,另一个事务又在该范围内插入了新行,当用户再读取该范围的数据行时,会发现有新的“幻影” 行;
| 时间线 | 事务A | 事务B |
|---|---|---|
| 1 | 开启事务 | |
| 2 | 查询范围行【select * from user where age>15】(结果有两行) | 开启事务 |
| 3 | 插入行【inset into user valus(10,周杰伦,41)】 | |
| 4 | 提交事务;commit | |
| 5 | 查询范围行【select * from user where age>15】(结果有三行) | |
| 可以看到事务A俩次读取的行数不一样,到底是两行还是三行? |
幻读和不可重复读的区别 不可重复读 重点是修改:在同一事务中,同样的条件,第一次读的数据和第二次读的数据不一样。(一个事务多次读取同一范围内数据时,另外一个发生了事务发生insert操作并提交了)。数据变化。 幻读 重点在于新增或者删除:在同一事务中,同样的条件,第一次和第二次读出来的记录数不一样。(一个事务多次读取同一条数据时,另外一个发生了事务发生insert,delete操作并提交了。insert操作一条新数据并不会影响刚才被读的那条数据。)。行数变化。
4、丢失更新: 丢失更新就是两个不同的事务(或者Java程序线程)在某一时刻对同一数据进行读取后,先后进行修改。导致第一次操作数据丢失。脏读、不可重复读、幻读三个问题都是由事务A对数据进行修改、增加,事务B总是在做读操作。如果两事务都在对数据进行修改则会导致另外的问题:丢失更新。MySQL InnoDB存储引擎即使是最低的级别,也不会出现第一类丢失更新问题 (为什么?下篇Mysql锁机制会讲到)
| 时间线 | 事务A | 事务B |
|---|---|---|
| 1 | 开启事务 | |
| 2 | 开启事务 | |
| 3 | update user set age=18 where id=1; | |
| 4 | update user set age=38 where id=1; | |
| 5 | 事务提交 | |
| 6 | 事务提交 | |
| 此时事务A做的更新被事务B做的更新所覆盖,这种现象就是丢失更新。 |
四、数据库隔离级别
| 隔离级别 | 是否存在脏读 | 是否存在不可重复读 | 是否存在幻读 | |
|---|---|---|---|---|
| Read Uncommited(读未提交) | Y | Y | Y | 考虑脏数据问题基本不用 |
| Read Commited(读已提交) | N | Y | Y | Oracle默认隔离级别 |
| Repeatable Read(可重复读) | N | N | Y | Mysql默认隔离级别,使用了加行锁的方式避免了不可重复读问题。 |
| Serializable(串行化) | N | N | N | 考虑并发问题基本不用 |
read uncommited:是最低的事务隔离级别,它允许另外一个事务可以看到这个事务未提交的数据。 read commited:保证一个事物提交后才能被另外一个事务读取。另外一个事务不能读取该事物未提交的数据。 repeatable read:这种事务隔离级别可以防止脏读,不可重复读。但是可能会出现幻象读。它除了保证一个事务不能被另外一个事务读取未提交的数据之外还避免了以下情况产生(不可重复读)。 serializable:这是花费最高代价但最可靠的事务隔离级别。事务被处理为顺序执行。除了防止脏读,不可重复读
