文章目录
- 1.1 为什么需要redo
- 1.2 redo log 结构
- 1.3 刷盘规则
- 1.4 LSN
- 1.5 相关变量
1.1 为什么需要redo
数据库为了取得更好的读写性能,会将数据缓存在内存中,对磁盘数据的修改也会落后于内存,这时如果进程或机器崩溃,会导致内存数据丢失,为了保证数据库本身的一致性和持久性,InnoDB引入了redo log。修改数据页之前需要先将修改的内容记录到redo log中,并保证redo log早于对应的数据页落盘,也就是常说的预写式日志(WAL)。当故障发生导致内存数据丢失后,InnoDB会在重启时,通过重放REDO,将Page恢复到崩溃前的状态。
1.2 redo log 结构
redo log是物理日志,记录的是数据页的物理修改,用来恢复提交后的物理数据页。
redo log包括两部分:
- 内存中的redo log buffer(内存数据,会丢失);
- 磁盘上的redo log file(磁盘数据,持久化);
日志块
innodb存储引擎中,redo log以块为单位进行存储的,每个块占512字节,称为redo log block。所以不管是log buffer中还是os buffer中以及redo log file中,都是这样以512字节的块存储的,一个页内可以存放非常多的log block。
每个redo log block由3部分组成:日志块头、日志块尾和日志主体。其中日志块头占用12字节,日志块尾占用8字节,所以每个redo log block的日志主体部分只有512-12-8=492字节。
日志主体包含以下内容:
- redo_log_type:占用1个字节,表示redo log的日志类型。
- space:表示表空间的ID,采用压缩的方式后,占用的空间可能小于4字节。
- page_no:表示页的偏移量,同样是压缩过的。
- redo_log_body表示每个重做日志的数据部分,恢复时会调用相应的函数进行解析。
1.3 刷盘规则
内存中未刷到磁盘的数据称为脏数据。log buffer中未刷到磁盘的日志称为脏日志。由于数据和日志都以页的形式存在,所以脏页分为脏数据和脏日志。
redo 脏日志刷盘
- 按照innodb_flush_log_at_trx_commit设置刷盘
- 发出commit动作时。已经说明过,commit发出后是否刷日志由变量 innodb_flush_log_at_trx_commit 控制。
- 每秒刷一次。这个刷日志的频率由变量 innodb_flush_log_at_timeout 值决定,默认是1秒。要注意,这个刷日志频率和commit动作无关。
- 当log buffer中已经使用的内存超过一半时。
- 当有checkpoint时,checkpoint在一定程度上代表了刷到磁盘时日志所处的LSN位置。
脏数据页刷盘
在innodb中,数据刷盘依赖于checkpoint。但是触发checkpoint的情况却有几种。checkpoint触发后,会将buffer中脏数据页和脏日志页都刷到磁盘。
innodb存储引擎中checkpoint分为两种:
- sharp checkpoint:在重用redo log文件(例如切换日志文件)的时候,将所有已记录到redo log中对应的脏数据刷到磁盘。
- fuzzy checkpoint:一次只刷一小部分的日志到磁盘,而非将所有脏日志刷盘。有以下几种情况会触发该检查点:
- master thread checkpoint:由master线程控制,每秒或每10秒刷入一定比例的脏页到磁盘。
- flush_lru_list checkpoint:从MySQL5.6开始可通过 innodb_page_cleaners 变量指定专门负责脏页刷盘的page cleaner线程的个数,该线程的目的是为了保证lru列表有可用的空闲页。
- async/sync flush checkpoint:同步刷盘还是异步刷盘。例如还有非常多的脏页没刷到磁盘(非常多是多少,有比例控制),这时候会选择同步刷到磁盘,但这很少出现;如果脏页不是很多,可以选择异步刷到磁盘,如果脏页很少,可以暂时不刷脏页到磁盘
- dirty page too much checkpoint:脏页太多时强制触发检查点,目的是为了保证缓存有足够的空闲空间。too much的比例由变量 innodb_max_dirty_pages_pct 控制,MySQL 5.6默认的值为75,即当脏页占缓冲池的百分之75后,就强制刷一部分脏页到磁盘。
MySQL停止时是否将脏数据和脏日志刷入磁盘,由变量innodb_fast_shutdown={ 0|1|2 }控制,默认值为1,即停止时只做一部分purge,忽略大多数flush操作(但至少会刷日志),在下次启动的时候再flush剩余的内容,实现fast shutdown。
1.4 LSN
LSN称为日志的逻辑序列号(log sequence number),在innodb存储引擎中,lsn占用8个字节。LSN的值会随着日志的写入而逐渐增大。LSN不仅存在于redo log中,还存在于数据页中,在每个数据页的头部,有一个fil_page_lsn记录了当前页最终的LSN值是多少。通过数据页中的LSN值和redo log中的LSN值比较,如果页中的LSN值小于redo log中LSN值,则表示数据丢失了一部分,这时候可以通过redo log的记录来恢复到redo log中记录的LSN值时的状态。
show engine innodb status\G
---
LOG
---
#当前的log buffer中的lsn
Log sequence number 498732361
#Link_buf初始化的lsn
Log buffer assigned up to 498732361
#Link_buf完成的lsn
Log buffer completed up to 498732361
#到了这个lsn 为止, 之前的log buffer 里面都不会有空洞
Log written up to 498732361
刷到redo log file on disk中的lsn
Log flushed up to 498732361
#redo log 对应的dirty page 已经添加到buffer pool 的flush list
Added dirty pages up to 498732361
#已经刷到磁盘数据页上的LSN
Pages flushed up to 498732361
#上一次检查点所在位置的LSN
Last checkpoint at 498732361
1092 log i/o's done, 0.00 log i/o's/second
Log sequence number>=Log buffer assigned up to>=Log buffer completed up to>=Log written up to>=Log flushed up to>=Added dirty pages up to>=Pages flushed up to>=Last checkpoint at
1.5 相关变量
- innodb_flush_log_at_trx_commit指定何时将事务日志刷到磁盘,默认为1。
- 0表示每秒将"log buffer"同步到"os buffer"且从"os buffer"刷到磁盘日志文件中。
- 1表示每事务提交都将"log buffer"同步到"os buffer"且从"os buffer"刷到磁盘日志文件中。
- 2表示每事务提交都将"log buffer"同步到"os buffer"但每秒才从"os buffer"刷到磁盘日志文件中。
- innodb_flush_log_at_timeout 定义了每次日志刷新的时间,与innodb_flush_log_at_trx_commit 配合使用
- innodb_log_buffer_size表示log buffer的大小
- innodb_log_file_size事务日志的大小
- innodb_log_checksums表示在写入redo log到文件之前,redo log的每一个block都需要加上checksum校验位,以防止apply损坏redo log
- innodb_log_files_group事务日志组中的事务日志文件个数
- innodb_log_group_home_dir事务日志组路径,当前目录表示数据目录
- innodb_redo_log_archive_dirs是redo归档目录
- innodb_redo_log_encrypt表示是否加密