Home 成长之路 事务总结

事务总结

0 71

一、什么是事务?

事务是逻辑上的一组操作,要么都执行,要么都不执行

 

我们系统的每个业务方法可能包括了多个原子性的数据库操作,比如下面的savePerson()方法中就有两个原子性的数据库操作,这些原子性的数据库操作性是有依赖的,它们要么都执行,要么就都不执行;

	public void savePerson() {
		personDao.save(person);
		personDetailDao.save(personDetail);
	}
另外,需要格外注意的是:事务能否生效 数据库引擎是否支持事务是关键;
常用的MySQL数据库默认使用支持事务的innodb引擎
但是如果把数据库引擎变为myisam,那么程序也就不再支持事务了;

事务最经典的例子:假如小明要给小红转账,这个转账涉及到两个关键操作:
1.将小明的余额减少1000元
2.将小红的余额增加1000元
万一在这两个操作之间突然出现错误,比如银行系统崩溃或者网络故障,导致小明余额减少而小红的余额没有增加,这样就不对了;
事务就是保证这两个关键操作要么都成功,要么都失败!
个人举例:请假;某一天小明生病了请假,来找辅导员小红请假,规定每个人拥有一定的请假次数,涉及到关键操作:
1.小明提交假条,小明的请假次数减少一次
2.小红审阅假条,小红批阅假条的次数增加一次
万一在这两个操作之间突然出现错误,比如小明不想请假了,导致小明假条次数减少而小红的批阅次数却没有增加,这也是不对的;
所以:事务就是保证这两个关键操作要么都成功,要么都失败;

并发事务带来的问题(数据库层面)
在典型的应用程序中,多个事务并发运行,经常会操作相同的数据来完成各自的任务(多个用户对统一数据进行操作)。
并发虽然是必须的,但可能会导致以下问题:

 *脏读当一个事务正在访问数据并对数据进行了修改,而在这种修改还没提交到数据库中,此时另外一个事务也访问了这个数据
然后使用了这个数据因为这个数据是还没有提交的数据,那么另外一个事务读到的这个数据就是“脏数据”,依据“脏数据”所作的操作可能是不正确的;
举例:

对datanum应该是执行两次(两个线程)20++,成为40,发生脏读,最终结果了35;

*丢失修改:指在一个事务读取一个数据时,另外一个事务也访问了该数据
那么在第一个事务中修改了这个数据后,第二个事务也修改了这个数据
这样第一个事务内的修改结果就被丢失,因此也称为丢失修改;
举例:事务1读取某表中的数据A=20,事务2也读取A=20,事务1修改A=A-1,事务2也修改A=A-1,最终结果A=19,事务1的修改被丢失

*不可重复读:一个事务内多次读同一数据。
在这个事务还没有结束时,另外一个事务也访问该数据
那么在第一个事务中的两次读数据之间,由于第二个事务的修改导致第一次事务两次读取的数据可能不太一样。
这样就发生了在一个事务内两次读到的数据是不一样的情况,因此称为不可重复读。



*幻读: 幻读与不可重复读类似。
它发生在一个事务(T1)读取了几行数据,接着另一个并发事务(T2)插入了一些数据时。
在随后的查询中,第一个事务(T1)就会发现多了一些原本不存在的记录,就好像发生了幻觉一样,所以称为幻读

不可重复读和幻读区别:

不可重复读的重点是修改比如多次读取一条记录发现其中某些列的值被修改,幻读的重点在于新增或者删除比如多次读取发现记录增多或减少了

事务隔离级别有哪些?MySQL的默认隔离级别是?

SQL 标准定义了四个隔离级别:

  • READ-UNCOMMITTED(读取未提交): 最低的隔离级别,允许读取尚未提交的数据变更,可能会导致脏读、幻读或不可重复读
  • READ-COMMITTED(读取已提交): 允许读取并发事务已经提交的数据,可以阻止脏读,但是幻读或不可重复读仍有可能发生
  • REPEATABLE-READ(可重复读): 对同一字段的多次读取结果都是一致的,除非数据是被本身事务自己所修改,可以阻止脏读和不可重复读,但幻读仍有可能发生
  • SERIALIZABLE(可串行化): 最高的隔离级别,完全服从ACID的隔离级别。所有的事务依次逐个执行,这样事务之间就完全不可能产生干扰,也就是说,该级别可以防止脏读、不可重复读以及幻读

隔离级别 脏读 不可重复读 幻影读
READ-UNCOMMITTED
READ-COMMITTED ×
REPEATABLE-READ × ×
SERIALIZABLE × × ×

MySQL InnoDB 存储引擎默认支持的隔离级别是 REPEATABLE-READ(可重复读)。我们可以通过SELECT @@tx_isolation;命令来查看,MySQL 8.0 该命令改为SELECT @@transaction_isolation;

mysql> SELECT @@tx_isolation;
+-----------------+
| @@tx_isolation  |
+-----------------+
| REPEATABLE-READ |
+-----------------+

这里需要注意的是:与 SQL 标准不同的地方在于 InnoDB 存储引擎在 REPEATABLE-READ(可重读) 事务隔离级别下使用的是Next-Key Lock 锁算法,因此可以避免幻读的产生,这与其他数据库系统(如 SQL Server) 是不同的。所以说InnoDB 存储引擎的默认支持的隔离级别是 REPEATABLE-READ(可重读) 已经可以完全保证事务的隔离性要求,即达到了 SQL标准的 SERIALIZABLE(可串行化) 隔离级别。因为隔离级别越低,事务请求的锁越少,所以大部分数据库系统的隔离级别都是 READ-COMMITTED(读取提交内容),但是你要知道的是InnoDB 存储引擎默认使用 REPEAaTABLE-READ(可重读) 并不会有任何性能损失

InnoDB 存储引擎在 分布式事务 的情况下一般会用到 SERIALIZABLE(可串行化) 隔离级别。

 

二、事务的特性(ACID)了解吗?

1.原子性(Atomicity):一个事务(transaction)中的所有操作,或者全部完成,或者完全不完成,不会结束在中间某个环节;事务在执行过程中发生错误,会被回滚(Rollback)到事务开始前的状态,就像这个事务从来没发生过一样,即服务不可分割;

2.一致性(Consistency):在事务开始前和事务结束后,数据库的完整性没有被破坏,这表示写入的资料必须完全符合所有的预设约束、触发器、级联回滚等;

3.隔离性(Isolation):数据库允许多个并发事务同时对其数据进行读写和修改的能力,隔离性可以防止多个事务并发执行时由于交叉执行而导致数据的不一致;事务隔离分为不同级别,包括未提交读、提交读、可重复读和串行化;

4.持久性(Druability):事务处理结束后,对数据的修改就是永久的,即便系统故障也不会丢失;

三、详谈Spring对事务的支持 重要提醒: 程序是否支持事务首先取决于数据库

比如使用MySQL的话,如果你选择的是innodb引擎,那么是可以支持事务的
但是如果你的MySQL数据库使用的是myisam引擎的话,那不好意思从根本上就不支持事务;

四、那么:MySQL是怎么保证原子性的?

如果想要保证事务的原子性,就需要在异常发生时,对已经执行的操作进行回滚
在MySQL中,恢复机制是通过回滚日志(undo log)实现的

所有事务进行的修改都会先记录到这个回滚日志中,然后再执行相关的操作

如果在执行过程中遇到异常,直接利用回滚日志将数据回滚到修改之前的样子即可,回滚日志会先于数据持久化到磁盘上;

这样就保证了即使遇到数据库突然宕机等情况,当用户再次启动数据库的时候,数据库还能通过查询回滚日志来回滚将之前未完成的事务;
举例: 以转账为例子,减少小明的余额之前会记录回滚日志,增加小红的余额之前也会记录回滚日志
如果发生异常,那么小明的余额则通过回滚日志恢复,小红同理,并且回滚日志会持久化到磁盘上
保证了即使遇到数据库宕机的情况也可以通过查询回滚日志的方式来回滚;

SIMILAR ARTICLES

发表评论

发表评论