redo log write和flush
http://bbs.chinaunix.net/thread-1753130-1-1.html
在事务提交时innobase会调用ha_innodb.cc 中的innobase_commit,而innobase_commit通过调用trx_commit_complete_for_mysql(trx0trx.c)来调用log_write_up_to(log0log.c),也就是当innobase提交事务的时候就会调用log_write_up_to来写redo log
innobase_commit中
- if (all # 如果是事务提交
- || (!thd_test_options(thd, OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN))) {
复制代码
通过下面的代码实现事务的commit串行化
- if (innobase_commit_concurrency > 0) {
- pthread_mutex_lock(&commit_cond_m);
- commit_threads++;
- if (commit_threads > innobase_commit_concurrency) {
- commit_threads--;
- pthread_cond_wait(&commit_cond,
- &commit_cond_m);
- pthread_mutex_unlock(&commit_cond_m);
- goto retry;
- }
- else {
- pthread_mutex_unlock(&commit_cond_m);
- }
- }
复制代码
- trx->flush_log_later = TRUE; # 在做提交操作时禁止flush binlog 到磁盘
- innobase_commit_low(trx);
- trx->flush_log_later = FALSE;
复制代码
先略过innobase_commit_low调用 ,下面开始调用trx_commit_complete_for_mysql做write日志操作
- trx_commit_complete_for_mysql(trx); #开始flush log
- trx->active_trans = 0;
复制代码
在trx_commit_complete_for_mysql中,主要做的是对系统参数srv_flush_log_at_trx_commit值做判断来调用
log_write_up_to,或者write redo log file或者write&&flush to disk
- if (!trx->must_flush_log_later) {
- /* Do nothing */
- } else if (srv_flush_log_at_trx_commit == 0) { #flush_log_at_trx_commit=0,事务提交不写redo log
- /* Do nothing */
- } else if (srv_flush_log_at_trx_commit == 1) { #flush_log_at_trx_commit=1,事务提交写log并flush磁盘,如果flush方式不是SRV_UNIX_NOSYNC (这个不是很熟悉)
- if (srv_unix_file_flush_method == SRV_UNIX_NOSYNC) {
- /* Write the log but do not flush it to disk */
- log_write_up_to(lsn, LOG_WAIT_ONE_GROUP, FALSE);
- } else {
- /* Write the log to the log files AND flush them to
- disk */
- log_write_up_to(lsn, LOG_WAIT_ONE_GROUP, TRUE);
- }
- } else if (srv_flush_log_at_trx_commit == 2) { #如果是2,则只write到redo log
- /* Write the log but do not flush it to disk */
- log_write_up_to(lsn, LOG_WAIT_ONE_GROUP, FALSE);
- } else {
- ut_error;
- }
复制代码
那么下面看log_write_up_to
- if (flush_to_disk #如果flush到磁盘,则比较当前commit的lsn是否大于已经flush到磁盘的lsn
- && ut_dulint_cmp(log_sys->flushed_to_disk_lsn, lsn) >= 0) {
- mutex_exit(&(log_sys->mutex));
- return;
- }
- if (!flush_to_disk #如果不flush磁盘则比较当前commit的lsn是否大于已经写到所有redo log file的lsn,或者在只等一个group完成条件下是否大于已经写到某个redo file的lsn
- && (ut_dulint_cmp(log_sys->written_to_all_lsn, lsn) >= 0
- || (ut_dulint_cmp(log_sys->written_to_some_lsn, lsn)
- >= 0
- && wait != LOG_WAIT_ALL_GROUPS))) {
- mutex_exit(&(log_sys->mutex));
- return;
- }
- #下面的代码判断是否log在write,有的话等待其完成
- if (log_sys->n_pending_writes > 0) {
- if (flush_to_disk # 如果需要刷新到磁盘,如果正在flush的lsn包括了commit的lsn,只要等待操作完成就可以了
- && ut_dulint_cmp(log_sys->current_flush_lsn, lsn)
- >= 0) {
- goto do_waits;
- }
- if (!flush_to_disk # 如果是刷到redo log file的那么如果在write的lsn包括了commit的lsn,也只要等待就可以了
- && ut_dulint_cmp(log_sys->write_lsn, lsn) >= 0) {
- goto do_waits;
- }
- ......
- if (!flush_to_disk # 如果在当前IO空闲情况下 ,而且不需要flush到磁盘,那么 如果下次写的位置已经到达buf_free位置说明wirte操作都已经完成了,直接返回
- && log_sys->buf_free == log_sys->buf_next_to_write) {
- mutex_exit(&(log_sys->mutex));
- return;
- }
复制代码
下面取到group,设置相关write or flush相关字段,并且得到起始和结束位置的block号
- log_sys->n_pending_writes++;
- group = UT_LIST_GET_FIRST(log_sys->log_groups);
- group->n_pending_writes++; /* We assume here that we have only
- one log group! */
- os_event_reset(log_sys->no_flush_event);
- os_event_reset(log_sys->one_flushed_event);
- 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);
- ut_ad(area_end - area_start > 0);
- log_sys->write_lsn = log_sys->lsn;
- if (flush_to_disk) {
- log_sys->current_flush_lsn = log_sys->lsn;
- }
复制代码
log_block_set_checkpoint_no调用设置end_offset所在block的LOG_BLOCK_CHECKPOINT_NO为log_sys中下个检查点号
- 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);
复制代码
保存不属于end_offset但在其所在的block中的数据到下一个空闲的block
- ut_memcpy(log_sys->buf + area_end,
- log_sys->buf + area_end - OS_FILE_LOG_BLOCK_SIZE,
- OS_FILE_LOG_BLOCK_SIZE);
复制代码
对于每个group调用log_group_write_buf写redo log buffer
- while (group) {
- log_group_write_buf(
- group, log_sys->buf + area_start,
- area_end - area_start,
- ut_dulint_align_down(log_sys->written_to_all_lsn,
- OS_FILE_LOG_BLOCK_SIZE),
- start_offset - area_start);
- log_group_set_fields(group, log_sys->write_lsn); # 计算这次写的lsn和offset来设置group->lsn和group->lsn_offset
- group = UT_LIST_GET_NEXT(log_groups, group);
- }
- ......
- if (srv_unix_file_flush_method == SRV_UNIX_O_DSYNC) { # 这个是什么东西
- /* O_DSYNC means the OS did not buffer the log file at all:
- so we have also flushed to disk what we have written */
- log_sys->flushed_to_disk_lsn = log_sys->write_lsn;
- } else if (flush_to_disk) {
- group = UT_LIST_GET_FIRST(log_sys->log_groups);
- fil_flush(group->space_id); # 最后调用fil_flush执行flush到磁盘
- log_sys->flushed_to_disk_lsn = log_sys->write_lsn;
- }
复制代码
接下来看log_group_write_buf做了点什么
在log_group_calc_size_offset中,从group中取到上次记录的lsn位置(注意是log files组成的1个环状buffer),并计算这次的lsn相对于上次的差值
- # 调用log_group_calc_size_offset计算group->lsn_offset除去多个LOG_FILE头部长度后的大小,比如lsn_offset落在第3个log file上,那么需要减掉3*LOG_FILE_HDR_SIZE的大小
- gr_lsn_size_offset = (ib_longlong)
- log_group_calc_size_offset(group->lsn_offset, group);
- group_size = (ib_longlong) log_group_get_capacity(group); # 计算group除去所有LOG_FILE_HDR_SIZE长度后的DATA部分大小
- # 下面是典型的环状结构差值计算
- if (ut_dulint_cmp(lsn, gr_lsn) >= 0) {
- difference = (ib_longlong) ut_dulint_minus(lsn, gr_lsn);
- } else {
- difference = (ib_longlong) ut_dulint_minus(gr_lsn, lsn);
- difference = difference % group_size;
- difference = group_size - difference;
- }
- offset = (gr_lsn_size_offset + difference) % group_size;
- # 最后算上每个log file 头部大小,返回真实的offset
- return(log_group_calc_real_offset((ulint)offset, group));
复制代码
接着看
- # 如果需要写的内容超过一个文件大小
- if ((next_offset % group->file_size) + len > group->file_size) {
- write_len = group->file_size # 写到file末尾
- - (next_offset % group->file_size);
- } else {
- write_len = len; # 否者写len个block
- }
- # 最后真正的内容就是写buffer了,如果跨越file的话另外需要写file log file head部分
- if ((next_offset % group->file_size == LOG_FILE_HDR_SIZE)
- && write_header) {
- /* We start to write a new log file instance in the group */
- log_group_file_header_flush(group,
- next_offset / group->file_size,
- start_lsn);
- srv_os_log_written+= OS_FILE_LOG_BLOCK_SIZE;
- srv_log_writes++;
- }
- # 调用fil_io来执行buffer写
- if (log_do_write) {
- log_sys->n_log_ios++;
- srv_os_log_pending_writes++;
- fil_io(OS_FILE_WRITE | OS_FILE_LOG, TRUE, group->space_id,
- next_offset / UNIV_PAGE_SIZE,
- next_offset % UNIV_PAGE_SIZE, write_len, buf, group);
- srv_os_log_pending_writes--;
- srv_os_log_written+= write_len;
- srv_log_writes++;
- }
复制代码
redo log write和flush的更多相关文章
- Oracle Redo Log 机制 小结(转载)
Oracle 的Redo 机制DB的一个重要机制,理解这个机制对DBA来说也是非常重要,之前的Blog里也林林散散的写了一些,前些日子看老白日记里也有说明,所以结合老白日记里的内容,对oracle 的 ...
- mysql redo log
mysql> show variables like '%innodb_log_file_size%'; +----------------------+-----------+ | Varia ...
- 说说MySQL中的Redo log Undo log都在干啥
在数据库系统中,既有存放数据的文件,也有存放日志的文件.日志在内存中也是有缓存Log buffer,也有磁盘文件log file,本文主要描述存放日志的文件. MySQL中的日志文件, ...
- 详细分析MySQL事务日志(redo log和undo log)
innodb事务日志包括redo log和undo log.redo log是重做日志,提供前滚操作,undo log是回滚日志,提供回滚操作. undo log不是redo log的逆向过程,其实它 ...
- 【转】说说MySQL中的Redo log Undo log都在干啥
阅读目录(Content) 1 undo 1.1 undo是啥 1.2 undo参数 1.3 undo空间管理 2 redo 2.1 redo是啥 2.2 redo 参数 2.3 redo 空间管理 ...
- 【MySQL (六) | 详细分析MySQL事务日志redo log】
Reference: https://www.cnblogs.com/f-ck-need-u/archive/2018/05/08/9010872.html 引言 为了最大程度避免数据写入时 IO ...
- 详细分析MySQL事务日志(redo log和undo log) 表明了为何mysql不会丢数据
innodb事务日志包括redo log和undo log.redo log是重做日志,提供前滚操作,undo log是回滚日志,提供回滚操作. undo log不是redo log的逆向过程,其实它 ...
- mysql的undo log和redo log
在数据库系统中,既有存放数据的文件,也有存放日志的文件.日志在内存中也是有缓存Log buffer,也有磁盘文件log file,本文主要描述存放日志的文件. MySQL中的日志文件,有这么两 ...
- binlog和redo log日志提交
组提交(group commit)是MYSQL处理日志的一种优化方式,主要为了解决写日志时频繁刷磁盘的问题.组提交伴随着MYSQL的发展不断优化,从最初只支持redo log 组提交,到目前5.6官方 ...
随机推荐
- 《Python基础教程(第二版)》学习笔记 -> 第七章 更加抽象
对象的魔力 多态:意味着可以对不同类的对象使用同样的操作: 封装:对外部世界隐藏对象的工作细节: 继承:以普通的类为基础建立专门的类对象 多态① 多态和方法绑定到对象特性上面的函数称为方法(metho ...
- web自动化框架之三获取数据库值与界面值比较~~
数据库用到的是mysql,框架涉及数据库,主要包含两个方面,一个是每个案例执行完毕后,插入案例相关信息与数据:一个是web界面数据核对的时候,需要从sql中获取某行某列值与界面某个值做比较. 描述:w ...
- 关于java异常的一点思考
关于异常的一点思考 异常生命周期 异常的来源 所有的异常都是抛出来的 有底层api抛出的 有自定义抛出的 异常的处理 1, 运行时异常 不做任何处理仍可编译通过 不建议捕获(不建议用异常来做流程控制, ...
- 【原】Storm 入门教程目录
Storm入门教程 1. Storm基础 Storm Storm主要特点 Storm基本概念 Storm调度器 Storm配置 Guaranteeing Message Processing(消息处理 ...
- CentOS下编译安装hping3
安装hping之前,先装上libpcap-dev和tcl-dev 1.获取源码包 wget http://www.hping.org/hping3-20051105.tar.gz 2.解压,得到 hp ...
- [Hive - LanguageManual ] ]SQL Standard Based Hive Authorization
Status of Hive Authorization before Hive 0.13 SQL Standards Based Hive Authorization (New in Hive 0. ...
- Codevs No.1245 最小的N个和
2016-05-31 18:52:15 题目链接: 最小的N个和 Codevs No.1245 题目大意: 给两个等长数列,各取一个数求和,找到最小的N组 解法: 堆优化的大暴力 直接枚举所有可能在最 ...
- Mac下的截屏功能
全屏截图 对全屏的截图我们可以通过按 苹果键(花键)+Shift键+3来执行,之后伴随着清脆的一声提示音后,在桌面上就会生成一个图片文件,这就是刚刚截屏的图片了,默认文件类型是PNG的. 自定义截图 ...
- CodeForces 567A Gerald is into Art
http://codeforces.com/problemset/problem/567/A A. Lineland Mail time limit per test 3 seconds memory ...
- eclipse中的js文件报错的解决办法
在使用别人的项目的时候,导入到eclipse中发现js文件报错,解决办法是关闭eclipse的js校验功能. 三个步骤: 1. 右键点击项目->properties->Validation ...