你的位置:首页 > 软件开发 > 数据库 > 【msql】redo and undo log

【msql】redo and undo log

发布时间:2015-11-29 21:00:10
Innodb Crash RecoveryInnoDB 有两块非常重要的日志,一个是undo log,另外一个是redo log,前者用来保证事务的原子性以及InnoDB的MVCC,后者用来保证事务的持久性。和大多数关系型数据库一样,InnoDB记录了对数据文件的物理更改,并保证 ...

Innodb Crash Recovery

InnoDB 有两块非常重要的日志,一个是undo log,另外一个是redo log,前者用来保证事务的原子性以及InnoDB的MVCC,后者用来保证事务的持久性。和大多数关系型数据库一样,InnoDB记录了对数据文件的物理更改,并保证总是日志先行,也就是所谓的WAL,即在持久化数据文件前,保证之前的redo日志已经写到磁盘

Crash Recovery是InnoDB引擎的一个特点,当故障发生,重新启服务后,会自动完成恢复操作,将数据库恢复到之前一个正常状态,恢复进程会完成两步

第一步:检查redo日志,将之前完成并提交的事务全部重做;

第二步:将undo日志中,未完成提交的事务,全部取消

LSN

LSN(log sequence number) 用于记录日志序号,它是一个不断递增的 unsigned long long 类型整数。在 InnoDB 的日志系统中,LSN 无处不在,它既用于表示修改脏页时的日志序号,也用于记录checkpoint,通过LSN,可以具体的定位到其在redo log文件中的位置。

为了管理脏页,在 Buffer Pool 的每个instance上都维持了一个flush list,flush list 上的 page 按照修改这些 page 的LSN号进行排序。因此定期做redo checkpoint点时,选择的 LSN 总是所有 bp instance 的 flush list 上最老的那个page(拥有最小的LSN)。由于采用WAL的策略,每次事务提交时需要持久化 redo log 才能保证事务不丢。而延迟刷脏页则起到了合并多次修改的效果,避免频繁写数据文件造成的性能问题。

Dirty page

在InnoDB中,buffer pool里面的dirty page一方面可以加快数据处理速度,同时也会造成数据的不一致(RAM vs DISK)

1、在对用户每次有导致数据变更的请求中,Innodb引擎把数据和索引都载入到内存中的缓冲池(buffer pool)中,如果每次修改数据和索引都需要更新到磁盘,必定会大大增加I/O请求

因为每次更新的位置都是随机的,磁头需要频繁定位导致效率低;数据暂放在内存中,也一定程度的提高了读的速度。所以Innodb每处理完一个请求(Transaction)后只添加一条日志log,另外有一个线程负责智能地读取日志文件并批量更新到磁盘上,实现最高效的磁盘写入。innodb既然利用Mem buffer提高相应的速度,那当然也会带来数据不一致,术语为脏数据,mysql称之为dirty page。

发生过程:当事务(Transaction)需要修改某条记录(row)时,InnoDB需要将该数据所在的page从disk读到buffer pool中,事务提交后,InnoDB修改page中的记录(row)。这时buffer pool中的page就已经和disk中的不一样了,mem中的数据称为脏数据(dirty page)。

2、在每次事务commit的时候,就立刻将事务更改操作记录到redo log。所以即使buffer pool中的dirty page在断电时丢失,InnoDB在启动时,仍然会根据redo log中的记录完成数据恢复,redo log的另一个作用是,通过延迟dirty page的flush最小化磁盘的random writes。(redo log会合并一段时间内TRX对某个page的修改)

3、正常情况下,dirty page什么时候flush到disk上

  • redo log是一个环(ring)结构,当redo空间占满时,将会将部分dirty page flush到disk上,然后释放部分redo log。这种情况可以通过Innodb_log_wait(SHOW GLOBAL STATUS)观察,情况发生该计数器会自增一次
  • 当需要在Buffer pool分配一个page,但是已经满了,并且所有的page都是dirty的(否则可以释放不dirty的page),通常是不会发生的。这时候必须flush dirty pages to disk。这种情况将会记录到Innodb_buffer_pool_wait_free中。一般地,可以可以通过启动参数innodb_max_dirty_pages_pct控制这种情况,当buffer pool中的dirty page到达这个比例的时候,将会强制设定一个checkpoint,并把dirty page flush到disk中
  • 检测到系统空闲的时候,会flush,每次64 pages

涉及的InnoDB配置参数:innodb_flush_log_at_trx_commit、innodb_max_dirty_pages_pct;状态参数:Innodb_log_wait、Innodb_buffer_pool_wait_free 

4、dirty page既然是在Buffer pool中,那么如果系统突然断电Dirty page中的数据修改是否会丢失

例如如果一个用户完成一个操作(数据库完成了一个事务,page已经在buffer pool中修改,但dirty page尚未flush),这时系统断电,buffer pool数据全部消失。那么这个用户完成的操作(导致的数据库修改)是否会丢失呢?答案是不会(innodb_flush_log_at_trx_commit=1)。这就是redo log要做的事情,在disk上记录更新(buffer pool中的数据并不是永久性)

系统故障造成数据库不一致的原因有两个:

  • 未完成事务对数据库的更新可能已写入数据库
  • 已提交事务对数据库的更新可能还留在缓冲区没来得及写入数据库

在这里我们先说恢复的一般方法:

  • 正向扫描日志文件(从头到尾),找出故障发生前已经提交的事务(存在begin transaction和commit记录),将其标识记入重做(redo)队列。同时找出故障发生时未完成的事务(只有begin transaction,没commit),将其标识记入(undo)队列
  • 对undo队列的各事务进行撤销处理。进行undo的处理方法是,反向扫描日志文件,对每个undo事务的更新操作执行反操作,即将日志记录中“更新前的值”写入数据库
  • 对重做日志中的各事务进行重做操作。进行redo的处理方法是,正向扫描日志,对每个redo事务重新执行日志文件登记操作。即将日志中“更新后的值”写入数据库

innodb_flush_log_at_trx_commit  

默认值1的意思是每一次事务提交或事务外的指令都需要把日志写入(flush)硬盘,这是很费时。特别是使用电池供电缓存(Battery backed up cache)时。设成2对于很多运用,特别是从MyISAM表转过来的是可以的,它的意思是不写入硬盘而是写入系统缓存。日志仍然会每秒flush到硬盘,所以一般不会丢失超过1-2秒的更新。设成0会更快一点,但安全方面比较差,即使MySQL挂了也可能会丢失事务的数据。而值2只会在整个操作系统挂了时才可能丢数据。 innodb_max_dirty_pages_pct

his is an integer in the range from 0 to 100. The default value is 90. The main thread in InnoDB tries to write pages from the buffer pool so that the percentage of dirty (not yet written) pages will not exceed this value. 

Log buffer

日志在内存里也是有缓存的,这里将其叫做log buffer。磁盘上的日志文件称为log file。log file一般是追加内容,可以认为是顺序写,顺序写的磁盘IO开销要小于随机写

ACID

原子性(Atomicity):事务中的所有操作,要么全部完成,要么不做任何操作,不能只做部分操作。如果在执行的过程中发生了错误,要回滚(Rollback)到事务开始前的状态,就像这个事务从来没有执行过

事务的持久性(Durability):事务一旦完成,该事务对数据库所做的所有修改都会持久的保存到数据库中。为了保证持久性,数据库系统会将修改后的数据完全的记录到持久的存储上

Undo log

Undo log是InnoDB MVCC事务特性的重要组成部分。当我们对记录做了变更操作时就会产生undo记录,Undo记录默认被记录到系统表空间(ibdata)中,但从5.6开始,也可以使用独立的Undo 表空间

Undo记录中存储的是老版本数据,当一个旧的事务需要读取数据时,为了能读取到老版本的数据,需要顺着undo链找到满足其可见性的记录。当版本链很长时,通常可以认为这是个比较耗时的操作(例如bug#69812)。

大多数对数据的变更操作包括INSERT/DELETE/UPDATE,其中INSERT操作在事务提交前只对当前事务可见,因此产生的Undo日志可以在事务提交后直接删除(谁会对刚插入的数据有可见性需求呢!!),而对于UPDATE/DELETE则需要维护多版本信息,在InnoDB里,UPDATE和DELETE操作产生的Undo日志被归成一类,即update_undo。

原理

Undo Log的原理很简单,为了满足事务的原子性,在操作任何数据之前,首先将数据备份到一个地方(这个存储数据备份的地方称为Undo Log)。然后进行数据的修改。如果出现了错误或者用户执行了ROLLBACK语句,系统可以利用Undo Log中的备份将数据恢复到事务开始之前的状态。

除了可以保证事务的原子性,Undo Log也可以用来辅助完成事务的持久化。

Undo Log 是为了实现事务的原子性,在MySQL数据库InnoDB存储引擎中,还用Undo Log来实现多版本并发控制(简称:MVCC)。

用Undo Log实现原子性和持久化的事务的简化过程

假设有A、B两个数据,值分别为1,2。

A.事务开始.B.记录A=1到undo log.C.修改A=3.D.记录B=2到undo log.E.修改B=4.F.将undo log写到磁盘。G.将数据写到磁盘。H.事务提交
例如:INSERT INTO JOBS VALUES(1,2,3)语句执行之后,log buffer将增加一个新的log记录,称为Log Record #5,它包含一个rowid和新记录的内容。同时,data buffer也将增加一个新行,但是,它会同时在页头标识:该页最新的log记录是Log Record #5。在这个例子中#5是Log Sequence Number(LSN),它对于接下来操作的时序安排是至关重要的。LOG FILE(S) AFTER WRITING LOG RECORD #5

 

海外公司注册、海外银行开户、跨境平台代入驻、VAT、EPR等知识和在线办理:https://www.xlkjsw.com

原标题:【msql】redo and undo log

关键词:sql

sql
*特别声明:以上内容来自于网络收集,著作权属原作者所有,如有侵权,请联系我们: admin#shaoqun.com (#换成@)。