Innodb log sys 相关的一些随笔

随笔记录,比较凌乱

A.log_sys的几个和log buffer相关的变量

buf_size:

     log_sys->buf_size = LOG_BUFFER_SIZE;   

     LOG_BUFFER_SIZE为srv_log_buffer_size * UNIV_PAGE_SIZE, 也就是变量innodb_log_buffer_size的大小

buf_free

       指向当前在log buf中可写入的起始偏移量

max_buf_free

        log_sys->max_buf_free = log_sys->buf_size / LOG_BUF_FLUSH_RATIO

                – LOG_BUF_FLUSH_MARGIN;

#define LOG_BUF_WRITE_MARGIN    (4 * OS_FILE_LOG_BLOCK_SIZE) 

#define LOG_BUF_FLUSH_RATIO     2

#define LOG_BUF_FLUSH_MARGIN    (LOG_BUF_WRITE_MARGIN + 4 * UNIV_PAGE_SIZE)

log_sys->max_buf_free主要用于:

1.当当前已经写入文件的位置(log_sys->write_end_offset)在buf中的log_sys/max_buf_free的二分之一时,也就是差不多1/4的log buffer的位置时,会做一次memmove,将从log_sys->write_end_offset到log_sys->buf_free之间以512字节的block为单位进行memmove到log buf的起始部分。同时修改log_sys->buf_free 和log_sys->buf_next_to_write这两个变量向前移动。

2.在写入一个mtr log后(log buf中最后一个未写满的block无法放入mtr log时),调用log_close时,会去看log_sys->buf_free是否大于max_buf_free

如果大于,则将log_sys->check_flush_or_checkpoint设置为TRUE,这可能会影响到DML操作检查redo空间的行为(log_free_check)

check_flush_or_checkpoint

    当这个变量设置为TRUE时,就是告诉用户线程,这时候log buf 可能太小了,需要去做一次flush,另外也会检查log file是否已经到某个临界点了(async/sync),可能需要去刷脏页,具体见函数log_free_check

buf_next_to_write

     下一次开始写入文件的位置,当需要将buf写文件时,这个变量用于决定写入的起始位置,及起始对齐的block。

      该变量的判断及赋值主要在log_write_up_to函数中

write_end_offset

      在准备将buf写入文件时,最后一个预备写入的Block的数据会向后拷贝一个block(512字节),write_end_offset指向新的block中数据的偏移量位置

write_lsn:

       写入的lsn,每次在准备将log buf的数据拷贝到文件之前,将其设置为log_sys->write_lsn = log_sys->lsn;

        因此write_lsn总是>= written_to_some_lsn 和written_to_all_lsn,前者可能已经设置了,但还没写入到该lsn,后两者表示已经写到文件的lsn

written_to_some_lsn

      在完成一次写入后,将其值设置为write_lsn,表示当前写入文件的lsn (log_group_check_flush_completion)

       在进入log_write_up_to函数时会根据该值判断是否需要进行写入 

     

written_to_all_lsn

       和written_to_some_lsn类似,同样在完成写入后,将其设置为write_lsn

current_flush_lsn

       和write_lsn类似,在将buf写入文件之前,如果flush_to_disk为true,则将其设置为log_sys->lsn;可以理解为当前正在flush的lsn,他的值总是>= flushed_to_disk_lsn

flushed_to_disk_lsn

        在完成写入后,会尝试将文件刷到磁盘(如果flush_to_disk为TRUE),完成后,将flushed_to_disk_lsn设置为当前写到的lsn(log_sys->write_lsn)

n_pending_writes

        等于时,表示当前已经有拷贝buf到文件正在进行,这种情况下,不允许同时发生写入操作,其他需要等待

one_flushed

     在开始将log buf写到文件之前设置为FALSE,在写入完成,以及完成fsync(如果需要)后,再调用函数log_group_check_flush_completion将其设置为TRUE.

 

B.

除了redo真正的日志外,每个写入磁盘的数据是以block为单位的,一个block默认为512字节,在block头部,有12个字节来维护该block的相关信息(LOG_BLOCK_HDR_SIZE),主要包括:

LOG_BLOCK_HDR_NO

该block的block no,每次初始化一个新的block时

会去计算,根据当前log_sys->lsn计算,保持递增

LOG_BLOCK_HDR_DATA_LEN

当前block中redo log的长度,包括头部12字节

LOG_BLOCK_FIRST_REC_GROUP

当该值为0时,表示这个block为一个mtr日志的一部分

否则,该值为该block中的第一个mtr的偏移量

LOG_BLOCK_CHECKPOINT_NO

在写满一个block/写buf到文件时,设置该值为当前

log_sys->next_checkpoint_no

      

 

C 拷贝mlog到log buf中

mtr_commit->mtr_log_reserve_and_write

有两种拷贝日志到buf的场景:

a.首先判断mlog->heap == NULL,这时候认为日志较小

对于比较小的log,进入函数log_reserve_and_write_fast

1)持有log_sys->mutex

log_sys->buf_free % OS_FILE_LOG_BLOCK_SIZE+len超过OS_FILE_LOG_BLOCK_SIZE – LOG_BLOCK_TRL_SIZE的话,

表示写入这个日志可能产生跨块(512字节),这时候释放log_sys->mutex,并直接返回

2)

否则把这个log拷贝到,推进log_sys变量:

log_sys->buf_free += len;

log_sys->lsn += len;

返回拷贝数据后的log_sys->lsn

这里的len就是日志的实际长度;

当无法进行fast copy时,就进入第二种方式

b.

首先计算日志数据的长度data_size,然后根据data_size来确定一个最大上限值,用于判断当前的buf里是否有足够的空闲空间(log_reserve_and_open(data_size))

len_upper_limit = LOG_BUF_WRITE_MARGIN + (5 * len) / 4;

其中LOG_BUF_WRITE_MARGIN为4*512字节

当剩余空间小于这个值时,就需要将buf刷到磁盘上(log_buffer_flush_to_disk();) 

这里的上限只是一个粗略的值;

遍历mtr->log,这是一个dyn_block_t数组,依次写入buf中(log_write_low),对于每一个mtr日志块:

这里可能产生多次拷贝

第一次拷贝时,如果log buf中最后一个已用的512字节的buf剩余空间无法存放这个block,就先拷贝一部分数据,将最后一个block占满(但预留4个字节)

实际上一个512字节的block中,需要首尾都预留空间,头部12字节,尾部4字节,

我们假定最后一个512字节的log buf 块中还剩下100字节可用, 即log_sys->buf_free%OS_FILE_LOG_BLOCK_SIZE = 412

假定这时候log_sys->lsn = 0; 我们要写入的日志块大小为1000字节

第一轮:

拷贝长度, len = 512-412-4 = 96字节

由于该buf block已经满了,所以我们推进lsn到下一个block,并跳过下一个block的头部12个保留字节

log_sys->lsn = 96+4+12 = 112

log_sys->buf_free也推进到下一个block的头部位置

日志数据剩余长度为1000-96=904字节

第二轮:

拷贝长度, len = 512-12-4=496字节

该block拷满,同样推进到下一个block

log_sys->lsn = 112 + 500 + 12 = 624

log_sys->buf_free %OS_FILE_LOG_BLOCK_SIZE = 12

剩余字节为904-496 =  408

第三轮

由于可用长度为512-12-4=496大于408,因此该block可以完成拷贝,拷贝数据长度为408

log_sys->lsn = 624+408 =  1032

log_sys->buf_free %OS_FILE_LOG_BLOCK_SIZE = 12+408 = 420

可见,我们并不能单独的计算一个日志块占用的block size,因为log_sys->buf_free的值可能被其他线程占用;

但我们可以占用log_sys->mutex并快速计算出占用log buf的长度

buf_free%OS_FILE_LOG_BLOCK_SIZE

a = 512 – buf_free%512 – 4 

if (data_size > a)

do

   n = (data_size -a )/(512-4-12) = (data_size -a )/496

   extra = (data_size -a )%496

done

len = (512 – buf_free%512) + n*512 +   extra +12

如上例,

a = 512-412-4 = 96

n = (1000-96)/496 = 1

extra = (1000-96)%496 = 408

len = (512-412) + 1*512 + 408 +12 = 1032

注意在拷贝数据的过程中,会去设置一个block的长度,在block头的第4位会设置这个block的长度,例如:

301         log_block = static_cast<byte*>(

302                 ut_align_down(

303                         log->buf + log->buf_free, OS_FILE_LOG_BLOCK_SIZE));

304

305         log_block_set_data_len(log_block, data_len);

拷贝完成数据后,这时候是持有log_sys->mutex的.

对于第一种拷贝方式,直接调用mtr_add_dirtied_pages_to_flush_list(mtr);

对于第二种拷贝方式,需要先调用log_close(),再调用mtr_add_dirtied_pages_to_flush_list(mtr)

mtr_close的返回值赋予mtr->end_lsn。对于一个mtr日志数组的最后占用的一个block,需要设置log_block_set_first_rec_group

由于持有log sys mutex,因此这里设置的是当前mtr在最后一个512字节block占用的长度;这部分可以并发来做

另外一个工作就是判断是否需要设置check_flush_or_checkpoint

mtr_close还会去调用buf_pool_get_oldest_modification,这里会去获取log_flush_order_mutex

在完成上述工作后,开始将脏页转移到flush list中,mtr_add_dirtied_pages_to_flush_list

这里会先获取log_flush_order_mutex 再去释放log_sys->mutex,然后将mtr修改的脏页加入到flush list中;

完成将脏页加入到lflush list后,释放log_flush_order_mutex锁。

D.将数据从buf写入到文件中

log_write_up_to是主要函数,这里只讨论拷贝buf的部分逻辑

持有log_sys->mutex

每次写入的起始点和结束点:

        start_offset = log_sys->buf_next_to_write;

end_offset = log_sys->buf_free;

        area_start = ut_calc_align_down(start_offset, OS_FILE_LOG_BLOCK_SIZE);

area_end = ut_calc_align(end_offset, OS_FILE_LOG_BLOCK_SIZE);

可见,如果需要刷buf到文件时,参数lsn只是用于判断是否已经有其他线程正在刷的LSN超过了传递的LSN,如果超过了,那么等待别的线程完成或者直接返回

决定了起始和终止的block后,设置log_sys->write_lsn或者log_sys->current_flush_lsn(如果参数flush_to_disk为TRUE时)为当前log_sys->lsn         

 

设置起始block和终止block的block头部对应位:

        log_block_set_flush_bit(log_sys->buf + area_start, TRUE);

log_block_set_checkpoint_no(

log_sys->buf + area_end – OS_FILE_LOG_BLOCK_SIZE,

log_sys->next_checkpoint_no);

将最后一个block拷贝到下一个block

        ut_memcpy(log_sys->buf + area_end,

log_sys->buf + area_end – OS_FILE_LOG_BLOCK_SIZE,

OS_FILE_LOG_BLOCK_SIZE);        log_sys->buf_free += OS_FILE_LOG_BLOCK_SIZE;

log_sys->write_end_offset = log_sys->buf_free;

将数据写入文件:

                 log_group_write_buf(

group, log_sys->buf + area_start,

area_end – area_start,

ut_uint64_align_down(log_sys->written_to_all_lsn,

OS_FILE_LOG_BLOCK_SIZE),

start_offset – area_start);

在函数log_group_write_buf中,还涉及到很多操作,例如计算每个512字节block的checksum等

完成写入后,即释放log_sys->mutex 

如果需要,做fil_flush刷磁盘操作,将日志落地

再次持有log_sys->mutex

unlock = log_group_check_flush_completion(group);

—>log_sys->written_to_some_lsn = log_sys->write_lsn;

—>log_sys->one_flushed = TRUE;

unlock = unlock | log_sys_check_flush_completion();

函数log_sys_check_flush_completion中,设置:

      a.

      log_sys->written_to_all_lsn = log_sys->write_lsn;

      log_sys->buf_next_to_write = log_sys->write_end_offset;

      b.

      当 log_sys->write_end_offset大于max_buf_free的一半时,将buffer内后面的内容整体向前挪动 

                        move_start = ut_calc_align_down(

                                log_sys->write_end_offset,

OS_FILE_LOG_BLOCK_SIZE);

move_end = ut_calc_align(log_sys->buf_free,

OS_FILE_LOG_BLOCK_SIZE);

ut_memmove(log_sys->buf, log_sys->buf + move_start,

move_end – move_start);

log_sys->buf_free -= move_start;

log_sys->buf_next_to_write -= move_start;

释放log_sys->mutex

时间: 2024-09-29 20:22:10

Innodb log sys 相关的一些随笔的相关文章

联机日志文件过小引发的log file 相关等待

      Oracle 联机重做日志文件记录了数据库的所有变化(DML,DDL或管理员对数据所作的结构性更改等),用于对于意外删除或宕机利用日志文件实现数据恢复来确保数据的完整性.但不合理的联机日志文件规划将引发日志相关的等待事件.下面是这样一个来自生产环境中的例子.   1.故障描述 --客户描述该数据库晚上用于实现数据同步以及汇总,以前一直工作的比较良好,随着需要同步的数量量的增大,最近变得越来越慢. --下面我们首先取了客户晚8点至第二天7点的awr report. WORKLOAD R

innodb buffer pool相关特性

背景 innodb buffer pool作为innodb最重要的缓存,其缓存命中率的高低会直接影响数据库的性能.因此在数据库发生变更,比如重启.主备切换实例迁移等等,innodb buffer poll 需要一段时间预热,期间数据库的性能会受到明显影响. 另外mysql 5.7以前innodb buffer pool缓存大小修改不是动态的,重启才能生效.因此innodb buffer pool的预热和innodb buffer pool大小的动态修改,对性能要求较高的应用来说是不错的特性,下面

MySQL · 特性分析 · innodb buffer pool相关特性

背景 innodb buffer pool做为innodb最重要的缓存,其缓存命中率的高低会直接影响数据库的性能.因此在数据库发生变更,比如重启.主备切换实例迁移等等,innodb buffer poll 需要一段时间预热,期间数据库的性能会受到明显影响. 另外mysql 5.7以前innodb buffer pool缓存大小修改不是动态的,重启才能生效.因此innodb buffer pool的预热和innodb buffer pool大小的动态修改,对性能要求较高的应用来说是不错的特性,下面

Innodb drop index 流程小记

最近在做一个小特性,让InnoDB支持repair table来重建corrupted的索引,本文描述的是实现过程中遇到的一个小问题,主要和innodb drop index相关的一些随笔记录.   现象:针对损坏的page,加上一个标记位为corrupted,然而在删除索引并重建后,该标记未被清除掉,但是我在读取page时已经加了清理标记的操作(buf_page_init_low).   drop index流程: a. ha_innobase::prepare_inplace_alter_t

MySQL · 引擎特性 · InnoDB redo log漫游

前言 InnoDB 有两块非常重要的日志,一个是undo log,另外一个是redo log,前者用来保证事务的原子性以及InnoDB的MVCC,后者用来保证事务的持久性. 和大多数关系型数据库一样,InnoDB记录了对数据文件的物理更改,并保证总是日志先行,也就是所谓的WAL,即在持久化数据文件前,保证之前的redo日志已经写到磁盘. LSN(log sequence number) 用于记录日志序号,它是一个不断递增的 unsigned long long 类型整数.在 InnoDB 的日志

数据库内核月报 - 2015 / 05-MySQL · 引擎特性 · InnoDB redo log漫游

前言 InnoDB 有两块非常重要的日志,一个是undo log,另外一个是redo log,前者用来保证事务的原子性以及InnoDB的MVCC,后者用来保证事务的持久性. 和大多数关系型数据库一样,InnoDB记录了对数据文件的物理更改,并保证总是日志先行,也就是所谓的WAL,即在持久化数据文件前,保证之前的redo日志已经写到磁盘. LSN(log sequence number) 用于记录日志序号,它是一个不断递增的 unsigned long long 类型整数.在 InnoDB 的日志

InnoDB 中文参考手册 --- 2 InnoDB 启动选项

参考|参考手册|中文 InnoDB 中文参考手册 --- 犬犬(心帆)翻译 2 InnoDB 启动选项为了在 MySQL-Max-3.23 中使用 InnoDB 表,你必须在配置文件'my.cnf'或'my.ini'(WINDOWS系统)中的 [mysqld] 区中详细指定配置参数. 作为最小设置,在 3.23 中你必须在 innodb_data_file_path 上指定数据文件名能及大小.如果在'my.cnf'中没有指定innodb_data_home_dir,系统将在 MySQL 的 da

MySQL · 引擎特性 · InnoDB 崩溃恢复过程

在前面两篇文章中,我们详细介绍了 InnoDB redo log 和 undo log 的相关知识,本文将介绍 InnoDB 在崩溃恢复时的主要流程. 本文代码分析基于 MySQL 5.7.7-RC 版本,函数入口为 innobase_start_or_create_for_mysql,这是一个非常冗长的函数,本文只涉及和崩溃恢复相关的代码. 在阅读本文前,强烈建议翻阅下面两篇文章: 1. MySQL · 引擎特性 · InnoDB undo log 漫游 2. MySQL · 引擎特性 · I

MySQL · 引擎特性 · InnoDB 事务子系统介绍

前言 在前面几期关于 InnoDB Redo 和 Undo 实现的铺垫后,本节我们从上层的角度来阐述 InnoDB 的事务子系统是如何实现的,涉及的内容包括:InnoDB的事务相关模块.如何实现MVCC及ACID.如何进行事务的并发控制.事务系统如何进行管理等相关知识.本文的目的是让读者对事务系统有一个较全面的理解. 由于不同版本对事务系统都有改变,本文的所有分析基于当前GA的最新版本MySQL5.7.9,但也会在阐述的过程中,顺带描述之前版本的一些内容.本文也会介绍5.7版本对事务系统的一些优