mysql的文件组成以及IO操作的流程

摘要:
文件组成:主要包括数据文件、索引文件和日志文件(undo log、redlog、binlog),其中undo log和redlog归innodb存储引擎所有。IO进程:1.InnoDB引擎使用WAL技术(writeaahead log)。执行事务时,当写入内存和日志(undo log、redlog、binlog)时,事务就完成了。异步将内存数据刷新到磁盘。2.取消登录、重做登录、bi

文件组成:

主要包括数据文件、索引文件、日志文件(undolog、redolog、binlog),其中undolog和redolog是innodb存储引擎所有。

 

IO流程:

1.InnoDB引擎使用的是 WAL 技术(write ahead log),执行事务的时候,写完内存和日志(undolog、redolog、binlog),事务就算完成了。  异步刷内存数据到磁盘。

2.undolog、redolog、binlog比较

日志种类 存储内容作用
undologInnodb引擎逻辑日志,记录的是数据内容。提供回滚和多版本并发控制(MVCC)
redologInnodb引擎

物理日志,记录的是数据页的物理修改。(某个磁盘位置,修改为...)

机器崩溃恢复
binlogmysql逻辑日志,记录的是数据内容或者SQL语句。主从复制

3.innodb使用两阶段提交,保证数据一致性。 (保证redolog与binlog的逻辑一致性,进而保证主库与从库的数据一致性)

4.具体流程详见:

redolog-InnoDB 基本概念

redo log包括两部分:一是内存中的日志缓冲(redo log buffer),该部分日志是易失性的;二是磁盘上的重做日志文件(redo log file),该部分日志是持久的。

在概念上,innodb通过force log at commit机制实现事务的持久性,即在事务提交的时候,必须先将该事务的所有事务日志写入到磁盘上的redo log file中进行持久化。

为了确保每次日志都能写入到事务日志文件中,在每次将log buffer中的日志写入日志文件的过程中都会调用一次操作系统的fsync操作(即fsync()系统调用)。因为MySQL是工作在用户空间的,MariaDB/MySQL的log buffer处于用户空间的内存中。要写入到磁盘上的log file中(redo:ib_logfileN文件),中间还要经过操作系统内核空间的os buffer,调用fsync()的作用就是将OS buffer中的日志刷到磁盘上的log file中。

从redo log buffer写日志到磁盘的redo log file中,过程如下:

mysql的文件组成以及IO操作的流程第1张

MySQL支持用户自定义在commit时如何将log buffer中的日志刷log file中。这种控制通过变量 innodb_flush_log_at_trx_commit 的值来决定。该变量有3种值:0、1、2,默认为1。但注意,这个变量只是控制commit动作是否刷新log buffer到磁盘。

  • 当设置为1的时候,事务每次提交都会将log buffer中的日志写入os buffer并调用fsync()刷到log file on disk中。这种方式即使系统崩溃也不会丢失任何数据,但是因为每次提交都写入磁盘,IO的性能较差。

  • 当设置为0的时候,事务提交时不会将log buffer中日志写入到os buffer,而是每秒写入os buffer并调用fsync()写入到log file on disk中。也就是说设置为0时是(大约)每秒刷新写入到磁盘中的,当系统崩溃,会丢失1秒钟的数据。

  • 当设置为2的时候,每次提交都仅写入到os buffer,然后是每秒调用fsync()将os buffer中的日志写入到log file on disk。

尽管设置为0和2可以大幅度提升插入性能,但是在故障的时候可能会丢失1秒钟数据,更好的插入数据的做法是将值设置为1。

 
 

redolog-InnoDB 记录方式

  1. 当有一条记录需要更新的时候,InnoDB会先把记录写到redolog,并更新内存(buffer pool)

    • InnoDB会在适当的时候(例如系统空闲),将这个操作记录到磁盘里面(刷脏页)

  2. InnoDB的redolog是固定大小的,如果每个日志文件大小为1GB,4个日志文件为一组

    • redolog的总大小为4GB,循环写

    • write pos是当前记录的位置,一边写一边后移,写到3号文件末尾后就回到0号文件开头

      • redolog是顺序写,数据文件是随机写

    • checkpoint是当前要擦除的位置,擦除记录前需要先把对应的数据落盘(更新内存页,等待刷脏页)

    • write pos到checkpoint之间的部分可以用来记录新的操作

      • 如果write pos赶上了checkpoint,说明redolog已满,不能再执行新的更新操作,需要先推进checkpoint

      • 只要write pos未赶上checkpoint,就可以执行新的更新操作

    • checkpoint到write pos之间的部分等待落盘(先更新内存页,然后等待刷脏页)

      • 如果checkpoint赶上了write pos,说明redolog已空

  3. 有了redolog之后,InnoDB能保证数据库即使发生异常重启,之前提交的记录都不会丢失,达到crash-safe

  4. 如果redolog太小,会导致很快被写满,然后就不得不强行刷redolog,这样WAL机制的能力就无法发挥出来

mysql的文件组成以及IO操作的流程第2张

Q1、更新内存是更新什么,所有数据在内存中有一份,更新了便于后来的查找?

是内存页里的数据,更新的时候会先把数据页加载到内存里再更新

Q2、redo log是在内存中还是磁盘上?在mysql的内存中?

既然能保证数据库异常重启不丢失,redo log肯定是在磁盘上。

binlog-Server

  1. redolog是InnoDB特有的日志,binlog属于Server层日志

  2. 有两份日志的历史原因

    • 一开始并没有InnoDB,采用的是MyISAM,但MyISAM没有crash-safe的能力,binlog日志只能用于归档

    • InnoDB是以插件的形式引入MySQL的,为了实现crash-safe,InnoDB采用了redolog的方案

  3. binlog一开始的设计就是不支持崩溃恢复(原库)的,如果不考虑搭建从库等操作,binlog是可以关闭的(sql_log_bin)

  4. redolog vs binlog

    • redolog是InnoDB特有的,binlog是MySQL的Server层实现的,所有层都可以使用

    • redolog是物理日志,记录某个数据页上做了什么修改

      • binlog是逻辑日志,记录某个语句的原始逻辑

      • 逻辑日志:提供给别的引擎用,是大家都能理解的逻辑,例如搭建从库

      • 物理日志:只能内部使用,其他引擎无法共享内部的物理格式

    • redolog是循环写,空间固定,不能持久保存,没有归档功能

      • binlog是追加写,空间不受限制,有归档功能

    • redolog主要用于crash-safe,原库恢复

      • binlog主要用于恢复成临时库(从库)

    • 崩溃恢复的过程不写binlog(可能需要读binlog,如果binlog有打开,一般都会打开)

      • 用binlog恢复实例(从库),需要写redolog

  5. redolog对应用开发来说是透明的

  6. binlog有两种模式

    • statement格式:SQL语句

    • row格式:行内容(记两条,更新前和更新后),推荐

      • 日志一样的可以用于重放

update 内部流程

浅色框在InnoDB内部执行,深色框在执行器中执行(SQL: update T set c=c+1 where ID=2;)

mysql的文件组成以及IO操作的流程第3张

  1. 执行器先通过InnoDB获取id=2这一行,id是主键,InnoDB可以通过聚簇索引找到这一行

    • 如果id=2这一行所在的数据页本来就在内存(InnoDB Buffer Pool)中,直接返回给执行器

    • 否则先从磁盘读入内存,然后再返回

  2. 执行器拿到InnoDB返回的行数据,进行+1操作,得到新的一行数据,再调用InnoDB的引擎接口写入这行数据

  3. InnoDB首先将这行新数据更新到内存(InnoDB Buffer Pool)中,同时将这个更新操作记录到redolog(物理记录)

    • 更新到内存中,在事务提交后,后续的查询就可以直接在内存中读取该数据页,但此时的数据可能还没有真正落盘

      • 但在事务提交前,其他事务是无法看到这个内存修改的

      • 而在事务提交后,说明已经成功写入了redolog,可崩溃恢复,不会丢数据,因此可以直接读内存的数据

    • 刚更新的内存是不会删除的,除非内存不够用,在数据从内存删除之前,系统会保证它们已经落盘

    • 此时redolog处于prepare状态(prepare标签),然后告诉执行器执行完成,随时可以提交事务

      • 对其他事务来说,刚刚修改的内存是不可见的

  4. 执行器生成这个操作的binlog(逻辑记录)并写入磁盘

    • binlog写成功事务就算成功,可以提交事务

      • 哪怕崩溃恢复,也会恢复binlog写成功的事务(此时对应的redolog处于prepare状态)

    • binlog如果没写成功就回滚,回滚会写redolog,打上rollback标签,binlog则会直接丢弃

      • 如果binlog不丢弃,则会传播到从库

  5. 执行器调用InnoDB的提交事务接口,InnoDB把刚刚写入的redolog改成commit状态,更新完成

    • redolog打上了commit标签

    • commit表示两个日志都生效了

    • commit完成后才会返回客户端

 

两阶段提交

概述

  1. 目的:为了让redolog和binlog的逻辑一致

  2. 脑洞:假设不采用两阶段提交

    • 有两种顺序:redolog->binlog,或者binlog->redolog

    • 此时可以认为redolog和binlog完全独立

      • 崩溃恢复完全依赖于redolog(原库),恢复临时库完全依赖于binlog

    • 按照上面的两种顺序,都会导致redolog和binlog逻辑上的不一致

      • 假设原库crash后执行原库恢复+执行临时库恢复,恢复出来的数据是不一致的(主从不一致)

  3. 恢复清醒:两阶段提交(假设是双1的配置)

    • redolog prepare + binlog成功,提交事务,崩溃恢复后也会继续提交事务(redolog commit),逻辑一致

    • redolog prepare + binlog失败,回滚事务,崩溃恢复后也会继续回滚事务(redolog rollback),逻辑一致

      • 一个事务的binlog是有固定格式的

      • redolog与binlog是通过事务id(XID)进行关联的

      • 此时binglog中没有对应的记录,事务记录是不完整的

    • 崩溃恢复后是会从checkpoint开始往后主动刷数据

  4. 采用非双1的配置,在极端环境下会出现redolog与binlog不一致的情况

    • 优先保证redolog,先原库崩溃恢复,再处理从库(原库可以在崩溃恢复后,重新做一次全量备份,重建从库)

在两阶段提交的不同瞬间,Mysql发生异常重启,如何保证数据完整性?

mysql的文件组成以及IO操作的流程第4张

崩溃恢复时的判断规则:

1、如果redo log里的事务是完整的,也就是已经有了commit标识,直接提交

2、如果redo log里的事务只有完整的prepare,则判断对应的事务binlog是否存在且完整,是则提交否则回滚事务。

如果在A处出现crash:由于binlog还没写入,redo log还没提交,所以崩溃恢复时事务会回滚(删除redo log,内存回滚),可以保证数据一致性。

如果在B处出现crash:对应于上面第二种情况,如果binlog已经写完则提交,否则回滚。

F&Q

1、redo log和binlog是如何关联起来的?

它们有一个共同的数据字段:XID。崩溃恢复时会按顺序扫描redo log:

如果碰到既有prepare又有commit标识的redo log就直接提交

如果碰到只有prepare没有commit的redo log则拿着XID去binlog找对应的事务

2、处于prepare阶段的redo log+完整的binlog重启就能恢复,MYSQL为什么这样设计?

主要考虑数据一致性,在某一时刻,如果binlog写完后MYSQL发生崩溃,这时候binlog已经写入,之后会被从库(或用这个binlog恢复出的库)使用。

所以,在主库上也要提交这个事务。采用这个策略,主库和备库的数据就保证了一致性。

3、为什么是“两阶段提交”策略,先写完redo log,再写binlog,崩溃恢复的时候必须两个日志都完整才可以吗?

两阶段提交是经典的分布式系统问题,这么做主要考虑事务的持久性问题

对于InnoDB,如果redo log提交了那么事务就不能回滚了,如果redo log提交了binlog写入失败则数据又不一致了,

只要当前事务不提交它就是活跃的,对别的事务就是不可见的,一旦提交就可能落到后面启动的事务的可见区域里,提交后回滚可能覆盖掉别的事务的更新

两阶段提交是给所有人一个机会,当所有人都说“ok”的时候再一起提交。

4、不引入两个日志,也就没有两阶段提交的必要了。只用binlog支持崩溃恢复和归档可以吗?

历史层面:InnoDB不是MYSQL原生引擎,在加入MYSQL之前就已经有了redo log支持崩溃恢复和事务

实现层面:下图是只用binlog的示意图

mysql的文件组成以及IO操作的流程第5张

这样的流程下,binlog 还是不能支持崩溃恢复的。

说一个不支持的点吧:binlog 没有能力恢复“数据页”。

如果在图中标的位置,也就是 binlog2 写完了,但是整个事务还没有 commit 的时候,MySQL 发生了 crash。

重启后,引擎内部事务 2 会回滚,然后应用 binlog2 可以补回来;但是对于事务 1 来说,系统已经认为提交完成了,不会再应用一次 binlog1。

但是,InnoDB 引擎使用的是 WAL 技术,执行事务的时候,写完内存和日志,事务就算完成了。如果之后崩溃,要依赖于日志来恢复数据页。

也就是说在图中这个位置发生崩溃的话,事务 1 也是可能丢失了的,而且是数据页级的丢失。此时,binlog 里面并没有记录数据页的更新细节,是补不回来的。

你如果要说,那我优化一下 binlog 的内容,让它来记录数据页的更改可以吗?但,这其实就是又做了一个 redo log 出来。

5、那可以只要redo log 不要binlog吗?

只从崩溃恢复的角度讲是可以的,关闭binlog依然是crash-safe的。

实际生产环境binlog都会是开着的,一个是归档,redo log无法记录历史日志,另外MYSQL系统依赖于binlog,比如高可用的基础就是binlog复制。

6、redo log一般设置多大?

对于常见的几个TB的硬盘,设置4个文件,每个1GB

7、正常运行的实例,数据写入后的最终落盘是从redo log更新过来的还是从buffer pool更新过来的?

redo log并没有记录数据页的完整数据,所以它没有能力自己去更新磁盘数据页

1、如果是正常运行的实例,数据页被修改后跟磁盘的数据页不一样称为脏页,最终落盘就是把内存中的数据页写入磁盘,也就是刷脏页(flush),这个过程跟redo log毫无关系

2、在崩溃恢复场景中,InnoDB如果判断到一个数据页可能在崩溃恢复时丢失了更新,就会将它读到内存然后用redo log更新内存内容,更新完成后内存就变成了上述情况的脏页。

8、redo log buffer是什么?是先修改内存还是先写redo log文件?

在一个事务的更新过程中,日志是要写多次的,如:

begin;

insert into t1 ...

insert into t2 ...

commit;

插入数据的过程中生成的日志都要先保存起来,但又不能在还没执行commit操作时直接写到redo log文件中

所以redo log buffer是一块内存,用来先存redo 日志的,也就是在执行第一个insert时数据内存被修改了,redo log buffer也写入了日志

真正把日志写到redo log文件(文件名是ib_logfile+数字)是在执行commit语句(不是commit小步骤)时做的。

免责声明:文章转载自《mysql的文件组成以及IO操作的流程》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇PCL常见编程问题Lucene之索引库的维护:添加,删除,修改下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

大数据 CDH 6.2 安装

环境信息 如果是单节点的,建议将虚拟机的内存最小设置为8G,硬盘大小为100G 类别 版本 备注 CDH 6.2.0 下载网址 数据库 mysql 5.7 系统 CentOS7 jdk 1.8 cdh 修改过的 parcels 下载网址 CDH6.2.0中的软件版本信息 Component Component Ve...

MySQL Access denied for user root@localhost 解决方法

今天把本地开发的项目(ThinkPHP 3.2.3)移到虚拟机上(CentOS 6.6,LNMP 1.2,MySQL 5.6.23),配置好 MySQL 之后访问首页,出现: 此时 MySQL 的用户名和密码都是 root 解决方法是: # /etc/init.d/mysql stop # mysqld_safe --user=mysql --skip...

如何修改数据库密码

mysql 忘记了管理员密码解决方法,修改数据库密码的几种方法 如何修改数据库密码 方法 1: 用 SET PASSWORD 命令 首先登录 MySQL。 格式:mysql> set password for 用户名 @localhost = password('新密码'); 例子:mysql> set password for root@lo...

Oracle表中一行记录被锁(行锁,表锁,死锁)

表现形式:可以向表里面save新数据,但是无法跟新某一条数据,update的时候就一直在等待。 Oracle锁表查询和解锁方法 数据库操作语句的分类DDL:数据库模式定义语言,关键字:createDML:数据操纵语言,关键字:Insert、delete、updateDCL:数据库控制语言 ,关键字:grant、removeDQL:数据库查询语言,关键字:s...

zabbix 安装

1、在已有的LAMP或者LNMP的基础上安装zabbix,安装一些依赖包: yum -y install mysql-devel libcurl-devel net-snmp-devel 2、添加用户: groupadd zabbixuseradd zabbix -g zabbix 3、创建数据库,添加授权账号 mysql -uxxx  -pxxx my...

Java连接MySQL数据库实现用户名密码的验证方法 Java语句中sql查询语句'' ""作用

//方法一,可以验证登录,但方法不实用。packagecom.swift; importjava.sql.Connection; importjava.sql.DriverManager; importjava.sql.ResultSet; importjava.sql.SQLException; importjava.sql.Statement...