Skip to content

检查点 - Checkpoint机制详解 ​

定义 ​

Checkpoint (检查点) 是InnoDB存储引擎的一种持久化机制,它确保在某个时间点之前产生的所有脏页(Dirty Page)都已经从Buffer Pool刷新到磁盘上的数据文件(.ibd)。同时,Checkpoint会记录一个LSN(Log Sequence Number)值,表示在该LSN之前的所有Redo Log都已经应用到数据文件中。

核心目标 ​

目标说明
缩短恢复时间崩溃后只需重做最后一个Checkpoint之后的Redo Log
控制脏页数量定期刷脏页,避免Buffer Pool爆满
保证数据一致性确保数据文件和Redo Log的LSN对齐
支持增量备份基于Checkpoint LSN进行增量备份

检查点类型对比 ​

锐利检查点 (Sharp Checkpoint)
┌─────────────────────────────────────┐
│ • MySQL启动时执行       │
│ • 等待所有事务完成      │
│ • 将所有脏页刷盘        │
│ • 记录Checkpoint LSN      │
│ • 恢复时间 = 0 (无需Redo)     │
│ • 缺点: 阻塞时间长      │
└─────────────────────────────────────┘

模糊检查点 (Fuzzy Checkpoint)
┌─────────────────────────────────────┐
│ • MySQL运行时定期执行     │
│ • 只刷部分脏页        │
│ • 不阻塞正常事务        │
│ • 记录Checkpoint LSN      │
│ • 恢复时间 = 重放Checkpoint后日志 │
│ • 优点: 不影响在线性能      │
└─────────────────────────────────────┘

InnoDB默认使用模糊检查点,有以下几种触发方式:

  1. Master Thread Checkpoint: 每秒检查一次
  2. Dirty Page Too Much Checkpoint: 脏页比例超过阈值
  3. Redo Log Full Checkpoint: Redo Log空间不足
  4. Async/Sync Flush Checkpoint: 异步/同步刷盘

为什么需要Checkpoint? ​

问题1: 崩溃恢复时间过长 ​

没有Checkpoint的场景:

时间线:
T0: MySQL启动
T1: 事务A修改Page #100, #200, #300 (提交)
T2: 事务B修改Page #400, #500 (提交)
...
T999999: 事务Z修改Page #9999 (提交)
T1000000: 系统崩溃! 💥

恢复过程:
┌──────────────────────────────────────┐
│ MySQL重启          │
└──────────────────────────────────────┘
   ↓
┌──────────────────────────────────────┐
│ 扫描所有Redo Log (LSN 1 ~ 1000000)  │
│ 重放100万次修改操作      │
│ 耗时: 30分钟 ⏰         │
└──────────────────────────────────────┘

使用Checkpoint的场景:

时间线:
T0: MySQL启动
T100: Checkpoint (LSN=100, 所有脏页刷盘)
T200: Checkpoint (LSN=200, 所有脏页刷盘)
...
T999900: Checkpoint (LSN=999900)
T999999: 事务Z修改Page #9999 (提交)
T1000000: 系统崩溃! 💥

恢复过程:
┌──────────────────────────────────────┐
│ MySQL重启          │
└──────────────────────────────────────┘
   ↓
┌──────────────────────────────────────┐
│ 读取最后一个Checkpoint LSN=999900  │
│ 只重放LSN 999900 ~ 1000000的日志   │
│ 重放100次修改操作        │
│ 耗时: 3秒 ✓          │
└──────────────────────────────────────┘

性能提升: 恢复时间从30分钟降低到3秒,提升600倍!

问题2: Buffer Pool空间不足 ​

场景:

Buffer Pool大小: 8GB
数据文件大小: 100GB

 workload:
- 持续随机读取不同数据页
- 每页16KB,共625万页

没有Checkpoint:
┌──────────────────────────────────────┐
│ Buffer Pool逐渐被填满      │
│ 脏页积累 → 可用页减少      │
│ 最终: 无法分配新页 ❌       │
└──────────────────────────────────────┘

有Checkpoint:
┌──────────────────────────────────────┐
│ 定期检查脏页比例       │
│ 超过75%时触发刷盘        │
│ 脏页 → 数据文件        │
│ Buffer Pool保持充足可用空间 ✓    │
└──────────────────────────────────────┘

问题3: Redo Log循环写入覆盖 ​

场景:

Redo Log配置:
- innodb_log_file_size = 1GB
- innodb_log_files_in_group = 2
- 总容量 = 2GB

写入速度: 100MB/s

没有Checkpoint的风险:
┌──────────────────────────────────────┐
│ T0: Redo Log写入位置: 0GB    │
│ T10s: 写入位置: 1GB      │
│ T20s: 写入位置: 2GB (回到起点)   │
│  需要覆盖0~1GB的旧日志    │
│  但这些日志对应的脏页还在内存!  │
│  如果此时崩溃,数据丢失! ❌    │
└──────────────────────────────────────┘

有Checkpoint的保护:
┌──────────────────────────────────────┐
│ T0: Checkpoint LSN = 1000     │
│   脏页已刷盘        │
│ T10s: Redo Log写入位置: 1GB    │
│ T15s: Checkpoint推进到LSN = 500000  │
│  LSN < 500000的脏页已刷盘    │
│ T20s: 可以安全覆盖LSN < 500000的日志  │
│  因为对应的数据已持久化 ✓   │
└──────────────────────────────────────┘

Checkpoint核心数据结构 ​

Checkpoint信息存储 ​

c
/* storage/innobase/include/log0log.h */

/**
 * Checkpoint存储在Redo Log文件的固定位置
 * 每个日志文件的前两个块专门用于存储Checkpoint
 */
#define LOG_CHECKPOINT_1 0    /* 第一个Checkpoint在块0 */
#define LOG_CHECKPOINT_2 1    /* 第二个Checkpoint在块1 */

/**
 * Checkpoint数据结构
 */
struct log_checkpoint_t {
  uint64_t checkpoint_no;   /* Checkpoint编号 */
  uint64_t checkpoint_lsn;  /* Checkpoint时的LSN */
  uint64_t lsn_offset;    /* LSN在日志文件中的偏移 */
  
  /* Buffer Pool状态 */
  uint32_t n_pending_flushes; /* 待刷新的页面数 */
  uint64_t oldest_modification; /* 最旧的脏页修改LSN */
  
  /* 文件信息 */
  uint32_t log_file_number;   /* 当前日志文件编号 */
  uint64_t log_file_size;   /* 日志文件大小 */
  
  /* 校验和 */
  uint32_t checksum;    /* Checkpoint校验和 */
};

/**
 * Checkpoint写入函数
 * @param checkpoint_no Checkpoint编号
 * @param checkpoint_lsn Checkpoint LSN
 */
void checkpoint_write(
  ulint checkpoint_no,
  lsn_t checkpoint_lsn)
{
  log_checkpoint_t cp;
  byte* buf;
  
  /* 1. 填充Checkpoint结构 */
  cp.checkpoint_no = checkpoint_no;
  cp.checkpoint_lsn = checkpoint_lsn;
  cp.oldest_modification = buf_pool_get_oldest_modification();
  
  /* 2. 计算存储位置(交替使用两个Checkpoint块) */
  ulint block_no = (checkpoint_no % 2 == 0) 
    ? LOG_CHECKPOINT_1 
    : LOG_CHECKPOINT_2;
  
  /* 3. 序列化到缓冲区 */
  buf = serialize_checkpoint(&cp);
  
  /* 4. 写入日志文件头部 */
  os_file_write(
    log_sys->files[0],
    buf,
    block_no * OS_FILE_LOG_BLOCK_SIZE,
    OS_FILE_LOG_BLOCK_SIZE);
  
  /* 5. 强制刷盘 */
  os_file_flush(log_sys->files[0]);
}

Buffer Pool中的脏页管理 ​

c
/* storage/innobase/buf/buf0flu.cc */

/**
 * 获取Buffer Pool中最旧的脏页修改LSN
 * @return 最旧修改LSN
 */
lsn_t buf_pool_get_oldest_modification(void)
{
  lsn_t min_lsn = ULINT_MAX;
  
  /* 遍历所有Buffer Pool实例 */
  for (ulint i = 0; i < srv_buf_pool_instances; i++) {
    buf_pool_t* buf_pool = buf_pool_from_array(i);
    
    mutex_enter(&buf_pool->flush_state_mutex);
    
    /* 获取flush_list中的第一个节点(最旧的脏页) */
    buf_page_t* bpage = UT_LIST_GET_FIRST(buf_pool->flush_list);
    
    if (bpage != NULL) {
    lsn_t oldest = bpage->oldest_modification;
    if (oldest < min_lsn) {
      min_lsn = oldest;
    }
    }
    
    mutex_exit(&buf_pool->flush_state_mutex);
  }
  
  return min_lsn;
}

/**
 * 脏页链表(Flush List)
 * 按oldest_modification LSN排序
 */
struct buf_page_t {
  /* ... 其他字段 ... */
  
  lsn_t oldest_modification;  /* 该页第一次被修改时的LSN */
  lsn_t newest_modification;  /* 该页最后一次被修改时的LSN */
  
  /* flush_list链表节点 */
  UT_LIST_NODE_T(buf_page_t, list) flush_list;
};

InnoDB源码分析 ​

模糊检查点主流程 ​

cpp
/* storage/innobase/log/log0chkp.cc */

/**
 * 执行模糊检查点
 * @return 成功返回true
 */
bool log_checkpoint_make(bool sync)
{
  lsn_t checkpoint_lsn;
  lsn_t oldest_lsn;
  
  /* 1. 获取当前LSN */
  mutex_enter(&log_sys->mutex);
  checkpoint_lsn = log_sys->lsn;
  
  /* 2. 获取最旧脏页修改LSN */
  oldest_lsn = buf_pool_get_oldest_modification();
  
  /* 3. 判断是否需要刷脏页 */
  if (oldest_lsn < checkpoint_lsn) {
    /* 有脏页需要刷新 */
    
    if (sync) {
    /* 同步刷新: 等待所有IO完成 */
    buf_flush_list(
      ULONG_MAX,     /* 刷新所有脏页 */
      checkpoint_lsn,  /* 目标LSN */
      TRUE);     /* 同步模式 */
    } else {
    /* 异步刷新: 后台继续 */
    buf_flush_list(
      srv_max_dirty_pages_pct_lwm, /* 刷新到阈值以下 */
      checkpoint_lsn,
      FALSE);      /* 异步模式 */
    }
  }
  
  /* 4. 更新Checkpoint编号 */
  log_sys->checkpoint_no++;
  
  /* 5. 写入Checkpoint信息到日志文件 */
  checkpoint_write(
    log_sys->checkpoint_no,
    checkpoint_lsn);
  
  mutex_exit(&log_sys->mutex);
  
  return true;
}

/**
 * Master Thread中的Checkpoint逻辑
 * 每秒执行一次
 */
void master_thread_checkpoint_loop()
{
  while (srv_shutdown_state == SRV_SHUTDOWN_NONE) {
    /* 睡眠1秒 */
    os_thread_sleep(1000000);
    
    /* 1. 检查脏页比例 */
    ulint dirty_pct = buf_get_modified_ratio_pct();
    
    if (dirty_pct > srv_max_dirty_pages_pct) {
    /* 脏页过多,触发Checkpoint */
    log_checkpoint_make(FALSE); /* 异步 */
    }
    
    /* 2. 检查Redo Log使用率 */
    ulint log_pct = log_calc_usage_pct();
    
    if (log_pct > 75) {
    /* Redo Log使用超过75%,紧急Checkpoint */
    log_checkpoint_make(TRUE); /* 同步 */
    }
  }
}

脏页刷新算法 ​

cpp
/* storage/innobase/buf/buf0flu.cc */

/**
 * 刷新脏页到磁盘
 * @param max_n_pages 最多刷新页数
 * @param lsn_limit 只刷新oldest_modification < lsn_limit的页
 * @param sync 是否同步等待
 * @return 实际刷新页数
 */
ulint buf_flush_list(
  ulint max_n_pages,
  lsn_t lsn_limit,
  bool sync)
{
  ulint flushed_count = 0;
  buf_page_t* bpage;
  
  mutex_enter(&buf_pool->flush_list_mutex);
  
  /* 1. 从flush_list头部开始遍历(最旧的脏页) */
  bpage = UT_LIST_GET_FIRST(buf_pool->flush_list);
  
  while (bpage != NULL && flushed_count < max_n_pages) {
    /* 2. 检查LSN条件 */
    if (bpage->oldest_modification >= lsn_limit) {
    break; /* 后面的页都不需要刷新 */
    }
    
    /* 3. 尝试获取页面锁 */
    if (!mutex_trylock(&bpage->mutex)) {
    /* 页面正被使用,跳过 */
    bpage = UT_LIST_GET_NEXT(flush_list, bpage);
    continue;
    }
    
    /* 4. 再次检查页面状态 */
    if (buf_page_is_dirty(bpage) &&
    bpage->oldest_modification < lsn_limit) {
    
    /* 5. 发起异步IO写请求 */
    buf_flush_page_and_try_neighbors(bpage);
    
    flushed_count++;
    }
    
    mutex_unlock(&bpage->mutex);
    
    /* 6. 移动到下一个脏页 */
    bpage = UT_LIST_GET_NEXT(flush_list, bpage);
  }
  
  mutex_exit(&buf_pool->flush_list_mutex);
  
  /* 7. 同步模式下等待所有IO完成 */
  if (sync && flushed_count > 0) {
    buf_flush_wait_batch_end(flushed_count);
  }
  
  return flushed_count;
}

/**
 * 刷新页面及其邻居页(LRU优化)
 */
void buf_flush_page_and_try_neighbors(buf_page_t* bpage)
{
  ulint space = bpage->space;
  ulint offset = bpage->offset;
  
  /* 1. 计算相邻页面范围 */
  ulint start_offset = offset - (offset % 8); /* 对齐到8页边界 */
  
  /* 2. 批量刷新连续脏页 */
  for (ulint i = 0; i < 8; i++) {
    ulint curr_offset = start_offset + i;
    
    buf_page_t* curr_bpage = buf_page_hash_get(space, curr_offset);
    
    if (curr_bpage != NULL && buf_page_is_dirty(curr_bpage)) {
    /* 3. 加入IO队列 */
    buf_flush_insert_into_io_queue(curr_bpage);
    }
  }
}

崩溃恢复中的Checkpoint应用 ​

cpp
/* storage/innobase/log/log0recv.cc */

/**
 * 崩溃恢复: 找到最后一个有效的Checkpoint
 * @return Checkpoint LSN
 */
lsn_t recv_find_last_checkpoint()
{
  log_checkpoint_t cp1, cp2;
  
  /* 1. 读取两个Checkpoint */
  cp1 = checkpoint_read(LOG_CHECKPOINT_1);
  cp2 = checkpoint_read(LOG_CHECKPOINT_2);
  
  /* 2. 验证校验和 */
  bool cp1_valid = checkpoint_validate_checksum(&cp1);
  bool cp2_valid = checkpoint_validate_checksum(&cp2);
  
  /* 3. 选择最新的 valid Checkpoint */
  log_checkpoint_t* last_cp;
  
  if (cp1_valid && cp2_valid) {
    last_cp = (cp1.checkpoint_no > cp2.checkpoint_no) ? &cp1 : &cp2;
  } else if (cp1_valid) {
    last_cp = &cp1;
  } else if (cp2_valid) {
    last_cp = &cp2;
  } else {
    /* 两个都损坏,需要从数据文件扫描 */
    ut_error;
  }
  
  return last_cp->checkpoint_lsn;
}

/**
 * 崩溃恢复主流程
 */
void crash_recovery()
{
  /* 1. 找到最后一个Checkpoint */
  lsn_t checkpoint_lsn = recv_find_last_checkpoint();
  
  printf("Last checkpoint LSN: %lu\n", checkpoint_lsn);
  
  /* 2. 从Checkpoint LSN开始扫描Redo Log */
  lsn_t start_lsn = checkpoint_lsn;
  lsn_t end_lsn = log_sys->lsn;
  
  printf("Recovering from LSN %lu to %lu\n", start_lsn, end_lsn);
  
  /* 3. REDO阶段: 重放日志 */
  recv_recover_from_log(start_lsn, end_lsn);
  
  /* 4. UNDO阶段: 回滚未提交事务 */
  recv_rollback_uncommitted_transactions();
  
  printf("Crash recovery completed!\n");
}

/**
 * 从日志恢复
 */
void recv_recover_from_log(lsn_t start_lsn, lsn_t end_lsn)
{
  ulint n_recovered = 0;
  
  /* 1. 定位到起始LSN */
  log_sys->buf_free = start_lsn % log_sys->size;
  
  /* 2. 逐条读取Redo Record */
  while (log_sys->buf_free < end_lsn % log_sys->size) {
    redo_record_t* rec = log_read_redo_record();
    
    if (rec == NULL) {
    break;
    }
    
    /* 3. 应用Redo Record到数据页 */
    recv_apply_redo_record(rec);
    
    n_recovered++;
  }
  
  printf("Recovered %lu pages\n", n_recovered);
}

Checkpoint工作流程 ​

完整生命周期 ​

MySQL启动
  ↓
┌──────────────────────────────────────────┐
│ 锐利检查点 (Sharp Checkpoint)    │
│ - 等待所有事务完成         │
│ - 将所有脏页刷盘         │
│ - 记录Checkpoint LSN       │
│ - 恢复时间 = 0         │
└──────────────────────────────────────────┘
  ↓
┌──────────────────────────────────────────┐
│ MySQL正常运行           │
│              │
│ 每秒检查(Master Thread):       │
│ IF 脏页比例 > 75% THEN       │
│   触发模糊检查点        │
│ END IF           │
│              │
│ IF Redo Log使用 > 75% THEN     │
│   触发紧急检查点        │
│ END IF           │
└──────────────────────────────────────────┘
  ↓
┌──────────────────────────────────────────┐
│ 模糊检查点执行 (Fuzzy Checkpoint)    │
│              │
│ Step 1: 获取当前LSN        │
│ Step 2: 获取最旧脏页LSN      │
│ Step 3: 异步刷新脏页到数据文件    │
│ Step 4: 更新Checkpoint编号     │
│ Step 5: 写入Checkpoint信息到日志文件头部  │
└──────────────────────────────────────────┘
  ↓
┌──────────────────────────────────────────┐
│ 系统崩溃            │
│              │
│ 恢复流程:            │
│ 1. 读取最后一个Checkpoint LSN    │
│ 2. 从Checkpoint LSN开始重放Redo Log │
│ 3. 只重放几百条记录(而非几百万条)   │
│ 4. 回滚未提交事务         │
│ 5. 数据库恢复一致状态       │
└──────────────────────────────────────────┘

Checkpoint与Redo Log的关系 ​

Redo Log文件布局:
┌──────────────────────────────────────────────────────┐
│ Block 0: Checkpoint #1           │
│ checkpoint_no = 1000           │
│ checkpoint_lsn = 500000          │
│ oldest_modification = 450000         │
├──────────────────────────────────────────────────────┤
│ Block 1: Checkpoint #2 (备用)        │
│ checkpoint_no = 999            │
│ checkpoint_lsn = 490000          │
├──────────────────────────────────────────────────────┤
│ Block 2+: Redo Records           │
│ LSN 450000: MLOG_4BYTES page=100 offset=50 ...  │
│ LSN 450001: MLOG_REC_INSERT page=200 ...    │
│ ...                │
│ LSN 500000: ← Checkpoint标记这里之前的都已刷盘   │
│ ...                │
│ LSN 600000: ← 当前写入位置         │
└──────────────────────────────────────────────────────┘

关键关系:
- Checkpoint LSN = 500000
- 当前LSN = 600000
- 最旧脏页LSN = 450000

含义:
✓ LSN < 500000的所有脏页已在数据文件中
✗ LSN 450000 ~ 500000的脏页还在Buffer Pool中
✓ 崩溃后只需重放LSN 500000 ~ 600000的日志

监控与调优 ​

监控指标 ​

sql
-- 1. 查看Checkpoint状态
SHOW ENGINE INNODB STATUS\G

-- 输出:
--- LOG ---
Log sequence number    1234567890  -- 当前LSN
Log flushed up to    1234500000  -- 已刷盘的LSN
Pages flushed up to    1234400000  -- Checkpoint LSN
Last checkpoint at     1234400000

-- 计算:
-- Checkpoint年龄 = 1234567890 - 1234400000 = 167890 bytes
-- Redo Log使用率 = 167890 / (2 * 1024 * 1024 * 1024) ≈ 0.008%

-- 2. 监控脏页比例
SELECT 
  pool_id,
  pool_size,
  free_buffers,
  database_pages,
  modified_database_pages,
  ROUND(modified_database_pages * 100.0 / pool_size, 2) AS dirty_pct
FROM information_schema.INNODB_BUFFER_POOL_STATS;

-- 输出:
-- +---------+-----------+--------------+------------------+-----------------------+-----------+
-- | pool_id | pool_size | free_buffers | database_pages | modified_database_pages| dirty_pct |
-- +---------+-----------+--------------+------------------+-----------------------+-----------+
-- |   0 |   65536 |    10000 |    50000 |      5000 |  7.62%|
-- +---------+-----------+--------------+------------------+-----------------------+-----------+

-- 3. 实时监控Checkpoint活动
SHOW GLOBAL STATUS LIKE 'Innodb_checkpoint_age';
SHOW GLOBAL STATUS LIKE 'Innodb_max_checkpoint_age';

-- Checkpoint年龄 = 当前LSN - Checkpoint LSN
-- 最大年龄 = Redo Log总容量

-- 4. Prometheus监控
-- alert: CheckpointAgeTooHigh
-- expr: innodb_checkpoint_age / innodb_max_checkpoint_age > 0.75
-- for: 5m
-- annotations:
-- summary: "Checkpoint age too high, risk of blocking"

关键配置参数 ​

ini
[mysqld]
# 脏页比例阈值(默认75%)
# 超过此值触发Checkpoint
innodb_max_dirty_pages_pct = 75

# 低水位线(默认0,禁用)
# 脏页降到此值以下停止刷新
innodb_max_dirty_pages_pct_lwm = 10

# Redo Log文件大小
# 越大,Checkpoint间隔越长
innodb_log_file_size = 2048M

# Redo Log文件数量
innodb_log_files_in_group = 2

# IO能力(影响Checkpoint刷盘速度)
innodb_io_capacity = 2000
innodb_io_capacity_max = 4000

# 自适应刷新策略(MySQL 5.7+)
# ON: 根据负载自动调整
# OFF: 固定使用innodb_max_dirty_pages_pct
innodb_adaptive_flushing = ON

性能调优案例 ​

场景1: Checkpoint频繁导致性能抖动

sql
-- 问题: 每隔几分钟就有一次IO高峰

-- 诊断:
SHOW ENGINE INNODB STATUS\G
-- 发现:
-- Checkpoint age: 1.8GB
-- Max checkpoint age: 2GB
-- 使用率: 90% (过高!)

-- 解决: 增大Redo Log
SET GLOBAL innodb_log_file_size = 4096M;
-- 需要重启MySQL

-- 效果:
-- Max checkpoint age: 4GB
-- 使用率: 45% ✓
-- Checkpoint频率降低

场景2: 崩溃恢复时间过长

sql
-- 问题: 重启需要10分钟

-- 诊断:
SHOW ENGINE INNODB STATUS\G
-- 发现:
-- Last checkpoint at: 1000000
-- Log sequence number: 100000000
-- Checkpoint age: 99MB (过大)

-- 原因: innodb_max_dirty_pages_pct = 90 (太高)

-- 解决:
SET GLOBAL innodb_max_dirty_pages_pct = 60;
SET GLOBAL innodb_io_capacity = 4000;

-- 效果:
-- Checkpoint更频繁,age保持在50MB以内
-- 恢复时间: 1分钟 ✓

最佳实践 ​

1. 生产环境配置 ​

ini
[mysqld]
# 根据写入负载调整
innodb_log_file_size = 2048M      # 高写入用4GB
innodb_log_files_in_group = 2
innodb_max_dirty_pages_pct = 75
innodb_max_dirty_pages_pct_lwm = 10   # 启用低水位
innodb_io_capacity = 2000       # SSD用4000+
innodb_io_capacity_max = 4000
innodb_adaptive_flushing = ON

2. 批量导入优化 ​

sql
-- 临时禁用自适应刷新
SET GLOBAL innodb_adaptive_flushing = OFF;
SET GLOBAL innodb_max_dirty_pages_pct = 90;

-- 执行批量导入
LOAD DATA INFILE '/data/huge.csv' INTO TABLE huge_table;

-- 手动触发Checkpoint
SET GLOBAL innodb_adaptive_flushing = ON;
CHECKPOINT;  -- 或通过FLUSH TABLES间接触发

-- 恢复配置
SET GLOBAL innodb_max_dirty_pages_pct = 75;

3. 快速关闭优化 ​

bash
# 正常关闭: 执行Sharp Checkpoint(慢)
mysqladmin shutdown

# 快速关闭: 跳过Checkpoint(快,但下次启动恢复时间长)
mysqladmin shutdown --fast

# 紧急关闭: 直接kill(危险!)
kill -9 $(pidof mysqld)

参考资料 ​

MySQL官方文档 ​

源码文件 ​

  • storage/innobase/log/log0chkp.cc - Checkpoint实现
  • storage/innobase/buf/buf0flu.cc - 脏页刷新
  • storage/innobase/log/log0recv.cc - 崩溃恢复

相关术语 ​


版本历史:

  • 2026-04-12: 初始版本,全面讲解Checkpoint机制

Released under MIT License.