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
先從 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,需要補齊
上次剩餘為0,連續兩次512位元組或者
此時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
當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檔案中的位置。
為寫入
為寫入redo日誌的大小,但是start_lsn - log.current_file_lsn 不等於圖示的大小,而是一個相對大小
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