MySQL的事务实现

事务需要满足四个特性,就是ACID。

  • 原子性(Atomicity,或称不可分割性)
  • 一致性(Consistency)
  • 隔离性(Isolation)
  • 持久性(Durability)

开始

在很久很久之前,没有数据库,我创建了多个txt文件,每个txt文件存储一些数据,我写了一个工具类,进行CRUD操作。这个简单的数据库天然支持持久性(Durability)。

原子性的实现

有一个操作需要修改两个txt文件,我希望同时成功或者失败,于是新创建了个文件,叫做undo log,中文名叫回滚日志,用于记录数据被修改前的信息,每操作一个文件的一次我就记录一下。undo log主要记录的是数据的逻辑变化,为了在发生错误时回滚之前的操作,需要将之前的操作都记录下来,然后在发生错误时才可以回滚。我实现了原子性(Atomicity)的需求。

持久性的实现

  由于频繁操作txt文件,每个txt文件所在的磁盘位置不一致,磁头不停的转,每次读写数据都需要磁盘IO,效率会很低。为此,我(InnoDB引擎)提供了缓存(Buffer Pool),Buffer Pool中包含了磁盘中部分数据页的映射,作为访问数据库的缓冲:当从数据库读取数据时,会首先从Buffer Pool中读取,如果Buffer Pool中没有,则从磁盘读取后放入Buffer Pool;当向数据库写入数据时,会首先写入Buffer Pool,Buffer Pool中修改的数据会定期刷新到磁盘中(这一过程称为刷脏)。Buffer Pool的使用大大提高了读写数据的效率,但是也带了新的问题:如果MySQL宕机,而此时Buffer Pool中修改的数据还没有刷新到磁盘,就会导致数据的丢失,事务的持久性无法保证。
  于是,redo log被引入来解决这个问题:当数据修改时,除了修改Buffer Pool中的数据,还会在redo log记录这次操作;当事务提交时,会调用fsync接口对redo log进行刷盘。如果MySQL宕机,重启时可以读取redo log中的数据,对数据库进行恢复。redo log采用的是WAL(Write-ahead logging,预写式日志),所有修改先写入日志,再更新到Buffer Pool,保证了数据不会因MySQL宕机而丢失,从而满足了持久性要求。
  既然redo log也需要在事务提交时将日志写入磁盘,为什么它比直接将Buffer Pool中修改的数据写入磁盘(即刷脏)要快呢?主要有以下两方面的原因:
(1)刷脏是随机IO,因为每次修改的数据位置随机,但写redo log是追加操作,属于顺序IO。
(2)刷脏是以数据页(Page)为单位的,MySQL默认页大小是16KB,一个Page上一个小修改都要整页写入;而redo log中只包含真正需要写入的部分,无效IO大大减少。

隔离性的实现

  假设事务不满足隔离性,我们耳熟能详的转钱例子就会出问题。所以事务需要满足隔离性。隔离性与原子性、持久性侧重于研究事务本身不同,隔离性研究的是不同事务之间的相互影响。隔离性是指,事务内部的操作与其他事务是隔离的,并发执行的各个事务之间不能互相干扰。严格的隔离性,对应了事务隔离级别中的Serializable (可串行化),但实际应用中出于性能方面的考虑很少会使用可串行化。
  隔离性追求的是并发情形下事务之间互不干扰。简单起见,我们仅考虑最简单的读操作和写操作(暂时不考虑带锁读for update等特殊操作)。如果没有隔离性的需求,就不会有MVCC的存在。锁的实现也会很简单,没有读锁一类的,只有一个写锁防止并发导致的错误就行了。

MySQL隔离级别有以下四种(级别由低到高):

  • READUNCOMMITED(未提交读)
  • READCOMMITED(提交读)
  • REPEATABLEREAD(可重复读)
  • SERIALIZABLE (串行化)
1. READUNCOMMITED未提交读

如何实现?

如果mysql 什么也不做,默认就是这个这样。

有什么好处?

快,效率高

有什么坏处?

有脏读,不可重复读,幻读的问题。脏读在实际应用中就是别人给你转钱,还没扣掉别人的,你查看账户的钱已经多了。

2. READCOMMITED提交读

如何实现?

mysql引入了MVCC的概念

有什么好处?

解决了脏读,读取到的数据是已经提交的数据。

有什么坏处?

效率变慢,依旧有不可重复读,幻读的问题。不可重复读就是你查询出来的数据第一次看到的是V1,再次查询时最新的值V2。这是对的,读到最新的值没有任何问题,但是在核查对账,主从备份的场景下,一个事务写A表和B表(两个表有业务关系),当我们在事务写完A表,没有写完B表的时候读取A表和B表数据,我们就会导致对账失败。

3.REPEATABLEREAD(可重复读)

如何实现?

mysql引入了MVCC的概念

有什么好处?

解决了不可重复读,一次事务中多次读取到的数据是相同的。

有什么坏处?

效率变慢,依旧有幻读的问题。解决了不可重复读的问题,但是没有解决幻读,不可重复读主要是删除和更新的影响,我们能够屏蔽掉,但是我们无法屏幕掉insert的影响,insert的影响就是幻读。在实际应用中就是你在一个方法中分页查询数据,后插入的数据如果在前面,会导致页码乱掉,你用这个数据打印还是做其他作用都不是一个合法的数据。

4.SERIALIZABLE (串行化)

如何实现?

mysql引入了读写锁的概念。对于同一行记录,“写”会加“写锁”,“读”会加“读锁”。当出现读写锁冲突的时候,后访问的事务必须等前一个事务执行完成,才能继续执行。

有什么好处?

解决了幻读。

有什么坏处?

效率变慢

一致性的实现

一致性是事务追求的最终目标:前面提到的原子性、持久性和隔离性,都是为了保证数据库状态的一致性。此外,除了数据库层面的保障,一致性的实现也需要应用层面进行保障。

实现一致性的措施包括:

  • 保证原子性、持久性和隔离性,如果这些特性无法保证,事务的一致性也无法保证
  • 数据库本身提供保障,例如不允许向整形列插入字符串值、字符串长度不能超过列的限制等
  • 应用层面进行保障,例如如果转账操作只扣除转账者的余额,而没有增加接收者的余额,无论数据库实现的多么完美,也无法保证状态的一致

其实幻读和不可重复读的区别是更新和新增

https://www.toutiao.com/a6777338939360412171/

未完待续

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值