Mysql-InnoDB存储引擎中-特性ACID

说起数据库事务,绕不开的就是两点,一个是事务的特性,另一个就是事务的隔离机制。

至于事务的隔离机制,之前已经分析过很多,传送门1传送门2

今天我们来说一说数据库特性,有人会说了,数据库特性有什么啊,不就是ACID嘛,A-原子性,C-一致性,I-隔离性,D-持久性。那好,我问你那数据库是基于什么玩意儿去保证它的呢,比如怎么保证它的原子性,或者怎么保证它的持久性呢~~~~没想过的是不是懵逼了,别急,今天我们就是要去深入了解它,不过本文先申明,对于这个也是学习,并不是个人经验分享或者是个人感悟分享。

1. Redo log

Redo log叫做重做日志,主要用来实现事务的持久性,即D。由两部分组成:

1. 内存中的重做日志缓冲(redo log buffer),缺点是易失。

2. 重做日志(redo log file)特点是持久的。

1.1 过程:

事务的存储引擎是InnoDB,通过Force Log at Commit机制实现事务的持久性,即当事务提交时,必须先将该事务的所有日志写入到重做到日志文件进行持久化,待事务的提交操作完成之后才算完成。

由于重做日志文件打开没有使用O_DIRECT选项,因此重做日志缓冲先写入到文件系统缓存。为了确保写入磁盘,必须进行一次fsync操作。因此fsync的效率取决于磁盘的性能,决定了事务提交的性能。

1.2 示例图

扫描二维码关注公众号,回复: 1306883 查看本文章


1.3 重做策略

innodb_flush_log_at_trx_commit用来控制重做日志刷新到磁盘的策略:

0:表示事务提交时不进行重做日志的刷新,该操作只在Master Thread中完成(Master Thread每1s会进行一次fsync操作)。

1:默认策略,每次事务提交时都进行刷新动作。

2:只写入文件系统的缓存内,不进行fsync的操作。

0和2一般不可取,不过还是提供了。

取值影响:默认插入的是50万条数据,时间的花费基本都是fsync上。


2. Undo log

Redo log是规范了重做的操作,因此如果一旦宕机,那么通过重做日志可以轻易恢复刚刚执行的数据变更,但是回滚的操作却不能解决,因此mysql提供了Undo log来记录原始数据,保证了事务能正常回滚,也就保证了原子性 A。

因此Innodb在执行变更操作时不仅仅生成了Redo日志,也同样生成了Undo日志,用来保证操作能够正常回滚。

2.1 存放地址

undo 存放在数据库内部一个特殊段中(segment),这个段称之为 undo段,位于共享表空间内(共享表是什么意思?不懂)。

2.2 误区

因为undo可以恢复回滚之前的数据,因此有人认为undo是将数据库物理地址恢复到执行语句或事务之前的样子,但是事实并非如此。undo是逻辑日志,所以只是将数据库逻辑的恢复到原来的样子。需要理解这句话,只是逻辑的取消之前的修改并且逻辑的恢复成原来的值。但是要注意的是数据结构和页本身在回滚之后可能大不相同,所以可以看出Innodb所做的工作其实是执行了相反的sql语句,,比如insert的语句回滚时执行delete,delete语句执行insert。

2.3 另一个作用MVCC(先写着,虽然不是很了解)

undo的另一个作用是MVCC。当用户读取一行记录时,若该记录已经被其他事务占用时,当前事务可以通过undo读取之前的行版本信息,以此实现非锁定读取。

2.4 redo的产生

undo log也会同样产生redo log,因为undo log 同样也需要持久性的保护。

2.5 基本结构


此结构也是从网络总结而来,本人并不知道真正内部结构是否如此。

1. rseg0保留项

2. rseg1-rseg32 用于临时表的undo

3. rseg33-rseg128用于普通事务的undo。

每个回滚段维护的是一个段头页的地址,在该页中又划分了1024个槽用于记录undo对象,每个事务占用两个slot,一个用于insert,一个用于update/delete。

Innodb1.1 版本,只有一个回滚段,只能支持理论上1024个事务。

1.1版本之后,可以支持理论上128*1024个事务。

1.2版本开始,可以通过参数对Rseg进行设置。

innodb_undo_directory:设置文件位置,可以将回滚段放在共享表之外的地址。

innodb_undo_logs:设置回滚段的个数,默认是128。

innodb_undo_tablespaces:设置构成Rseg文件数量,可以均匀的分散在多个文件中。

2.6 执行过程

当事务提交时,InnoDB会做下面两件事:

1. 将undo log放入列表,为purge提供依据。

2.判断undo log是否可以重用,可以的话分配给下个事务使用

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

2.6 格式

分为2个格式:

2.6.1 insert undo log

2.6.2 update undo log

insert操作由于事务的隔离性要求,它只能对本事务可见,对其他事务不可见,所以该undo log可以在事务提交后直接删除,不需要进行purge操作。

而delete/update操作不行,需要放入链表中,由purge线程进行最后的删除。

3. purge

对于delete操作,并不是执行真正的删除,例如对于delete * from t where a = 1而是在讲delete flag设置成1,该记录仍然在B+树中。对于update语句,不是直接对记录进行更新,而是标识旧记录为删除,然后产生一条新记录。那么此时产生的旧版本的记录什么时候删除呢,怎么删除?就是通过purge操作。

purge线程开启purge操作,每10s进行一次,通过undo log来进行旧版本数据的删除。上面其实已经说过,一个undo 段页上允许多个对象重用产生undo log,由此InnoDB会维护一个history列表,它根据事务提交的顺序,将undo log进行连接。


1. 先提交的事务排在尾端。undo page中存放了很多不同事务的undo log。

2. 首先找到需要删除的trx1,清理之后会在trx1所在的undo page中寻找其他可以删除的trx,删除trx3之后发现了trx5,但是trx5被引用了,所以不删除,接着删除trx7。然后找到最尾端的trx2,发现trx2在Undo Page2,所以到Page2中再进行删除。

引用文章:

MysqlInnoDB存储引擎。

猜你喜欢

转载自blog.csdn.net/qq_32924343/article/details/80397876