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. XTUOJ 1248 TC or CF 搜索

    这个题一眼看上去不会 然后有人说是网络流 然后我就想怎么建图啊,然后不会(是本蒟蒻太垃圾了),肯定有网络流解法 然后去群里问了gdut的巨巨,他说他队友爆搜+剪枝过了(我也是非常的叹服) 然后我也写了 ...

  2. 同行评审 Peer Review

    周五的课上,章老师给我们上了一节关于同行评审(Peer Review)的课程,让我了解了以前并不熟悉的这一过程.课上我们就姚思丹同学项目组做的项目,分组进行了审查. 首先介绍一下同行评审(Peer R ...

  3. mysql EF

    使用 mysql-installer-community-5.6.26.0.msi visual studio 2013 update 4版 Install-Package EntityFramewo ...

  4. 去除下载电影和电视剧文件名中的多余字符[python实现]

    讨厌下载电影和电视剧文件名中的多余字符(如网址和广告字样),,搞得文件名好长,可以使用下面的Python代码,自行修改即可. #!\usr\bin\env python # -*- coding: u ...

  5. 【和我一起学python吧】python入门语法总结

    1.python是一个解释性语言: 一个用编译性语言比如C或C++写的程序可以从源文件(即C或C++语言)转换到一个你的计算机使用的语言(二进制代码,即0和1).这个过程通过编译器和不同的标记.选项完 ...

  6. wuzhicms访问统计实现方法

    实现目标:程序实现了对整站页面pv的统计文件的位置:coreframe/app/content/pv.php代码预览: /** * 总站访问次数统计 */ defined('IN_WZ') or ex ...

  7. Windows 8.1及Windows8 JDK环境变量配置

    一.首先安装JDK JDK:http://www.oracle.com/technetwork/cn/java/javase/downloads/index.html 根据操作系统选择相应的版本 二. ...

  8. [Hive - Tutorial] Creating, Showing, Altering, and Dropping Tables

    Creating, Showing, Altering, and Dropping Tables See Hive Data Definition Language for detailed info ...

  9. matlab中的字符串数组与函数调用

    1, matlab中的字符串就是1维字符数组,即如: a = 'dddssd'; b = 'lsde'; c = [a, b]; 当然也可以: c= strcat(a, b); 2, matlab中的 ...

  10. Hibernate之HQL查询

    一.Hibernate 提供了以下几种检索对象的方式: 导航对象图检索方式: 根据已经加载的对象导航到其他对象 OID 检索方式: 按照对象的 OID 来检索对象 HQL 检索方式:使用面向对象的 H ...