mysql事务的四大特性


如何实现事务的隔离性

在并发场景下,并发执行事务的时候:

​ 比如先执行的事务A要把商品库存数量从 0 修改成 1,然后又有一个事务B在执行时要读取商品的库存数量,这时候事务B读取到的商品库存数量为 1,事务B准备下一步要进行扣减库存数量了(但还未执行扣减);这时候,事务A回滚了,商品库存数量从 1 又变成了 0,这就会导致事务B中执行扣减库存的操作时出现问题。

​ 这就出现了“脏读”。一个事务读取到了另一个事务没有提交的数据。所以,在并发执行事务的时候,多个事务之间应该要相互隔离,互不影响,否则就会出现问题。这就是事务的隔离性

MVCC + 锁,再配合 undo log 来实现事务的隔离性

脏读、不可重复读、幻读的问题

通过事务的隔离级别来应对。

数据库提供了四种事务的隔离级别:

  • 读未提交:最低的一种隔离级别,每个事务都能看到其他事务未提交的数据。无法解决脏读、不可重复读、幻读的问题。实际场景基本不会用到。
  • 读已提交:每个事务只能读到其他事务已经提交的数据。可以解决脏读的问题,因为其他事务未提交的事务读取不到;但是无法解决不可重复读和幻读的问题。
  • 可重复读:Mysql数据库的一个默认的隔离级别。 当事务第一次读取数据时,会生成一份数据快照,生成快照之后,其他事务的修改对当前事务来说是不可见的。 因为这时候当前事务多次读取的时候,读取的是生成数据快照那一刻时的数据,这样前后多次读取的数据都是一样的,就解决了不可重复读的问题。即使这时候重复读取,也读取不到变了之后的数据。但还是无法解决幻读的问题。
  • 串行化:Mysql最高的一个隔离级别。这个级别下,事务不能并发执行,只能一个接一个的顺序执行,串行化是可以解决所有的并发问题,但性能很差。并发量高的情况下,容易导致大量的超时和锁竞争的问题,通常不会启用这个隔离级别。

如何实现:

  • 串行化:加锁,只有拿到锁的事务才能执行。
  • 读未提交:不用管,多个事务并发实现天然就是。但针对写的操作还是要加锁,多个事务不能同时写一个数据。
  • 可重复读:MVCC +复用第一次read view 去实现。 在可重复读的级别下,第一次 select 会生成一个 read view,read view 是判断当前的数据版本对当前事务是否可见的。 所以,可重复读的级别当一个事务多次读取一个数据时,每次都会复用第一次生成的 read view。 换句话说,当你的 read view生成的时候,哪些版本的数据对当前事务可见就已经固定下来了,后续每次读取时通过生成的同一个read view去判断数据的可见性。可解决不可重复读的问题。

MVCC:多版本并发控制。通过维护数据的多个版本来实现。

按照正常流程,只要保证了事务的原子性持久性隔离性,事务的一致性自然就实现了。

事务的原子性

原子性:就是使代码逻辑(方法)中的多个执行sql的操作逻辑,要么全部成功,要么全部失败。

mysql的原子性如何实现呢

可以通过undo log 回滚日志去实现。

比如,n个连续的sql操作中,失败了一个,通过undo log给前面已经执行成功的sql操作进行回滚。

事务的持久性

事务提交后,数据库崩溃了,导致数据丢失了怎么办?此时就需要事务的持久性来保证。

持久性:只要事务一提交,无论发生什么事,数据库的修改(操作)都不会丢失。

如何实现持久性呢

通过redo log来实现,每一次事务提交之后,就把这个修改的操作写入到redo log中,这样即便 mysql数据库中的旧数据还没有立马变成修改后的值,也能读取redo log来恢复数据。

换个角度,redo log 让mysql有了一定的崩溃恢复的能力。

事务的隔离性

事务的隔离性:针对并发执行事务的情况,多个事务之间应该相互隔离,互不影响。否则,就会出现问题。

在并发场景下,并发执行事务的时候:

​ 比如先执行的事务A要把商品库存数量从 0 修改成 1,然后又有一个事务B在执行时要读取商品的库存数量,这时候事务B读取到的商品库存数量为 1,事务B准备下一步要进行扣减库存数量了(但还未执行扣减);这时候,事务A回滚了,商品库存数量从 1 又变成了 0,这就会导致事务B中执行扣减库存的操作时出现问题。

​ 这就出现了“脏读”。一个事务读取到了另一个事务没有提交的数据。所以,在并发执行事务的时候,多个事务之间应该要相互隔离,互不影响,否则就会出现问题。这就是事务的隔离性

MVCC + 锁,再配合 undo log 来实现事务的隔离性

事务的一致性

一致性:无论事务最终执行结果是成功还是失败,执行成功后的结果和执行成功前的结果,数据是一致的;同理,执行失败也是。

比如,对于转账的业务来讲,在这个事务里,转账人要执行的是扣除转账金额(减少100)的操作,收款人要执行的是添加余额(添加100)的操作,无论事务是否成功,转账人和收款人的钱的总数,应该是保持不变的。

一致性如何实现呢

按照正常流程,只要保证了事务的原子性持久性隔离性,事务的一致性自然就实现了。

JAVA-技能点
知识点
Mysql