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中

  1. if (all              # 如果是事务提交
  2. || (!thd_test_options(thd, OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN))) {

复制代码

通过下面的代码实现事务的commit串行化

  1. if (innobase_commit_concurrency > 0) {
  2. pthread_mutex_lock(&commit_cond_m);
  3. commit_threads++;
  4. if (commit_threads > innobase_commit_concurrency) {
  5. commit_threads--;
  6. pthread_cond_wait(&commit_cond,
  7. &commit_cond_m);
  8. pthread_mutex_unlock(&commit_cond_m);
  9. goto retry;
  10. }
  11. else {
  12. pthread_mutex_unlock(&commit_cond_m);
  13. }
  14. }

复制代码

  1. trx->flush_log_later = TRUE;   # 在做提交操作时禁止flush binlog 到磁盘
  2. innobase_commit_low(trx);
  3. trx->flush_log_later = FALSE;

复制代码

先略过innobase_commit_low调用 ,下面开始调用trx_commit_complete_for_mysql做write日志操作

  1. trx_commit_complete_for_mysql(trx);  #开始flush  log
  2. 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

  1. if (!trx->must_flush_log_later) {
  2. /* Do nothing */
  3. } else if (srv_flush_log_at_trx_commit == 0) {  #flush_log_at_trx_commit=0,事务提交不写redo log
  4. /* Do nothing */
  5. } else if (srv_flush_log_at_trx_commit == 1) { #flush_log_at_trx_commit=1,事务提交写log并flush磁盘,如果flush方式不是SRV_UNIX_NOSYNC (这个不是很熟悉)
  6. if (srv_unix_file_flush_method == SRV_UNIX_NOSYNC) {
  7. /* Write the log but do not flush it to disk */
  8. log_write_up_to(lsn, LOG_WAIT_ONE_GROUP, FALSE);
  9. } else {
  10. /* Write the log to the log files AND flush them to
  11. disk */
  12. log_write_up_to(lsn, LOG_WAIT_ONE_GROUP, TRUE);
  13. }
  14. } else if (srv_flush_log_at_trx_commit == 2) {   #如果是2,则只write到redo log
  15. /* Write the log but do not flush it to disk */
  16. log_write_up_to(lsn, LOG_WAIT_ONE_GROUP, FALSE);
  17. } else {
  18. ut_error;
  19. }

复制代码

那么下面看log_write_up_to

  1. if (flush_to_disk              #如果flush到磁盘,则比较当前commit的lsn是否大于已经flush到磁盘的lsn
  2. && ut_dulint_cmp(log_sys->flushed_to_disk_lsn, lsn) >= 0) {
  3. mutex_exit(&(log_sys->mutex));
  4. return;
  5. }
  6. if (!flush_to_disk       #如果不flush磁盘则比较当前commit的lsn是否大于已经写到所有redo log file的lsn,或者在只等一个group完成条件下是否大于已经写到某个redo file的lsn
  7. && (ut_dulint_cmp(log_sys->written_to_all_lsn, lsn) >= 0
  8. || (ut_dulint_cmp(log_sys->written_to_some_lsn, lsn)
  9. >= 0
  10. && wait != LOG_WAIT_ALL_GROUPS))) {
  11. mutex_exit(&(log_sys->mutex));
  12. return;
  13. }
  14. #下面的代码判断是否log在write,有的话等待其完成
  15. if (log_sys->n_pending_writes > 0) {
  16. if (flush_to_disk    # 如果需要刷新到磁盘,如果正在flush的lsn包括了commit的lsn,只要等待操作完成就可以了
  17. && ut_dulint_cmp(log_sys->current_flush_lsn, lsn)
  18. >= 0) {
  19. goto do_waits;
  20. }
  21. if (!flush_to_disk  # 如果是刷到redo log file的那么如果在write的lsn包括了commit的lsn,也只要等待就可以了
  22. && ut_dulint_cmp(log_sys->write_lsn, lsn) >= 0) {
  23. goto do_waits;
  24. }
  25. ......
  26. if (!flush_to_disk  # 如果在当前IO空闲情况下 ,而且不需要flush到磁盘,那么 如果下次写的位置已经到达buf_free位置说明wirte操作都已经完成了,直接返回
  27. && log_sys->buf_free == log_sys->buf_next_to_write) {
  28. mutex_exit(&(log_sys->mutex));
  29. return;
  30. }

复制代码

下面取到group,设置相关write or flush相关字段,并且得到起始和结束位置的block号

  1. log_sys->n_pending_writes++;
  2. group = UT_LIST_GET_FIRST(log_sys->log_groups);
  3. group->n_pending_writes++;      /* We assume here that we have only
  4. one log group! */
  5. os_event_reset(log_sys->no_flush_event);
  6. os_event_reset(log_sys->one_flushed_event);
  7. start_offset = log_sys->buf_next_to_write;
  8. end_offset = log_sys->buf_free;
  9. area_start = ut_calc_align_down(start_offset, OS_FILE_LOG_BLOCK_SIZE);
  10. area_end = ut_calc_align(end_offset, OS_FILE_LOG_BLOCK_SIZE);
  11. ut_ad(area_end - area_start > 0);
  12. log_sys->write_lsn = log_sys->lsn;
  13. if (flush_to_disk) {
  14. log_sys->current_flush_lsn = log_sys->lsn;
  15. }

复制代码

log_block_set_checkpoint_no调用设置end_offset所在block的LOG_BLOCK_CHECKPOINT_NO为log_sys中下个检查点号

  1. log_block_set_flush_bit(log_sys->buf + area_start, TRUE);   # 这个没看明白
  2. log_block_set_checkpoint_no(
  3. log_sys->buf + area_end - OS_FILE_LOG_BLOCK_SIZE,
  4. log_sys->next_checkpoint_no);

复制代码

保存不属于end_offset但在其所在的block中的数据到下一个空闲的block

  1. ut_memcpy(log_sys->buf + area_end,
  2. log_sys->buf + area_end - OS_FILE_LOG_BLOCK_SIZE,
  3. OS_FILE_LOG_BLOCK_SIZE);

复制代码

对于每个group调用log_group_write_buf写redo log buffer

  1. while (group) {
  2. log_group_write_buf(
  3. group, log_sys->buf + area_start,
  4. area_end - area_start,
  5. ut_dulint_align_down(log_sys->written_to_all_lsn,
  6. OS_FILE_LOG_BLOCK_SIZE),
  7. start_offset - area_start);
  8. log_group_set_fields(group, log_sys->write_lsn);   # 计算这次写的lsn和offset来设置group->lsn和group->lsn_offset
  9. group = UT_LIST_GET_NEXT(log_groups, group);
  10. }
  11. ......
  12. if (srv_unix_file_flush_method == SRV_UNIX_O_DSYNC) {  # 这个是什么东西
  13. /* O_DSYNC means the OS did not buffer the log file at all:
  14. so we have also flushed to disk what we have written */
  15. log_sys->flushed_to_disk_lsn = log_sys->write_lsn;
  16. } else if (flush_to_disk) {
  17. group = UT_LIST_GET_FIRST(log_sys->log_groups);
  18. fil_flush(group->space_id);         # 最后调用fil_flush执行flush到磁盘
  19. log_sys->flushed_to_disk_lsn = log_sys->write_lsn;
  20. }

复制代码

接下来看log_group_write_buf做了点什么

在log_group_calc_size_offset中,从group中取到上次记录的lsn位置(注意是log files组成的1个环状buffer),并计算这次的lsn相对于上次的差值

  1. # 调用log_group_calc_size_offset计算group->lsn_offset除去多个LOG_FILE头部长度后的大小,比如lsn_offset落在第3个log file上,那么需要减掉3*LOG_FILE_HDR_SIZE的大小
  2. gr_lsn_size_offset = (ib_longlong)
  3. log_group_calc_size_offset(group->lsn_offset, group);
  4. group_size = (ib_longlong) log_group_get_capacity(group); # 计算group除去所有LOG_FILE_HDR_SIZE长度后的DATA部分大小
  5. # 下面是典型的环状结构差值计算
  6. if (ut_dulint_cmp(lsn, gr_lsn) >= 0) {
  7. difference = (ib_longlong) ut_dulint_minus(lsn, gr_lsn);
  8. } else {
  9. difference = (ib_longlong) ut_dulint_minus(gr_lsn, lsn);
  10. difference = difference % group_size;
  11. difference = group_size - difference;
  12. }
  13. offset = (gr_lsn_size_offset + difference) % group_size;
  14. # 最后算上每个log file 头部大小,返回真实的offset
  15. return(log_group_calc_real_offset((ulint)offset, group));

复制代码

接着看

  1. # 如果需要写的内容超过一个文件大小
  2. if ((next_offset % group->file_size) + len > group->file_size) {
  3. write_len = group->file_size                # 写到file末尾
  4. - (next_offset % group->file_size);
  5. } else {
  6. write_len = len;                              # 否者写len个block
  7. }
  8. # 最后真正的内容就是写buffer了,如果跨越file的话另外需要写file log file head部分
  9. if ((next_offset % group->file_size == LOG_FILE_HDR_SIZE)
  10. && write_header) {
  11. /* We start to write a new log file instance in the group */
  12. log_group_file_header_flush(group,
  13. next_offset / group->file_size,
  14. start_lsn);
  15. srv_os_log_written+= OS_FILE_LOG_BLOCK_SIZE;
  16. srv_log_writes++;
  17. }
  18. # 调用fil_io来执行buffer写
  19. if (log_do_write) {
  20. log_sys->n_log_ios++;
  21. srv_os_log_pending_writes++;
  22. fil_io(OS_FILE_WRITE | OS_FILE_LOG, TRUE, group->space_id,
  23. next_offset / UNIV_PAGE_SIZE,
  24. next_offset % UNIV_PAGE_SIZE, write_len, buf, group);
  25. srv_os_log_pending_writes--;
  26. srv_os_log_written+= write_len;
  27. srv_log_writes++;
  28. }

复制代码

redo log write和flush的更多相关文章

  1. Oracle Redo Log 机制 小结(转载)

    Oracle 的Redo 机制DB的一个重要机制,理解这个机制对DBA来说也是非常重要,之前的Blog里也林林散散的写了一些,前些日子看老白日记里也有说明,所以结合老白日记里的内容,对oracle 的 ...

  2. mysql redo log

    mysql> show variables like '%innodb_log_file_size%'; +----------------------+-----------+ | Varia ...

  3. 说说MySQL中的Redo log Undo log都在干啥

        在数据库系统中,既有存放数据的文件,也有存放日志的文件.日志在内存中也是有缓存Log buffer,也有磁盘文件log file,本文主要描述存放日志的文件.     MySQL中的日志文件, ...

  4. 详细分析MySQL事务日志(redo log和undo log)

    innodb事务日志包括redo log和undo log.redo log是重做日志,提供前滚操作,undo log是回滚日志,提供回滚操作. undo log不是redo log的逆向过程,其实它 ...

  5. 【转】说说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 空间管理 ...

  6. 【MySQL (六) | 详细分析MySQL事务日志redo log】

    Reference:  https://www.cnblogs.com/f-ck-need-u/archive/2018/05/08/9010872.html 引言 为了最大程度避免数据写入时 IO ...

  7. 详细分析MySQL事务日志(redo log和undo log) 表明了为何mysql不会丢数据

    innodb事务日志包括redo log和undo log.redo log是重做日志,提供前滚操作,undo log是回滚日志,提供回滚操作. undo log不是redo log的逆向过程,其实它 ...

  8. mysql的undo log和redo log

    在数据库系统中,既有存放数据的文件,也有存放日志的文件.日志在内存中也是有缓存Log buffer,也有磁盘文件log file,本文主要描述存放日志的文件.     MySQL中的日志文件,有这么两 ...

  9. binlog和redo log日志提交

    组提交(group commit)是MYSQL处理日志的一种优化方式,主要为了解决写日志时频繁刷磁盘的问题.组提交伴随着MYSQL的发展不断优化,从最初只支持redo log 组提交,到目前5.6官方 ...

随机推荐

  1. 《Python基础教程(第二版)》学习笔记 -> 第七章 更加抽象

    对象的魔力 多态:意味着可以对不同类的对象使用同样的操作: 封装:对外部世界隐藏对象的工作细节: 继承:以普通的类为基础建立专门的类对象 多态① 多态和方法绑定到对象特性上面的函数称为方法(metho ...

  2. web自动化框架之三获取数据库值与界面值比较~~

    数据库用到的是mysql,框架涉及数据库,主要包含两个方面,一个是每个案例执行完毕后,插入案例相关信息与数据:一个是web界面数据核对的时候,需要从sql中获取某行某列值与界面某个值做比较. 描述:w ...

  3. 关于java异常的一点思考

    关于异常的一点思考 异常生命周期 异常的来源 所有的异常都是抛出来的 有底层api抛出的 有自定义抛出的 异常的处理 1, 运行时异常 不做任何处理仍可编译通过 不建议捕获(不建议用异常来做流程控制, ...

  4. 【原】Storm 入门教程目录

    Storm入门教程 1. Storm基础 Storm Storm主要特点 Storm基本概念 Storm调度器 Storm配置 Guaranteeing Message Processing(消息处理 ...

  5. CentOS下编译安装hping3

    安装hping之前,先装上libpcap-dev和tcl-dev 1.获取源码包 wget http://www.hping.org/hping3-20051105.tar.gz 2.解压,得到 hp ...

  6. [Hive - LanguageManual ] ]SQL Standard Based Hive Authorization

    Status of Hive Authorization before Hive 0.13 SQL Standards Based Hive Authorization (New in Hive 0. ...

  7. Codevs No.1245 最小的N个和

    2016-05-31 18:52:15 题目链接: 最小的N个和 Codevs No.1245 题目大意: 给两个等长数列,各取一个数求和,找到最小的N组 解法: 堆优化的大暴力 直接枚举所有可能在最 ...

  8. Mac下的截屏功能

    全屏截图 对全屏的截图我们可以通过按 苹果键(花键)+Shift键+3来执行,之后伴随着清脆的一声提示音后,在桌面上就会生成一个图片文件,这就是刚刚截屏的图片了,默认文件类型是PNG的. 自定义截图 ...

  9. CodeForces 567A Gerald is into Art

    http://codeforces.com/problemset/problem/567/A A. Lineland Mail time limit per test 3 seconds memory ...

  10. eclipse中的js文件报错的解决办法

    在使用别人的项目的时候,导入到eclipse中发现js文件报错,解决办法是关闭eclipse的js校验功能. 三个步骤: 1. 右键点击项目->properties->Validation ...