Redo Logs

The idb::innodb::log module provides types and functions for reading InnoDB redo log files. These are ib_logfile0/ib_logfile1 for MySQL versions before 8.0.30, or #ib_redo* numbered files for MySQL 8.0.30 and later.

File Layout

An InnoDB redo log file consists of 512-byte blocks:

  • Block 0: Log file header (group ID, start LSN, file number, creator string)
  • Block 1: Checkpoint slot 0
  • Block 2: Reserved
  • Block 3: Checkpoint slot 1
  • Blocks 4+: Data blocks containing redo log records

Each block has a 14-byte header and a 4-byte trailer (CRC-32C checksum at bytes 508-511).

Opening a Redo Log

From a file path

use idb::innodb::log::LogFile;

let mut log = LogFile::open("/var/lib/mysql/ib_logfile0").unwrap();
println!("File size: {} bytes", log.file_size());
println!("Total blocks: {}", log.block_count());
println!("Data blocks: {}", log.data_block_count());

LogFile::open is not available on wasm32 targets.

From an in-memory buffer

use idb::innodb::log::LogFile;

let data: Vec<u8> = std::fs::read("/var/lib/mysql/ib_logfile0").unwrap();
let mut log = LogFile::from_bytes(data).unwrap();

The file must be at least 2048 bytes (4 blocks of 512 bytes for the header and checkpoint areas).

Reading the File Header

use idb::innodb::log::LogFile;

let mut log = LogFile::open("/var/lib/mysql/ib_logfile0").unwrap();
let header = log.read_header().unwrap();

println!("Group ID: {}", header.group_id);
println!("Start LSN: {}", header.start_lsn);
println!("File number: {}", header.file_no);
println!("Created by: {}", header.created_by);

LogFileHeader Fields

FieldTypeDescription
group_idu32Log group ID
start_lsnu64Start LSN of this log file
file_nou32File number within the log group
created_byStringMySQL version string (e.g., "MySQL 8.0.32", "MariaDB 10.11.4")

The created_by string can be used for vendor detection:

use idb::innodb::log::LogFile;
use idb::innodb::vendor::detect_vendor_from_created_by;

let mut log = LogFile::open("/var/lib/mysql/ib_logfile0").unwrap();
let header = log.read_header().unwrap();
let vendor = detect_vendor_from_created_by(&header.created_by);
println!("Vendor: {}", vendor);

Reading Checkpoints

InnoDB maintains two checkpoint slots for crash recovery. Slot 0 is in block 1, slot 1 is in block 3.

use idb::innodb::log::LogFile;

let mut log = LogFile::open("/var/lib/mysql/ib_logfile0").unwrap();

let cp0 = log.read_checkpoint(0).unwrap();
println!("Checkpoint 0: number={}, LSN={}, offset={}, buf_size={}",
    cp0.number, cp0.lsn, cp0.offset, cp0.buf_size);

let cp1 = log.read_checkpoint(1).unwrap();
println!("Checkpoint 1: number={}, LSN={}, offset={}, buf_size={}",
    cp1.number, cp1.lsn, cp1.offset, cp1.buf_size);

LogCheckpoint Fields

FieldTypeDescription
numberu64Checkpoint sequence number
lsnu64LSN at the time of this checkpoint
offsetu32Byte offset of the checkpoint within the log file
buf_sizeu32Log buffer size at checkpoint time
archived_lsnu64LSN up to which log has been archived

Reading Data Blocks

use idb::innodb::log::{LogFile, LogBlockHeader, LogBlockTrailer, LOG_FILE_HDR_BLOCKS};

let mut log = LogFile::open("/var/lib/mysql/ib_logfile0").unwrap();

// Read data blocks (starting at block 4, after header/checkpoint blocks)
for block_no in LOG_FILE_HDR_BLOCKS..log.block_count() {
    let block = log.read_block(block_no).unwrap();
    let header = LogBlockHeader::parse(&block).unwrap();

    if header.has_data() {
        println!("Block {}: data_len={}, flush={}, checkpoint_no={}",
            header.block_no, header.data_len, header.flush_flag, header.checkpoint_no);
    }
}

LogBlockHeader Fields

FieldTypeDescription
block_nou32Block number (with flush bit masked out)
flush_flagboolWhether this block was the first in a flush batch (bit 31 of raw block number)
data_lenu16Number of bytes of log data in this block (including the 14-byte header)
first_rec_groupu16Offset of the first log record group starting in this block
checkpoint_nou32Checkpoint number when this block was written

The has_data() method returns true when data_len exceeds the header size (14 bytes), indicating the block contains log record data.

Block Checksum Validation

Each 512-byte block has a CRC-32C checksum stored in the last 4 bytes (offset 508-511), covering bytes 0-507.

use idb::innodb::log::{LogFile, validate_log_block_checksum, LOG_FILE_HDR_BLOCKS};

let mut log = LogFile::open("/var/lib/mysql/ib_logfile0").unwrap();

for block_no in LOG_FILE_HDR_BLOCKS..log.block_count() {
    let block = log.read_block(block_no).unwrap();
    let valid = validate_log_block_checksum(&block);
    if !valid {
        println!("Block {} has invalid checksum", block_no);
    }
}

MLOG Record Types

The MlogRecordType enum classifies redo log record types from MySQL's mtr0types.h. This is useful for analyzing what operations are recorded in the redo log.

use idb::innodb::log::MlogRecordType;

let rec_type = MlogRecordType::from_u8(9);
println!("{}", rec_type);        // "MLOG_REC_INSERT"
println!("{}", rec_type.name()); // "MLOG_REC_INSERT"

let unknown = MlogRecordType::from_u8(200);
println!("{}", unknown);         // "UNKNOWN(200)"

Common MLOG Record Types

ValueVariantName
1Mlog1ByteMLOG_1BYTE
2Mlog2BytesMLOG_2BYTES
4Mlog4BytesMLOG_4BYTES
8Mlog8BytesMLOG_8BYTES
9MlogRecInsertMLOG_REC_INSERT
13MlogRecUpdateMLOG_REC_UPDATE_IN_PLACE
14MlogRecDeleteMLOG_REC_DELETE
19MlogPageCreateMLOG_PAGE_CREATE
22MlogUndoInitMLOG_UNDO_INIT
30MlogInitFilePageMLOG_INIT_FILE_PAGE
31MlogWriteStringMLOG_WRITE_STRING
32MlogMultiRecEndMLOG_MULTI_REC_END
36MlogCompRecInsertMLOG_COMP_REC_INSERT
44MlogCompPageReorganizeMLOG_COMP_PAGE_REORGANIZE
52MlogFileCreateMLOG_FILE_CREATE

Unknown type codes are represented as MlogRecordType::Unknown(value).

Block Count Methods

MethodDescription
block_count()Total number of 512-byte blocks in the file
data_block_count()Number of data blocks (total minus 4 header/checkpoint blocks)
file_size()File size in bytes

Constants

The module exports several constants matching MySQL's log0log.h:

ConstantValueDescription
LOG_BLOCK_SIZE512Size of a redo log block in bytes
LOG_BLOCK_HDR_SIZE14Size of the block header
LOG_BLOCK_TRL_SIZE4Size of the block trailer
LOG_BLOCK_FLUSH_BIT_MASK0x80000000Bitmask for the flush flag in block number
LOG_BLOCK_CHECKSUM_OFFSET508Byte offset of CRC-32C checksum within a block
LOG_FILE_HDR_BLOCKS4Number of reserved header/checkpoint blocks