存储层 #
一、存储层概述 #
1.1 存储架构 #
text
存储层架构
┌─────────────────────────────────────────────────────────────┐
│ │
│ 事务层 │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ MVCC 层 │ │
│ │ 多版本并发控制 │ │
│ └─────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ Pebble 存储引擎 │ │
│ │ LSM-Tree 结构 │ │
│ └─────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ 文件系统 │ │
│ │ ext4 / xfs │ │
│ └─────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ 磁盘存储 │
│ │
└─────────────────────────────────────────────────────────────┘
1.2 核心组件 #
| 组件 | 职责 |
|---|---|
| MVCC | 多版本并发控制 |
| Pebble | 存储引擎 |
| Block Cache | 数据缓存 |
| WAL | 预写日志 |
二、Pebble存储引擎 #
2.1 LSM-Tree结构 #
text
LSM-Tree 结构
┌─────────────────────────────────────────────────────────────┐
│ │
│ 写入流程: │
│ │
│ 写入请求 │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ Write Ahead Log (WAL) │ │
│ │ 顺序写入,保证持久性 │ │
│ └─────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ MemTable (Active) │ │
│ │ 内存中的有序表,新数据写入 │ │
│ │ 默认大小: 4MB │ │
│ └─────────────────────────────────────────────────────┘ │
│ │ │
│ │ MemTable 满了 │
│ ▼ │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ MemTable (Immutable) │ │
│ │ 等待刷盘的内存表 │ │
│ └─────────────────────────────────────────────────────┘ │
│ │ │
│ │ 后台刷盘 │
│ ▼ │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ SSTable (Sorted String Table) │ │
│ │ │ │
│ │ Level 0: 直接从 MemTable 刷盘 │ │
│ │ Level 1-6: 合并压缩后的文件 │ │
│ │ │ │
│ │ ┌─────────────────────────────────────────────┐ │ │
│ │ │ L0: 文件可能有重叠,查询需要检查多个文件 │ │ │
│ │ │ L1-L6: 文件有序,查询效率高 │ │ │
│ │ └─────────────────────────────────────────────┘ │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────┘
2.2 读取流程 #
text
读取流程
┌─────────────────────────────────────────────────────────────┐
│ │
│ 读取请求 │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ Block Cache │ │
│ │ 检查缓存 │ │
│ └─────────────────────────────────────────────────────┘ │
│ │ │
│ │ 未命中 │
│ ▼ │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ MemTable (Active) │ │
│ │ 检查活跃内存表 │ │
│ └─────────────────────────────────────────────────────┘ │
│ │ │
│ │ 未找到 │
│ ▼ │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ MemTable (Immutable) │ │
│ │ 检查不可变内存表 │ │
│ └─────────────────────────────────────────────────────┘ │
│ │ │
│ │ 未找到 │
│ ▼ │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ SSTable (L0 → L1 → ... → L6) │ │
│ │ 从 L0 开始逐层查找 │ │
│ └─────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ 返回结果 │
│ │
└─────────────────────────────────────────────────────────────┘
2.3 Compaction #
text
Compaction (压缩合并)
┌─────────────────────────────────────────────────────────────┐
│ │
│ 目的: │
│ ├── 合并多个 SSTable │
│ ├── 删除过期数据 │
│ ├── 清理已删除数据 │
│ └── 减少查询需要检查的文件 │
│ │
│ 触发条件: │
│ ├── L0 文件数超过阈值 │
│ ├── 某层大小超过阈值 │
│ └── 手动触发 │
│ │
│ 过程: │
│ │
│ L0 → L1: │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ L0: [A] [B] [C] (可能有重叠) │ │
│ │ │ │ │
│ │ ▼ │ │
│ │ L1: [D] [E] [F] (无重叠) │ │
│ │ │ │ │
│ │ ▼ │ │
│ │ 合并后: [G] [H] [I] [J] │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
│ 写放大: │
│ 每层数据量是上一层的10倍 │
│ 总写放大: ~10x * 7层 = ~50-100x │
│ │
└─────────────────────────────────────────────────────────────┘
三、MVCC实现 #
3.1 多版本数据 #
text
MVCC 数据结构
┌─────────────────────────────────────────────────────────────┐
│ │
│ Key-Value 存储: │
│ │
│ Key: /Table/<table_id>/<index_id>/<primary_key> │
│ Value: <timestamp> + <value> │
│ │
│ 示例: │
│ │
│ Key: /Table/53/1/1 │
│ │
│ 版本历史: │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ Timestamp: 100 │ │
│ │ Value: {id: 1, name: "Alice", age: 25} │ │
│ ├─────────────────────────────────────────────────────┤ │
│ │ Timestamp: 50 │ │
│ │ Value: {id: 1, name: "Alice", age: 24} │ │
│ ├─────────────────────────────────────────────────────┤ │
│ │ Timestamp: 10 │ │
│ │ Value: {id: 1, name: "Alice", age: 23} │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
│ 读取时: │
│ ├── 根据时间戳选择版本 │
│ └── 默认读取最新版本 │
│ │
└─────────────────────────────────────────────────────────────┘
3.2 时间戳 #
sql
-- HLC (混合逻辑时钟)
-- 结合物理时钟和逻辑时钟
-- 时间旅行查询
SELECT * FROM users
AS OF SYSTEM TIME '-10s';
-- 查看历史数据
SELECT * FROM users
AS OF SYSTEM TIME '2024-01-01 00:00:00';
-- 使用时间戳
SELECT * FROM users
AS OF SYSTEM TIME 1234567890;
3.3 垃圾回收 #
text
MVCC 垃圾回收
┌─────────────────────────────────────────────────────────────┐
│ │
│ GC 配置: │
│ ├── 默认保留时间: 25小时 │
│ ├── 定期执行GC │
│ └── 清理旧版本数据 │
│ │
│ 配置GC策略: │
│ │
│ ALTER DATABASE defaultdb │
│ CONFIGURE ZONE USING │
│ gc.ttlseconds = 86400; -- 24小时 │
│ │
│ GC 流程: │
│ │
│ 1. 扫描Range │
│ │ │
│ ▼ │
│ 2. 识别过期版本 │
│ │ │
│ ▼ │
│ 3. 删除旧版本 │
│ │ │
│ ▼ │
│ 4. 触发Compaction │
│ │
└─────────────────────────────────────────────────────────────┘
四、Block Cache #
4.1 缓存结构 #
text
Block Cache 结构
┌─────────────────────────────────────────────────────────────┐
│ │
│ 缓存层次: │
│ │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ SSTable Block Cache │ │
│ │ 缓存数据块 │ │
│ │ 默认大小: 总内存的 50% │ │
│ └─────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ Index Block Cache │ │
│ │ 缓存索引块 │ │
│ └─────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ Filter Block Cache │ │
│ │ 缓存布隆过滤器 │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
│ 缓存策略: │
│ ├── LRU (最近最少使用) │
│ └── 分片缓存 (减少锁竞争) │
│ │
└─────────────────────────────────────────────────────────────┘
4.2 缓存配置 #
bash
# 启动时配置缓存大小
cockroach start --cache=4GB
# 推荐配置
# cache = 总内存的 50%
# 例如: 16GB 内存 -> 8GB cache
五、WAL预写日志 #
5.1 WAL机制 #
text
WAL 机制
┌─────────────────────────────────────────────────────────────┐
│ │
│ 写入流程: │
│ │
│ 写入请求 │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ 写入 WAL │ │
│ │ 顺序写入磁盘 │ │
│ │ 保证持久性 │ │
│ └─────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ 写入 MemTable │ │
│ │ 内存写入 │ │
│ └─────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ 返回成功 │
│ │
│ 恢复流程: │
│ │
│ 节点重启 │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ 读取 WAL │ │
│ │ 重放日志 │ │
│ └─────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ 恢复 MemTable │ │
│ │ 重建内存状态 │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────┘
5.2 WAL配置 #
bash
# WAL 目录配置
cockroach start --store=path=/data,wal-dir=/wal
# 同步配置
--wal-sync-interval=32KB
六、数据编码 #
6.1 Key编码 #
text
Key 编码格式
┌─────────────────────────────────────────────────────────────┐
│ │
│ 主键索引: │
│ /Table/<table_id>/<index_id>/<primary_key> │
│ │
│ 示例: │
│ /Table/53/1/1 (表53, 主键索引, 主键值1) │
│ /Table/53/1/'user1' (字符串主键) │
│ │
│ 二级索引: │
│ /Table/<table_id>/<index_id>/<index_value>/<primary_key> │
│ │
│ 示例: │
│ /Table/53/2/'user@example.com'/1 │
│ (表53, 索引2, 邮箱值, 主键) │
│ │
│ 编码特点: │
│ ├── 有序编码 │
│ ├── 支持范围扫描 │
│ └── 前缀压缩 │
│ │
└─────────────────────────────────────────────────────────────┘
6.2 Value编码 #
text
Value 编码格式
┌─────────────────────────────────────────────────────────────┐
│ │
│ 格式: <timestamp> + <value> │
│ │
│ Timestamp: │
│ ├── 事务时间戳 │
│ └── 用于MVCC版本控制 │
│ │
│ Value: │
│ ├── 行数据编码 │
│ ├── 列值序列化 │
│ └── 支持NULL值 │
│ │
│ 示例: │
│ Timestamp: 1234567890.123456789 │
│ Value: {id: 1, name: "Alice", age: 25} │
│ │
└─────────────────────────────────────────────────────────────┘
七、存储配置 #
7.1 存储参数 #
bash
# 启动参数
cockroach start \
--store=path=/data,size=100GB \
--cache=8GB \
--max-sql-memory=4GB
# 存储选项
--store=path=<path>,size=<size>,ballast-size=<size>
# 缓存选项
--cache=<size>
# SQL内存选项
--max-sql-memory=<size>
7.2 性能调优 #
text
存储性能调优
┌─────────────────────────────────────────────────────────────┐
│ │
│ 硬件建议: │
│ ├── SSD (推荐 NVMe) │
│ ├── 足够的内存 │
│ └── 独立的WAL磁盘 │
│ │
│ 配置建议: │
│ ├── cache = 总内存的 50% │
│ ├── max-sql-memory = 总内存的 25% │
│ └── wal-dir = 独立磁盘 │
│ │
│ 监控指标: │
│ ├── 缓存命中率 │
│ ├── Compaction速度 │
│ ├── 写放大 │
│ └── 磁盘IOPS │
│ │
└─────────────────────────────────────────────────────────────┘
八、总结 #
存储层要点:
| 组件 | 功能 |
|---|---|
| Pebble | LSM-Tree存储引擎 |
| MVCC | 多版本并发控制 |
| Block Cache | 数据缓存 |
| WAL | 预写日志 |
下一步,让我们学习分布式层!
最后更新:2026-03-27