mysql8.0源码解析 InnoDB redo log日志 写 write ahead 巧用pageCache实现高效写

pageCache背景

当往磁盘上写文件时,如果文件内容还没有被缓存或者被置换出去了,在内存里不存在对应的page cache,则需要先将对应page的内容从磁盘上读到内存里,修改要写入的数据,然后再将整个page写回到磁盘;在这种情况下,会有一次额外的读IO开销,IO的性能会有一定的损失。

mysql的整体性能高度依赖redo log写IO的性能,InnoDB对对redo日志的写做了优化,redo log写入是追加写的模式(append write),引入了write ahead方法。巧用一个8192字节大小的内存空间(log.write_ahead_buf),实现pageCahe的高效写入。

write ahead 基本原理

利用8192字节大小的内存与ib_logfile 的pageCache 对齐,一次性生成pageCache,而不会出现先读后写的情况。
当第一次写的时候,先生成8192字节大小的缓存。

redo日志大小不同情况的说明

1 小于512字节
2 连续几次之和小于512字节
3 大于512字节,如600字节
4 大于8192字节 ,因引入write ahead,一次写入最大值为8192
5 小于8192字节

write ahead剩余空间大小不同情况的说明

1 剩余为0 ,即开启新一轮的cache
2 剩余空间 不足一次写入redo日志大小
3 剩余空间满足一次写入

对不同情况的画图描述

剩余为0 写入大于8192

mysql8.0源码解析 InnoDB redo log日志 写 write ahead 巧用pageCache实现高效写

先从 write_ahead_buf 的log.write_ahead_end_offset 与 log.current_file_real_offset 相等开始,
有8192字节 redo日志需要写入,因size >= 8192 直接写入pageCache,write_ahead_buf的log.write_ahead_end_offset 不变,下一轮操作再更新
这时 write_ahead_buf 的log.write_ahead_end_offset 比 log.current_file_real_offset小8192,需要补齐
保证log.write_ahead_end_offset 与 log.current_file_real_offset 对齐

上次剩余为0,连续两次512字节或者

mysql8.0源码解析 InnoDB redo log日志 写 write ahead 巧用pageCache实现高效写
此时write_ahead_buf 的log.write_ahead_end_offset 与 log.current_file_real_offset 相等

现在写入一个512字节的redo日志(大于512小于1024,则只写入512字节,下面代码中详解)
现在write_ahead_buf 的空间为0(不是write_ahead_buf8192的字节大小,而是逻辑上虚拟空间)
需要增加逻辑空间为8192,把buffer中的512字节复制到write_ahead_buf 中,第一次要写8192字节
写入pageCahe,形成一个8192字节大小的缓存区

再次写入一个512字节,直接把512字节写入的pageCache,绕开write_ahead_buf.如上图

连续几次写入的大小之和小于512

mysql8.0源码解析 InnoDB redo log日志 写 write ahead 巧用pageCache实现高效写
当write_ahead_buf 中还有虚拟空间时,写入小于512字节(假如写入25字节)
需要把redo日志复制到write_ahead_buf中,剩余空间补0,修改12字节的block头和4字节的block尾,因不足512字节,需要修改12字节的表头,不能在bufferCache中直接修改,会冲突(buffer Cache是并发写,没有锁),这也是巧用write_ahead_buf 的好处
把512字节的大小写入pageCache中

再次写入45字节(两次之和小于512),需要从上次的起始位置即需要复制25+45个字节复制到write_ahead_buf中,
剩余空间补0,修改12字节的block头和4字节的block尾,再把此512字节复制到pageCache中,覆盖上一次的pageCache中的512字节

代码详解-log_files_write_buffer

注源码为mysql8.0.20
log0write.cc

/*此函主要是把buffer的redo日志,通过write_ahead 功能,把redo日志写入ib_logfile的缓存(pageCache)*/
1624  static void log_files_write_buffer(log_t &log, byte *buffer, size_t buffer_size,
1625                                     lsn_t start_lsn) {
1626    ut_ad(log_writer_mutex_own(log));
1627  
1628    using namespace Log_files_write_impl;
1629  
1630    validate_buffer(log, buffer, buffer_size);
1631  
1632    validate_start_lsn(log, start_lsn, buffer_size);
1633  
1634    checkpoint_no_t checkpoint_no = log.next_checkpoint_no.load();
1635    /*得到当前start_lsn在文件中的偏移,ib_logfile是循环使用,start_lsn 可能是ib_logfile总大小的几倍,start_lsn的大小转换成文件的偏移*/
1636    const auto real_offset = compute_real_offset(log, start_lsn);
1637  
1638    bool write_from_log_buffer;
1639    /*计算本次要写入redo日志的大小
         当大于8192时,则截断为8192,一次最大写入8192
         根据情况判断,判断是否需要把redo日志复制到write_ahead_buf,确定其write_from_log_buffer状态,为true 则不需要复制,为false ,需要把buffer中的redo复制到write_from_log_buffer
         */
1640    auto write_size = compute_how_much_to_write(log, real_offset, buffer_size,
1641                                                write_from_log_buffer);
1642    /*
         start_next_file 函数解析
         当上次写的文件尾时,本次返回为0,则做文件切换
         主要更新log.current_file_lsn  log.current_file_real_offset  log.current_file_end_offset 这三个变量(这三个变量),实现文件的切换
         这三个变量仅在启动时 和切换文件时修改
         log.current_file_lsn  随着 lsn的增长而增长  
         log.current_file_real_offset 是整个文件总和的偏移量,>=2048 且 < 全部ib_logfile文件的总大小          
         log.current_file_end_offset 当前要写的ib_logfile 文件的结尾
        */
1643    if (write_size == 0) {
1644      start_next_file(log, start_lsn);
1645      return;
1646    }
1647    /*如果write_size大与512,则填充block的12字节的头和4字节的校验尾
         只填充512的block,如果不足512的部分,不在此函数的处理范围中
         */
1648    prepare_full_blocks(log, buffer, write_size, start_lsn, checkpoint_no);
1649  
1650    byte *write_buf;
1651    uint64_t written_ahead = 0;
1652    lsn_t lsn_advance = write_size;
1653    
1654    if (write_from_log_buffer) {
1655      /* We have at least one completed log block to write.
1656      We write completed blocks from the log buffer. Note,
1657      that possibly we do not write all completed blocks,
1658      because of write-ahead strategy (described earlier). */
1659      DBUG_PRINT("ib_log",
1660                 ("write from log buffer start_lsn=" LSN_PF " write_lsn=" LSN_PF
1661                  " -> " LSN_PF,
1662                  start_lsn, log.write_lsn.load(), start_lsn + lsn_advance));
1663      /*利用write_buf指针,指向buffer缓存*/
1664      write_buf = buffer;
1665  
1666      LOG_SYNC_POINT("log_writer_before_write_from_log_buffer");
1667     
1668    } else {
1669      DBUG_PRINT("ib_log",
1670                 ("incomplete write start_lsn=" LSN_PF " write_lsn=" LSN_PF
1671                  " -> " LSN_PF,
1672                  start_lsn, log.write_lsn.load(), start_lsn + lsn_advance));
1673  
1674  #ifdef UNIV_DEBUG
1675      if (start_lsn == log.write_lsn.load()) {
1676        LOG_SYNC_POINT("log_writer_before_write_new_incomplete_block");
1677      }
1678      /* Else: we are doing yet another incomplete block write within the
1679      same block as the one in which we did the previous write. */
1680  #endif /* UNIV_DEBUG */
1681      /*write_from_log_buffer 为false
            利用write_buf指针,指向write_ahead缓存*/
1682      write_buf = log.write_ahead_buf;
1683  
1684      /* We write all the data directly from the write-ahead buffer,
1685      where we first need to copy the data. */
          /*write_size大小的redo从buffer中复制到log.write_ahead_buf
           把log.write_ahead_buf中最后一个不足512的block 补0 
           并把write_size大小512向下对齐,如果不能被512整除,则把最后一个block补0的长度加到write_size,使其能够与512整除           
           */
1686      copy_to_write_ahead_buffer(log, buffer, write_size, start_lsn,
1687                                 checkpoint_no);
1688      /*判断write_ahead的虚拟空间是否完全被占用,用1个字节来判断,如果1字节个都放不下则虚拟空间用尽*/
1689      if (!current_write_ahead_enough(log, real_offset, 1)) {
            /*write_ahead 虚拟空间用尽时,需要判断ib_logfile 当前文件的剩余空间(当前偏移到文件尾的偏移)是否足够放下当前要写的redo日志的大小
            返回值为write_ahead 虚拟空间剩余空间
*/
1690        written_ahead = prepare_for_write_ahead(log, real_offset, write_size);
1691      }
1692    }
1693  
1694    srv_stats.os_log_pending_writes.inc();
1695  
1696    /* Now, we know, that we are going to write completed
1697    blocks only (originally or copied and completed). */
        /*把write_size的小的redo日志写入ib_logfile的缓存(pageCache)*/
1698    write_blocks(log, write_buf, write_size, real_offset);
1699  
1700    LOG_SYNC_POINT("log_writer_before_lsn_update");
1701  
1702    const lsn_t old_write_lsn = log.write_lsn.load();
1703    /*lsn_advance 不是写入pageCache的大小,补0的部分不包含在此变量中,lsn_advance 为当前写入redo日志的大小
     当对于连续几次小范围的redo日志写入时,lsn_advance为几次写的总和
     start_lsn 为512对齐,并不是上次结束的位置
     start_lsn <= old_write_lsn
*/
1704    const lsn_t new_write_lsn = start_lsn + lsn_advance;
1705    ut_a(new_write_lsn > log.write_lsn.load());
1706    /*更新log.write_lsn为当前最新的值*/
1707    log.write_lsn.store(new_write_lsn);
1708    /*通知Log write_notifier thread*/
1709    notify_about_advanced_write_lsn(log, old_write_lsn, new_write_lsn);
1710  
1711    LOG_SYNC_POINT("log_writer_before_buf_limit_update");
1712  
1713    log_update_buf_limit(log, new_write_lsn);
1714  
1715    srv_stats.os_log_pending_writes.dec();
1716    srv_stats.log_writes.inc();
1717  
1718    /* Write ahead is included in write_size. */
1719    ut_a(write_size >= written_ahead);
1720    srv_stats.os_log_written.add(write_size - written_ahead);
1721    MONITOR_INC_VALUE(MONITOR_LOG_PADDED, written_ahead);
1722  
1723    int64_t free_space = log.lsn_capacity_for_writer - log.extra_margin;
1724  
1725    /* The free space may be negative (up to -log.extra_margin), in which
1726    case we are in the emergency mode, eating the extra margin and asking
1727    to increase concurrency_margin. */
1728    free_space -= new_write_lsn - log.last_checkpoint_lsn.load();
1729  
1730    MONITOR_SET(MONITOR_LOG_FREE_SPACE, free_space);
1731  
1732    log.n_log_ios++;
1733    /*判断是否更新 log.write_ahead_end_offset
          本地虚拟空间用尽时不更新此变量
          当本地虚拟空间用尽后,第一次写入时,更新log.write_ahead_end_offset,即增加8192 
         */
1734    update_current_write_ahead(log, real_offset, write_size);
1735  }
  

代码详解-compute_how_much_to_write

简单介绍下compute_real_offset此函,虽然短小,但很经典,主要计算出当前lsn在整个文件的绝对偏移量(real_offset),lsn 映射成应写在ib_logfile文件中的位置。

为写入
mysql8.0源码解析 InnoDB redo log日志 写 write ahead 巧用pageCache实现高效写为写入redo日志的大小,但是start_lsn - log.current_file_lsn 不等于图示的大小,而是一个相对大小

mysql8.0源码解析 InnoDB redo log日志 写 write ahead 巧用pageCache实现高效写
log0write.cc

1223  static inline uint64_t compute_real_offset(const log_t &log, lsn_t start_lsn) {
        /*start_lsn 当前开始写的lsn的起始值
          log.current_file_lsn 在启动或切换文件后确定,相对于lsn的一个固定位置
          log.current_file_real_offset 在启动或切换文件后确定 ,相对于文件大小的偏移量
 */
1228    const auto real_offset =
1229        log.current_file_real_offset + (start_lsn - log.current_file_lsn);
1230  
1239  
1240    return (real_offset);
1241  }

compute_how_much_to_write 函数

log0write.cc

/*
  计算本次可以写redo日志的大小
  real_offset 在文件的绝对偏移量
  buffer_size 需要写入redo日志的大小  
  write_from_log_buffer 返回参数 
     true  buffer中的redo日志不需要复制到write_ahead_buf中
     false buffer中的redo日志需要复制到write_ahead_buf中 
*/
1310   static inline size_t compute_how_much_to_write(const log_t &log,
1311                                                  uint64_t real_offset,
1312                                                  size_t buffer_size,
1313                                                  bool &write_from_log_buffer) {
1314     size_t write_size;
1315   
1316     /* First we ensure, that we will write within single log file.
1317     If we had more to write and cannot fit the current log file,
1318     we first write what fits, then stops and returns to the main
1319     loop of the log writer thread. Then, the log writer will update
1320     maximum lsn up to which, it has data ready in the log buffer,
1321     and request next write operation according to its strategy. */
         /*当前的文件的偏移量(real_offset)到文件尾的空间大小是否满足 buffer_size的大小*/
1322     if (!current_file_has_space(log, real_offset, buffer_size)) {
1323       /* The end of write would not fit the current log file. */
1324   
1325       /* But the beginning is guaranteed to fit or to be placed
1326       at the first byte of the next file. */
1327       ut_a(current_file_has_space(log, real_offset, 0));
1328       /*当前的文件的偏移量(real_offset)到文件尾的空间大小是否0 判断上次是否已经把空间全部写满,如果写满则返回0 准备切换文件*/
1329       if (!current_file_has_space(log, real_offset, 1)) {
1330         /* The beginning of write is at the first byte
1331         of the next log file. Flush header of the next
1332         log file, advance current log file to the next,
1333         stop and return to the main loop of log writer. */
1334         write_from_log_buffer = false;
1335         return (0);
1336   
1337       } else {
1338         /* We write across at least two consecutive log files.
1339         Limit current write to the first one and then retry for
1340         next_file. */
1341   
1342         /* If the condition for real_offset + buffer_size holds,
1343         then the expression below is < buffer_size, which is
1344         size_t, so the typecast is ok. */
             /*buffer_size大于当前的剩余空间,则只能写剩余空间的大小,下次则走一个分支,切换文件
             write_size 为剩余空间的大小
             */
1345         write_size =
1346             static_cast<size_t>(log.current_file_end_offset - real_offset);
1347   
1348         ut_a(write_size <= buffer_size);
1349         ut_a(write_size % OS_FILE_LOG_BLOCK_SIZE == 0);
1350       }
1351   
1352     } else {
          /*如果空间足够大,则直接赋值*/
1353       write_size = buffer_size;
1354   
1355       ut_a(write_size % OS_FILE_LOG_BLOCK_SIZE >= LOG_BLOCK_HDR_SIZE ||
1356            write_size % OS_FILE_LOG_BLOCK_SIZE == 0);
1357   
1358       ut_a(write_size % OS_FILE_LOG_BLOCK_SIZE <
1359            OS_FILE_LOG_BLOCK_SIZE - LOG_BLOCK_TRL_SIZE);
1360     }
1361   
1362     /* Now, we know we can write write_size bytes from the buffer,
1363     and we will do the write within single log file - current one. */
1364   
1365     ut_a(write_size > 0);
1366     ut_a(real_offset >= log.current_file_real_offset);
1367     ut_a(real_offset + write_size <= log.current_file_end_offset);
1368     ut_a(log.current_file_real_offset / log.file_size + 1 ==
1369          log.current_file_end_offset / log.file_size);
1370   
1371     /* We are interested in writing from log buffer only,
1372     if we had at least one completed block for write.
1373     Still we might decide not to write from the log buffer,
1374     because write-ahead is needed. In such case we could write
1375     together with the last incomplete block after copying. */
1376     write_from_log_buffer = write_size >= OS_FILE_LOG_BLOCK_SIZE;
1377   
1378     if (write_from_log_buffer) {
1379       MONITOR_INC(MONITOR_LOG_FULL_BLOCK_WRITES);
1380     } else {
1381       MONITOR_INC(MONITOR_LOG_PARTIAL_BLOCK_WRITES);
1382     }
1383   
1384     /* Check how much we have written ahead to avoid read-on-write. */
1385     /*当前的文件的偏移量(real_offset)到write_ahead尾的空间大小是否满足buffer_size的大小*/
1386     if (!current_write_ahead_enough(log, real_offset, write_size)) {
           /*当前的文件的偏移量(real_offset)到write_ahead尾的空间大小是否0 判断上次是否已经把空间全部写满,如果写满 则下次更新 write_ahead_end_offset*/
1387       if (!current_write_ahead_enough(log, real_offset, 1)) {
1388         /* Current write-ahead region has no space at all. */
1389         /*说明上次写已经到write_ahead尾,则根据real_offset起始,计算一个8192空间大小的write_ahead尾值next_wa*/
1390         const auto next_wa = compute_next_write_ahead_end(real_offset);
1391         /*判断新计算的write_ahead尾值 满足 write_size的大小*/
1392         if (!write_ahead_enough(next_wa, real_offset, write_size)) {
1393           /* ... and also the next write-ahead is too small.
1394           Therefore we have more data to write than size of
1395           the write-ahead. We write from the log buffer,
1396           skipping last fragment for which the write ahead
1397           is required. */
1398   
1399           ut_a(write_from_log_buffer);
1400           /*上一次用尽,新计算的尾值也不能满足,则write_size 已经大于write-ahead的总量,一般大于8192,如果写入的大小大于8192 ,则只能写8192,且不需要要把buffer中的redo日志复制到write-ahead,而是直接写到pageCache
*/   
1401           write_size = next_wa - real_offset;
1402   
1403           ut_a((real_offset + write_size) % srv_log_write_ahead_size == 0);
1404   
1405           ut_a(write_size % OS_FILE_LOG_BLOCK_SIZE == 0);
1406   
1407         } else {
1408           /* We copy data to write_ahead buffer,
1409           and write from there doing write-ahead
1410           of the bigger region in the same time. */
               /*
               当write-ahead上次用尽后,则需要新开辟一个8192的pageCache,
               当第一写不满足8192时,则把当前要写入的redo日志复制到write-ahead,剩余空间补0,
               8192个字节一次写入redo日志,形成一个8192大小的pageCache
               此处为整个pageCache利用的精华,有画龙点睛的意思
               */
1411           write_from_log_buffer = false;
1412         }
1413   
1414       } else {
1415         /* We limit write up to the end of region
1416         we have written ahead already. */
             /*pageCache 剩余空间不足要写入write_size的大小,则重新计算write_head大小此次把pageCache用尽,下次则开辟一个块新的空间*/
1417         write_size =
1418             static_cast<size_t>(log.write_ahead_end_offset - real_offset);
1419   
1420         ut_a(write_size >= OS_FILE_LOG_BLOCK_SIZE);
1421         ut_a(write_size % OS_FILE_LOG_BLOCK_SIZE == 0);
1422       }
1423   
1424     } else {
           
1425       if (write_from_log_buffer) {
           /*此处完全执行需要满足几个条件
           1 PageCache的空间足够大
           2 不是第一次写
           3 大于等于512 
           执行的结果为512的整数倍,不足512的丢弃,并得到实际写入大小
          */
1426         write_size = ut_uint64_align_down(write_size, OS_FILE_LOG_BLOCK_SIZE);
1427       }
1428     }
1429     /*通过复杂的计算,返回写pageCache大小*/
1430     return (write_size);
1431   }

代码详解 copy_to_write_ahead_buffer

/*
  此函数实现两个功能
  把buffer中的redo日志复制到write-ahead 中
  把不足512字节的redo日志补0 ,并计算填充block的头和尾
*/
1518   static inline void copy_to_write_ahead_buffer(log_t &log, const byte *buffer,
1519                                                 size_t &size, lsn_t start_lsn,
1520                                                 checkpoint_no_t checkpoint_no) {
1521     ut_a(size <= srv_log_write_ahead_size);
1522   
1523     ut_a(buffer >= log.buf);
1524     ut_a(buffer + size <= log.buf + log.buf_size);
1525   
1526     byte *write_buf = log.write_ahead_buf;
1527   
1528     LOG_SYNC_POINT("log_writer_before_copy_to_write_ahead_buffer");
1529     /*复制功能*/
1530     std::memcpy(write_buf, buffer, size);
1531   
1532     size_t completed_blocks_size;
1533     byte *incomplete_block;
1534     size_t incomplete_size;
1535     /*写入大小与512向下对齐,得到512的整数*/
1536     completed_blocks_size = ut_uint64_align_down(size, OS_FILE_LOG_BLOCK_SIZE);
1537     /*log.write_ahead_buf redo日志大小 512整数倍的偏移量,之后则为不足512的redo日志,需要特殊处理*/
1538     incomplete_block = write_buf + completed_blocks_size;
1539     /*不足512的大小*/
1540     incomplete_size = size % OS_FILE_LOG_BLOCK_SIZE;
1541   
1542     ut_a(incomplete_block + incomplete_size <=
1543          write_buf + srv_log_write_ahead_size);
1544     /*存在不足512的部分*/
1545     if (incomplete_size != 0) {
1546       /* Prepare the incomplete (last) block. */
1547       ut_a(incomplete_size >= LOG_BLOCK_HDR_SIZE);
1548       /*设置当前lsn 为block块的序号*/
1549       log_block_set_hdr_no(
1550           incomplete_block,
1551           log_block_convert_lsn_to_no(start_lsn + completed_blocks_size));
1552       /*记录一次写不足512的情况*/
1553       log_block_set_flush_bit(incomplete_block, completed_blocks_size == 0);
1554       /*记录写入块的实际redo日志的大小*/
1555       log_block_set_data_len(incomplete_block, incomplete_size);
1556   
1557       if (log_block_get_first_rec_group(incomplete_block) > incomplete_size) {
1558         log_block_set_first_rec_group(incomplete_block, 0);
1559       }
1560       /*记录当前checkpoint_no的序号*/
1561       log_block_set_checkpoint_no(incomplete_block, checkpoint_no);
1562        /*不足512的剩余部分填充0*/
1563       std::memset(incomplete_block + incomplete_size, 0x00,
1564                   OS_FILE_LOG_BLOCK_SIZE - incomplete_size);
1565       /*计算尾部校验数*/
1566       log_block_store_checksum(incomplete_block);
1567       /*返回512的整数倍,保证每次写都是512的倍数,如果不足512,也需要写入512字节*/
1568       size = completed_blocks_size + OS_FILE_LOG_BLOCK_SIZE;
1569     }
1570   
1571     /* Since now, size is about completed blocks always. */
1572     ut_a(size % OS_FILE_LOG_BLOCK_SIZE == 0);
1573   }

代码解读-

/*
  此处的判断及实现也是非常的经典
  当上次把write_ahead 的虚拟空间或者pageCache 写满后的补充处理
  实现两个功能
  1 如果恰好在ib_logfile 尾部不足一个8192的大小(恰有这样的情况产生,文件的大小减去2048不是8192的倍数,文件大小可以配置,产生不一样的情况,会有很大几率可能出现),则只能使用剩余空间的pageCache的大小
  2 本次要写入8192字节大小,剩余的空间需要填充0 
  此处处理与函数compute_how_much_to_write中的
  if (!current_write_ahead_enough(log, real_offset, 1)){  
    ...    
  }对应,是对此处的完美补充
  
*/
1689 if (!current_write_ahead_enough(log, real_offset, 1)) {
1690      written_ahead = prepare_for_write_ahead(log, real_offset, write_size);
1691 }
    
1575  static inline size_t prepare_for_write_ahead(log_t &log, uint64_t real_offset,
1576                                               size_t &write_size) {
1577    /* We need to perform write ahead during this write. */
1578   /*得到下一个write-ahead尾偏移量*/
1579    const auto next_wa = compute_next_write_ahead_end(real_offset);
1580  
1581    ut_a(real_offset + write_size <= next_wa);
1582    /*此write_ahead 8192字节大小中还未使用的部分*/
1583    size_t write_ahead =
1584        static_cast<size_t>(next_wa - (real_offset + write_size));
1585    /*判断当前real_offset 到 ib_logfile 尾部的剩余空间大小是否能写下一个完整的8192的空间的大小,即最后一个pageCache 不一定是8192字节的大小,在当前ib_logfile文件马上要写满时会出现 */
1586    if (!current_file_has_space(log, real_offset, write_size + write_ahead)) {
1587      /* We must not write further than to the end
1588      of the current log file.
1589  
1590      Note, that: log.file_size - LOG_FILE_HDR_SIZE
1591      does not have to be divisible by size of write
1592      ahead. Example given:
1593              innodb_log_file_size = 1024M,
1594              innodb_log_write_ahead_size = 4KiB,
1595              LOG_FILE_HDR_SIZE is 2KiB. */
1596      /*
            当ib_logfile的最后一个pageCache时,计算出剩余空间
            虽然一个8192字节的pageCache 放不下,但是要写redo日志的大小肯定能放得下,
            compute_how_much_to_write 此函数已提前处理
           */
1597      write_ahead = static_cast<size_t>(log.current_file_end_offset -
1598                                        real_offset - write_size);
1599    }
1600  
1601    ut_a(current_file_has_space(log, real_offset, write_size + write_ahead));
1602  
1603    LOG_SYNC_POINT("log_writer_before_write_ahead");
1604    /*剩余空间填充0*/
1605    std::memset(log.write_ahead_buf + write_size, 0x00, write_ahead);
1606    /*得到pageCache的大小,大多数情况为8192 */
1607    write_size += write_ahead;
1608    
1609    return (write_ahead);
1610  }

代码解析- update_current_write_ahead

/*
  write-ahead 的pageCache的收官之作
  当第一次使用write-ahead,把write-ahead尾部的偏移量write_ahead_end_offset更新为最新的偏移的量,可以理解为加8192(除ib_logfile尾的特殊处理)
*/
1612  static inline void update_current_write_ahead(log_t &log, uint64_t real_offset,
1613                                                size_t write_size) {
1614    const auto end = real_offset + write_size;
1615  
1616    if (end > log.write_ahead_end_offset) {
1617      log.write_ahead_end_offset =
1618          ut_uint64_align_down(end, srv_log_write_ahead_size);
1619    }
1620  }
1621  
1622  }  // namespace Log_files_write_impl

上一篇:mac os x 通过Carbon Process Manager启动应用程序


下一篇:833-Redis缓存穿透,缓存击穿,缓存雪崩