【mysql】Innodb存储引擎是如何保证事务的ACID四个原则的

事务是数据库区别于文件系统的重要特性之一。
事务会把数据库从一种一致状态转换为另一种一致状态。

事务的四个特性

  • 原子性:原子性是指整个数据库事务是不可分割的工作单位。只有使事务中所有的数据库操作都执行成功,才算整个事务成功。事务中一个SQL语句执行失败,已经执行成功的SQL语句必须撤销,数据库状态应该退回到执行事务前的状态。
  • 一致性:一致性是指事务将数据库从一种状态转变为下一种一致状态。在事务之前和事务结束之后,数据库的完整性约束没有被破坏。
  • 隔离性:又可叫做并发控制、可串行化、锁等。事务的隔离性要求每个读写事务的对象对其他事务的操作对象能相互隔离,即该事务提交前对其他事务都不可见,通常使用锁来实现。(下篇文章总结)
  • 持久性:事务一旦提交,其结果就是永久性的。即使发生宕机等故障,数据库也能将数据恢复。

INNODB如何保证事务的四个特性?

由于隔离性关系到锁的相关知识内容比较多,下篇文章总结,先来总结其他的三个特性的实现。

原子性、一致性、持久性通过数据库的redo log和undo log来完成。redo log称为重做日志,用来保证事务的原子性和持久性。undo log用来保证事务的原子性和一致性。
在这里插入图片描述

redo log

redo log即重做日志。通常是物理日志(物理日志是幂等的),记录的是页的物理修改操作。由两部分组成一是内存中的日志缓冲,二是重做日志文件。在事务进行时,Innodb首先将重做日志信息先放入重做日志缓冲中,然后按照一定频率将其刷新到重做日志文件中。
redolog整个结构和执行过程
重做日志的写入时间:
InnoDB是事务的存储引擎,其通过Force Log at Commit 机制实现事务的持久性,即当事务提交COMMIT时,必须将该事务的所有日志写入到重做日志文件中进行持久化。重做日志是在事务进行中不断地写入,即并不是随事务提交的顺序进行写入的。

重做日志的写入方式:
在INNODB存储引擎中,重做日志都是以512字节进行存储的。这意味着重做日志缓冲,重做日志文件都是以块的方式进行保存的。称之为重做日志块,每块的大小为512字节。
若一个页中产生的重做日志数量大于512字节,那么需要分割为多个重做日志块进行存储。此外,由于重做日志块的大小和磁盘扇区大小一样,都是512字节,因此重做日志的写入可以保证原子性,不需要doublewrite技术。

重做日志格式:
| redo_log_type | space | page_no | redo log body |
由于innodb存储引擎的存储管理是基于页的,故其重做日志格式也是基于页的。

LSN 检查点技术:
LSN是Log Sequence Number的缩写,代表的是日志序列号。LSN代表的是事务写入重做日志的总量。例如当前重做日志LSN为1000,有一个事务T1写入了100字节的重做日志,那么LSN就变成了1100,若又有事务T2写入了200字节的重做日志,那么LSN就变成了1300.

LSN不仅记录在重做日志中,还存在每个页中。在每个页的头部,有一个值FIL_PAGE_LSN,记录了该页的LSN。在页中,LSN表示该页最后刷新时LSN的大小。因为重做日志记录的是每个页的日志,因此页中的LSN用来判断页是否需要进行恢复操作。
例如:页P1的LSN为10000,而数据库启动时,INNODB检测到写入重做日志中的LSN为13000,并且该事务已经提交,那么数据库需要进行恢复操作,将重做日志应用到P1页中,同样的,对于重做日志中LSN小于P1页中的LSN不需要进行重做,因为P1页中的LSN表示页已经被刷新到该位置。

在mysql中通过SHOW ENGINE INNODB STATUS可以查看LSN的情况:
log sequence number :当前的lsn
log flushed up to :刷新到重做日志文件中的lsn
last checkpoint at 表示刷新到磁盘的lsn,checkpoint的位置,在恢复过程中,通常仅需要恢复checkpoint位置之后的数据。

重做日志是如何保证持久性的?
为了确保每次日志都写入重做日志文件,在每次将重做日志缓冲写入重做日志文件后,innodb存储引擎都需要调用一次fsync操作,通过fsync来确保日志总能被持久化到磁盘中。同时,重做日志文件的持久化操作是在数据库数据持久化操作之前已经完成的。

undo log

重做日志记录了事务的行为,可以很好地通过其对页进行“重做”操作。但是事务有时还需要进行回滚操作,这时就需要undo。

undo 用于将数据库逻辑地恢复到数据库事务执行语句之前的样子。

在对数据库进行修改时,innodb存储引擎不但会产生redo,还会产生一定量的undo。这样如果用户只需事务或语句由于某种原因失败了,或者rollback语句请求回滚,就可以利用undo将数据回滚到修改之前的样子。

undo log作用

  • 回滚
  • MVCC 多版本并发控制实现数据库一致性非锁定读

undo log 存储
存放在数据库内部的一个特殊段(segment)中,这个段称为undo段。undo段位于共享表空间内。undo是逻辑日志,因此只是将数据库逻辑的恢复到原来的样子。

事务在undo log segment分配页并写入undo log的这个过程同样需要写入重做日志。当事务提交时,innodb存储引擎会做以下两件事情:

  • 将undo log放入列表中,以供之后的purge操作。
  • 判断undo log所在的页是否可以重用,如果可以分配给下个事务使用

事务提交后并不能马上删除undo log 以及undo log所在的页。这是因为可能还有其他事务需要通过undo log来得到行记录之前的版本。故事务提交时将undo log放入一个链表中,是否可以删除undo log及undo log所在的页由purge线程来判断

undo log 分类

  • insert undo log: insert undo log 是指在insert操作中产生的undo log。因为insert 操作纪律,只对事务本身可见,对其他事务不可见。故该undo log可以在事务提交后直接删除。不需要进行purge操作。
  • update undo log:update undo log 记录的是对delete和update操作产生的undo log。该undo log可能需要提供MVCC机制,因此不能再事务提交时就进行删除。提交时放入undo log链表,等待purge线程进行最后的删除。

对于delete操作并不是直接删记录,而是将记录标记为已删除,也就是将记录的delete flag设置为1,而记录最终的删除是在purge操作中完成。
对于update操作,如果更新对象的非主键值,则记录一条TRX_UNDO_UPD_EXIST_REC的undo日志。对于更新对象的主键值,则会将原有记录标记为删除,再插入一条新的记录,会有一个类型为TRX_UNDO_DEL_MARK_REC和一条类型为TRX_UNDO_INSERT_MARK_REC两条undo日志。

undo log 存储的版本链
在InnoDB引擎表中,它的聚簇索引记录中有两个必要的隐藏列:

  • trx_id
    这个id用来存储的每次对某条聚簇索引记录进行修改的时候的事务id。
  • roll_pointer
    每次对哪条聚簇索引记录有修改的时候,都会把老版本写入undo日志中。这个roll_pointer就是存了一个指针,它指向这条聚簇索引记录的上一个版本的位置,通过它来获得上一个版本的记录信息。(注意插入操作的undo日志没有这个属性,因为它没有老版本)

对于不同的操作,存储的数据如下:

SELECT
  innodb会根据以下两个条件检查每行记录:
    a.innodb只查找版本号早于当前事务版本的数据行,<=当前事务版本号,这样可以确保事务读取的行,要么是在事务开始前已经存在的,要么是事务自身插入或者修改过的

b.行的删除版本要么未定义,要么大于当前的事务版本号。这可以确保事务读取到的行,在事务开始之前未被删除。

INSERT
  INNODB为新插入的每一行保存当前系统版本号作为行版本号
DELETE
  innodb为删除的每一行保存当前系统版本号作为行删除标识
UPDATE
  innodb为插入一行新纪录,保存当前系统版本号为行版本号,同时保存当前系统版本号到原来的行作为行删除标识

undo log如何保证事务的一致性?
由于在事务执行时,不仅会产生redo log,也同时会产生undo log,当事务执行时发生宕机或者异常时,通过回滚执行undo log能使数据返回初始状态来保证数据一致性。

innodb存储引擎如何保证事务的原子性?
原子性分为两种情况,一种是所有语句执行完成,或者一句失败,之前执行过的语句全部回滚。
第一种情况由redo log完成。当事务commit后,数据库持久化未完成时发生宕机,可以通过执行redo log完成事务的提交。
第二种情况由undo log完成。当一条语句执行失败时,事务回滚,执行undo log使得数据恢复到原来的样子。

以上关于innodb存储引擎保证事务的原子性,一致性和持久性的实现,还有很多待完善的地方,下片文章总结关于隔离性相关的知识。

  • 1
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值