Skip to content

Latest commit

 

History

History
474 lines (342 loc) · 10.9 KB

File metadata and controls

474 lines (342 loc) · 10.9 KB

WiredTiger Insert-Delete History Store 演示程序说明

📋 程序概述

本程序 (ex_insert_delete_history.c) 演示了在 WiredTiger 中通过持续的插入-删除操作,如何产生历史版本数据并存储在 WiredTigerHS.wt (History Store) 中。

这模拟了 MongoDB 启用 minSnapshotHistoryWindowInSeconds 功能后的行为。

🎯 功能特性

核心功能

  1. 生成复合Key

    • 格式:aaaaaaaaaaaaaaaa_X_bbbbbbbbbbbbbbbb_Y_cccccccccccccccc_Z
    • X: 随机取值 1-1000
    • Y: 随机取值 1-2000
    • Z: 单调递增(从1开始)
  2. 插入-删除循环

    • 每次循环:先INSERT(空值),然后立即DELETE
    • 每个操作都使用独立的时间戳
    • 使用事务确保一致性
  3. 历史窗口管理(模拟 minSnapshotHistoryWindowInSeconds):

    • 维持约60秒的历史快照窗口
    • 定期推进 oldest_timestamp
    • 保留最近60秒内的所有历史版本
  4. 监控和统计

    • 实时显示文件大小变化
    • 显示 History Store 统计信息
    • 演示历史清理过程

📂 文件说明

wiredtiger-11.3.1/examples/c/
├── ex_insert_delete_history.c          # 主程序源码
├── build_and_run_insert_delete.sh      # 编译和运行脚本
└── wt_insert_delete_db/                # 程序生成的数据库目录
    ├── WiredTigerHS.wt                 # History Store 文件
    ├── insert_delete_test.wt           # 主表文件
    └── ...                             # 其他 WiredTiger 文件

🔧 编译和运行

方法1:使用自动脚本(推荐)

cd /data/reading-and-annotate-wiredtiger-11.3.1/wiredtiger-11.3.1/examples/c

# 赋予执行权限
chmod +x build_and_run_insert_delete.sh

# 编译并运行
./build_and_run_insert_delete.sh

方法2:手动编译

cd /data/reading-and-annotate-wiredtiger-11.3.1/wiredtiger-11.3.1/examples/c

# 编译
gcc -o ex_insert_delete_history \
    ex_insert_delete_history.c \
    -I../../ \
    -L../../.libs \
    -lwiredtiger \
    -lpthread \
    -Wall \
    -O2

# 设置库路径
export LD_LIBRARY_PATH=../../.libs:$LD_LIBRARY_PATH

# 运行
./ex_insert_delete_history

📊 程序输出说明

1. 初始化阶段

========================================
WiredTiger Insert-Delete History Demo
========================================
Configuration:
  - Operation count: 10000
  - Snapshot window: 60 seconds
  - Checkpoint interval: every 100 operations
  - Database directory: wt_insert_delete_db
  - Key format: aaaaaaaaaaaaaaaa_X_bbbbbbbbbbbbbbbb_Y_cccccccccccccccc_Z

2. 操作进度

Step 3: Starting insert-delete operations (10000 iterations)...

  Completed 100 insert-delete cycles (z_counter: 100, timestamp: 201)
  
  [Progress Update]
    WiredTigerHS.wt (History Store): 24.5 KB
    insert_delete_test.wt (Table):   16.0 KB
    Total: 40.5 KB

  [Timestamp Management] Advanced oldest_timestamp to 201 
  (keeping ~60 second history window)

3. 文件大小变化

程序会在不同阶段显示文件大小:

  • Initial State: 初始状态(几乎为空)
  • Progress Update: 每100次操作后的中间状态
  • After All Operations: 所有操作完成后
  • After History Cleanup: 清理历史后
  • After Compact: 压缩后
  • Final State: 最终状态

4. 统计信息

History Store Statistics:
  history store operation successful: 1234
  history store table insert calls: 5678
  history store table size in bytes: 524288
  ...

🔍 关键概念解析

1. 时间戳管理

/* 插入操作的时间戳 */
current_ts++;
commit_timestamp = current_ts    // 例如: 101
stable_timestamp = current_ts    // 推进到 101

/* 删除操作的时间戳 */
current_ts++;
commit_timestamp = current_ts    // 例如: 102
stable_timestamp = current_ts    // 推进到 102

/* oldest_timestamp 管理 */
if (current_ts > 6000) {
    oldest_timestamp = current_ts - 6000  // 保持60秒窗口
}

时间戳关系

  • oldest_timestampstable_timestampcommit_timestamp
  • 历史窗口 = stable_timestamp - oldest_timestamp

2. 为什么会产生历史版本?

每个INSERT-DELETE循环会产生以下历史:

时间轴:
T1: INSERT key=xxx, value="" [timestamp=101]
T2: DELETE key=xxx           [timestamp=102]

在 History Store 中:
- 保存了 key=xxx 在时间戳101的INSERT版本
- 保存了 key=xxx 在时间戳102的DELETE标记

3. minSnapshotHistoryWindowInSeconds 模拟

MongoDB 的 minSnapshotHistoryWindowInSeconds 确保至少保留N秒的历史:

// MongoDB 配置
db.adminCommand({
  setParameter: 1, 
  minSnapshotHistoryWindowInSeconds: 60
})

我们的程序模拟方式:

#define SNAPSHOT_WINDOW_SECONDS 60

/* 
 * 假设: 100 个 timestamp = 1 秒逻辑时间
 * 则 60 秒 = 6000 个 timestamp
 */
if (current_ts > 6000) {
    oldest_ts = current_ts - 6000;  // 保持60秒窗口
}

4. Key 生成规则

void generate_key(char *buffer, size_t buffer_size, uint64_t z_counter) {
    int x = (rand() % 1000) + 1;      // X: 1-1000
    int y = (rand() % 2000) + 1;      // Y: 1-2000
    
    snprintf(buffer, buffer_size, 
             "aaaaaaaaaaaaaaaa_%d_bbbbbbbbbbbbbbbb_%d_cccccccccccccccc_%lu",
             x, y, z_counter);
}

示例Key

aaaaaaaaaaaaaaaa_523_bbbbbbbbbbbbbbbb_1847_cccccccccccccccc_1
aaaaaaaaaaaaaaaa_89_bbbbbbbbbbbbbbbb_356_cccccccccccccccc_2
aaaaaaaaaaaaaaaa_742_bbbbbbbbbbbbbbbb_1203_cccccccccccccccc_3
...

🧪 实验和调整

调整操作次数

修改 OPERATION_COUNT 常量:

#define OPERATION_COUNT 10000   // 改为你想要的次数

调整快照窗口

修改 SNAPSHOT_WINDOW_SECONDS

#define SNAPSHOT_WINDOW_SECONDS 60  // 改为你想要的秒数

调整Checkpoint频率

#define CHECKPOINT_INTERVAL 100  // 每N次操作做checkpoint

修改Key格式

generate_key() 函数中自定义:

/* 示例:更短的key */
snprintf(buffer, buffer_size, "key_%d_%d_%lu", x, y, z_counter);

/* 示例:更长的key */
snprintf(buffer, buffer_size, 
         "prefix_very_long_aaaaaaaaaaa_%d_middle_bbbbbbbbbb_%d_suffix_cccccc_%lu",
         x, y, z_counter);

📈 观察和分析

1. 监控 History Store 增长

# 实时监控文件大小
watch -n 1 'ls -lh wt_insert_delete_db/WiredTigerHS.wt'

# 或使用程序内置的监控(每100次操作报告一次)

2. 使用 wt 工具分析

cd wt_insert_delete_db

# 列出所有表
wt list

# 验证数据完整性
wt verify table:insert_delete_test

# 查看统计信息
wt stat

# 查看 History Store 的详细信息
wt stat file:WiredTigerHS.wt

3. 查看历史版本

# 使用 wt dump 命令
wt dump -x table:insert_delete_test

# 查看 History Store 内容(需要特殊权限)
wt dump file:WiredTigerHS.wt

🔬 与 MongoDB 的对比

相似之处

特性 本程序 MongoDB
历史存储 WiredTigerHS.wt WiredTigerHS.wt
快照窗口 SNAPSHOT_WINDOW_SECONDS minSnapshotHistoryWindowInSeconds
时间戳管理 oldest/stable/commit 相同
插入-删除 显式循环 应用层操作

差异之处

方面 本程序 MongoDB
时间映射 简化的时间戳(100ts=1秒) 实际系统时间
并发控制 单线程 多线程/多会话
Value大小 空字符串 实际文档数据
复杂度 演示级别 生产级别

📚 代码结构说明

主要函数

/* 生成复合key */
void generate_key(char *buffer, size_t buffer_size, uint64_t z_counter);

/* 管理 oldest_timestamp */
void manage_oldest_timestamp(WT_CONNECTION *conn, ...);

/* 获取文件大小 */
long get_file_size(const char *filename);

/* 打印文件大小信息 */
void print_file_sizes(const char *phase);

/* 主函数 */
int main(int argc, char *argv[]);

主要流程

1. 初始化
   ├─ 打开数据库连接
   ├─ 创建表
   └─ 打开游标

2. 插入-删除循环 (N次)
   ├─ 生成Key
   ├─ INSERT 操作 (timestamp T)
   ├─ DELETE 操作 (timestamp T+1)
   ├─ 管理 oldest_timestamp
   └─ 定期 checkpoint

3. 清理演示
   ├─ 推进 oldest_timestamp
   ├─ 执行 checkpoint
   └─ 压缩 History Store

4. 统计和报告
   ├─ 显示文件大小
   └─ 显示 History Store 统计

🐛 常见问题

Q1: 编译错误 - 找不到 wiredtiger.h

解决

# 确保在正确的目录
cd /data/reading-and-annotate-wiredtiger-11.3.1/wiredtiger-11.3.1/examples/c

# 确保 WiredTiger 已编译
cd ../../
make

Q2: 运行错误 - libwiredtiger.so not found

解决

export LD_LIBRARY_PATH=/data/reading-and-annotate-wiredtiger-11.3.1/wiredtiger-11.3.1/.libs:$LD_LIBRARY_PATH

Q3: WiredTigerHS.wt 没有增长

可能原因

  1. oldest_timestamp 没有正确设置
  2. Checkpoint 频率太低
  3. 操作次数太少

解决

  • 增加 OPERATION_COUNT
  • 减小 CHECKPOINT_INTERVAL
  • 检查 manage_oldest_timestamp() 逻辑

Q4: 程序运行很慢

优化建议

  1. 减少 OPERATION_COUNT
  2. 增大 CHECKPOINT_INTERVAL
  3. 调整 cache_size 配置

📖 进阶学习

1. 添加并发操作

创建多个线程同时执行插入-删除:

pthread_t threads[4];
for (int i = 0; i < 4; i++) {
    pthread_create(&threads[i], NULL, insert_delete_worker, ...);
}

2. 添加不同的Value大小

char large_value[1024];
memset(large_value, 'x', sizeof(large_value) - 1);
large_value[sizeof(large_value) - 1] = '\0';
cursor->set_value(cursor, large_value);

3. 实现精确的时间映射

/* 使用实际系统时间 */
struct timespec ts;
clock_gettime(CLOCK_REALTIME, &ts);
uint64_t timestamp = ts.tv_sec * 1000000000 + ts.tv_nsec;

4. 添加读取历史版本的功能

/* 在指定时间戳读取数据 */
snprintf(config, sizeof(config), "read_timestamp=%lx", old_ts);
session->begin_transaction(session, config);
cursor->search(cursor);
/* ... 读取旧版本数据 ... */
session->commit_transaction(session, NULL);

🎓 学习目标

通过本程序,您应该理解:

  1. ✅ WiredTiger 如何存储历史版本
  2. ✅ History Store (WiredTigerHS.wt) 的作用
  3. ✅ 时间戳 (oldest/stable/commit) 的管理
  4. ✅ INSERT-DELETE 操作如何产生历史
  5. ✅ minSnapshotHistoryWindowInSeconds 的工作原理
  6. ✅ 历史数据的清理机制

📞 支持和反馈

如有问题或建议,请参考:

🔗 相关资源