# 基于 CDC/Xet 的模型仓库多版本管理——完整设计方案


## 一、设计目标

| 目标 | 说明 |
|------|------|
| **高效版本管理** | 支持模型文件的无限版本迭代，`git log`、`git diff`、`git tag` 等操作保持毫秒级响应 |
| **块级去重** | 文件修改后仅上传变化的数据块（约 64KB 粒度），而非整个文件 |
| **存储空间节省** | 多版本共享相同数据块，避免冗余存储 |
| **传输效率** | 上传/下载仅传输新增/变化的数据块，典型场景下速度提升 2-3 倍 |
| **向下兼容** | 同时支持原生 Git 客户端和 Xet 增强客户端 |
| **水平扩展** | 存储层和服务层均可独立扩展 |


## 二、整体架构

```
┌─────────────────────────────────────────────────────────────────────────────┐
│                            客户端层 (Client Layer)                          │
│  ┌─────────────┐  ┌─────────────┐  ┌─────────────┐  ┌─────────────────┐  │
│  │   git-xet   │  │huggingface- │  │   Web UI    │  │  MLOps 工具链   │  │
│  │  (CLI增强)  │  │  cli/SDK    │  │   (浏览)    │  │ (MLflow/Kubeflow)│  │
│  └──────┬──────┘  └──────┬──────┘  └──────┬──────┘  └────────┬────────┘  │
└─────────┼────────────────┼────────────────┼───────────────────┼────────────┘
          │                │                │                   │
          ▼                ▼                ▼                   ▼
┌─────────────────────────────────────────────────────────────────────────────┐
│                          网关与服务层 (Service Layer)                       │
│  ┌─────────────────────────────────────────────────────────────────────┐  │
│  │                      API 网关 (Hub Gateway)                         │  │
│  │          认证、鉴权、限流、路由、请求转发                            │  │
│  └───────────────────────────────┬─────────────────────────────────────┘  │
│                                  │                                         │
│  ┌──────────────────────────────┼─────────────────────────────────────┐  │
│  │                              ▼                                      │  │
│  │  ┌──────────────────────────────────────────────────────────────┐  │  │
│  │  │              版本管理服务 (Version Manager)                   │  │  │
│  │  │  - 仓库创建/删除  - Commit 管理  - Tag/Branch 管理           │  │  │
│  │  └──────────────────────────────────────────────────────────────┘  │  │
│  │                              │                                      │  │
│  │  ┌──────────────────────────┼──────────────────────────────────┐  │  │
│  │  │                          ▼                                   │  │  │
│  │  │  ┌──────────────────────────────────────────────────────┐  │  │  │
│  │  │  │          映射服务 (Mapping Service)                  │  │  │  │
│  │  │  │  - Commit → 文件清单映射  - 块哈希 → 存储位置查询    │  │  │  │
│  │  │  │  - 全局块去重查询  - 文件重建元数据管理              │  │  │  │
│  │  │  └──────────────────────────────────────────────────────┘  │  │  │
│  │  └──────────────────────────────────────────────────────────────┘  │  │
│  └─────────────────────────────────────────────────────────────────────┘  │
└─────────────────────────────────────────────────────────────────────────────┘
          │                              │                              │
          ▼                              ▼                              ▼
┌─────────────────────────────────────────────────────────────────────────────┐
│                              存储层 (Storage Layer)                        │
│                                                                             │
│  ┌─────────────────────────────┐  ┌─────────────────────────────────────┐  │
│  │    元数据仓库 (Git Repo)     │  │     内容寻址存储 (CAS)              │  │
│  │                             │  │                                     │  │
│  │  - 指针文件 (.ptr)          │  │  ┌─────────────────────────────┐  │  │
│  │  - 配置文件 (config.json)   │  │  │    Xorb 聚合层              │  │  │
│  │  - 模型卡片 (README.md)     │  │  │  (64MB/8,192个块) │  │  │
│  │  - .gitattributes           │  │  └──────────┬──────────────────┘  │  │
│  │  - 完整 Git 历史            │  │             │                       │  │
│  │                             │  │  ┌──────────▼──────────────────┐  │  │
│  │  托管: GitHub/GitLab/自建   │  │  │    数据块 (Chunks)          │  │  │
│  │                             │  │  │  目标 64KB, 范围 8-128KB   │  │  │
│  └─────────────────────────────┘  │  │  标识: Blake3 MerkleHash│  │
│                                    │  └──────────┬──────────────────┘  │  │
│                                    │             │                       │
│                                    │  ┌──────────▼──────────────────┐  │  │
│                                    │  │   对象存储 (S3/MinIO/Ceph)  │  │  │
│                                    │  │   持久化所有数据块和元数据   │  │  │
│                                    │  └─────────────────────────────┘  │  │
│                                    │                                     │
│                                    │  LFS 桥接层 (向下兼容)    │  │
│                                    └─────────────────────────────────────┘  │
└─────────────────────────────────────────────────────────────────────────────┘
```


## 三、核心组件详解

### 3.1 客户端 (Xet-Enabled Client)

客户端的核心职责是在 `git push` 时拦截并执行块级去重上传。

```
┌──────────────────────────────────────────────────────┐
│                   Xet 客户端架构                      │
├──────────────────────────────────────────────────────┤
│  ┌────────────────────────────────────────────────┐ │
│  │           Git 钩子拦截器 (pre-push)            │ │
│  │  检测 push 事件，接管大文件处理                 │ │
│  └──────────────────┬─────────────────────────────┘ │
│                     ▼                               │
│  ┌────────────────────────────────────────────────┐ │
│  │           CDC 分块引擎 (Chunker)               │ │
│  │  - 读取文件流  - GearHash 滚动哈希             │ │
│  │  - 确定块边界  - 输出可变大小块 (8-128KB)      │ │
│  └──────────────────┬─────────────────────────────┘ │
│                     ▼                               │
│  ┌────────────────────────────────────────────────┐ │
│  │           哈希计算与去重引擎                    │ │
│  │  - Blake3 MerkleHash 计算  - 本地缓存查询      │ │
│  │  - 与服务端全局索引比对  - 识别新增块           │ │
│  └──────────────────┬─────────────────────────────┘ │
│                     ▼                               │
│  ┌────────────────────────────────────────────────┐ │
│  │           Xorb 聚合器 (Aggregator)             │ │
│  │  将多个小块聚合成 64MB 的 Xorb 对象   │ │
│  └──────────────────┬─────────────────────────────┘ │
│                     ▼                               │
│  ┌────────────────────────────────────────────────┐ │
│  │           上传调度器 (Uploader)                 │ │
│  │  - 并发上传 Xorb  - 断点续传  - 校验重试       │ │
│  └────────────────────────────────────────────────┘ │
└──────────────────────────────────────────────────────┘
```

**参考实现**：Hugging Face 的参考实现使用 Rust 编写，位于 [xet-core](https://github.com/huggingface/xet-core)。

### 3.2 元数据仓库 (Git Repository)

存放所有**文本形态**的元数据，保持轻量高效：

| 文件类型 | 示例 | 大小 |
|----------|------|------|
| 指针文件 | `model.safetensors.ptr` | ~200 字节 |
| 模型配置 | `config.json` | ~几 KB |
| 模型卡片 | `README.md` | ~几 KB |
| 属性配置 | `.gitattributes` | ~几十字节 |

**指针文件格式示例**（伪代码）：
```
version https://xet-spec.com/v1.0
oid sha256:3b8a5921e2c... (文件内容哈希)
size 10737418240
xet_file_id xet_abc123def456
```

指针文件仅记录文件的逻辑标识（`xet_file_id`），不包含任何块级别的映射信息。真正的块映射存储在映射服务中。

### 3.3 内容寻址存储 (CAS)

CAS 是 Xet 系统的核心存储基础设施：

**核心特性**：
- **内容寻址**：所有对象通过其加密哈希进行存储和检索
- **不可变性**：内容一旦存储即不可更改
- **自动去重**：相同内容在存储级别自动去重

**存储层级**：

```
┌─────────────────────────────────────────────────────────┐
│                    CAS 存储层级                         │
├─────────────────────────────────────────────────────────┤
│                                                         │
│  Level 1: 数据块 (Chunk)                                │
│  ├─ 大小: 8KB ~ 128KB (目标 64KB)              │
│  ├─ 标识: Blake3 MerkleHash                    │
│  └─ 内容: 原始文件数据片段                              │
│                                                         │
│  Level 2: Xorb (聚合块)                    │
│  ├─ 最大大小: 64MB                                      │
│  ├─ 最大块数: 8,192 个                                 │
│  └─ 作用: 减少元数据和网络开销                          │
│                                                         │
│  Level 3: Shard (碎片)                     │
│  ├─ 最大大小: 64MB                                      │
│  └─ 内容: Xorb 列表 + 文件重建元数据                    │
│                                                         │
│  Level 4: 对象存储 (S3/MinIO)              │
│  └─ 最终持久化层                                        │
│                                                         │
└─────────────────────────────────────────────────────────┘
```

### 3.4 映射服务 (Mapping Service)

维护 **Commit → 文件 → 块列表** 的三级映射关系：

**数据模型**：

```sql
-- 仓库表
CREATE TABLE repositories (
    repo_id UUID PRIMARY KEY,
    repo_name VARCHAR(255) UNIQUE,
    created_at TIMESTAMP
);

-- Commit 映射表
CREATE TABLE commit_mappings (
    commit_hash CHAR(40) PRIMARY KEY,   -- Git commit SHA
    repo_id UUID REFERENCES repositories,
    parent_hash CHAR(40),               -- 父 commit
    file_manifest_id UUID,              -- 指向文件清单
    created_at TIMESTAMP
);

-- 文件清单表
CREATE TABLE file_manifests (
    manifest_id UUID PRIMARY KEY,
    file_path VARCHAR(512),
    file_size BIGINT,
    xet_file_id VARCHAR(64),            -- Xet 文件唯一标识
    chunk_list JSONB,                   -- [offset, hash, size]
    -- 示例: [{"offset":0, "hash":"abc...", "size":65536}, ...]
);

-- 全局块索引表 (用于去重查询)
CREATE TABLE global_chunks (
    chunk_hash CHAR(64) PRIMARY KEY,    -- Blake3 哈希
    storage_location VARCHAR(255),      -- S3 路径或 CAS 引用
    size INT,
    ref_count INT DEFAULT 1             -- 引用计数
);
```

**关键操作**：

1. **去重查询**：客户端上传时，将块哈希列表发送给映射服务，服务返回哪些块已存在
2. **映射注册**：新 commit 创建时，注册文件→块的映射关系
3. **文件重建**：下载时，根据 commit_hash 查询完整的块列表和存储位置


## 四、CDC 分块算法

### 4.1 算法选择：GearHash

Xet 协议规范中指定使用 **GearHash** 算法进行内容定义分块。

**核心原理**：

CDC 的关键属性是**边界稳定性**：分块边界由数据内容通过滚动哈希确定，而非固定的字节偏移量。

```
原始数据:  ···AAAAAA|BBBBBBBB|CCCCCCCC|DDDDDDDD|EEEEEEEE···
           chunk0   chunk1    chunk2    chunk3    chunk4

在 chunk2 中插入 "XX":
           ···AAAAAA|BBBBBBBB|CCCCXXCCCC|DDDDDDDD|EEEEEEEE···
           chunk0   chunk1    chunk2'    chunk3    chunk4
           (相同)   (相同)    (变化)     (相同)    (相同)
```

**固定大小分块的问题**（对比）：

```
固定大小分块 - 在 chunk2 中插入 "XX":
           ···AAAAAA|BBBBBBBB|CCCCXXCC|CCDDDDDD|DDEEEEEE···
           chunk0   chunk1    chunk2'   chunk3'   chunk4'
           (相同)   (相同)    (变化)    (变化)    (变化)
```
插入操作导致编辑之后的所有边界偏移，所有后续块全部失效。

### 4.2 算法参数

| 参数 | 值 | 说明 |
|------|-----|------|
| 目标块大小 | 64KB | 平衡去重粒度和元数据开销 |
| 最小块大小 | 8KB | 防止块过小导致元数据爆炸 |
| 最大块大小 | 128KB | 防止块过大降低去重效率 |
| 哈希函数 | Blake3 | 用于 MerkleHash 计算 |
| 切割条件 | `hash(window) & mask == 0` | 概率均匀，保证边界分布 |

### 4.3 Python 核心实现

```python
import hashlib

def gear_hash_chunk(data: bytes, 
                    min_size: int = 8 * 1024,
                    max_size: int = 128 * 1024,
                    target_size: int = 64 * 1024,
                    mask: int = 0x0000FFFF) -> list:
    """
    基于 GearHash 的 CDC 分块
    使用滚动哈希 + 边界条件确定切割点
    """
    chunks = []
    start = 0
    i = 0
    h = 0
    mod = 1 << 64
    base = 257
    
    while i < len(data):
        # 滚动哈希更新
        h = (h * base + data[i]) % mod
        i += 1
        
        # 达到最小块大小后才允许切割
        if i - start >= min_size:
            # 哈希匹配条件 或 达到最大块大小
            if (h & mask) == 0 or (i - start) >= max_size:
                chunk = data[start:i]
                chunk_hash = hashlib.blake3(chunk).hexdigest()
                chunks.append({
                    'offset': start,
                    'size': len(chunk),
                    'hash': chunk_hash,
                    'data': chunk
                })
                start = i
                # 重置滚动哈希
                h = 0
    
    # 处理末尾剩余数据
    if start < len(data):
        chunk = data[start:]
        chunks.append({
            'offset': start,
            'size': len(chunk),
            'hash': hashlib.blake3(chunk).hexdigest(),
            'data': chunk
        })
    
    return chunks
```


## 五、版本管理机制

### 5.1 版本标识体系

| 层级 | 标识方式 | 作用 | 可变性 |
|------|----------|------|--------|
| **Commit** | SHA-1 哈希 | 唯一标识一次提交的快照 | 不可变 |
| **Tag** | 语义化名称 (v1.0, v2.1) | 用户可读的版本号 | 不可变（指向固定 Commit） |
| **Branch** | 分支名 (main, dev) | 开发线标识 | 可变（指向最新 Commit） |

**映射关系**：
```
用户输入: git checkout v1.0
    ↓
Git 解析: v1.0 → commit_hash = a3f2c7d9...
    ↓
映射服务: commit_hash → file_manifest_id → chunk_list
    ↓
CAS 存储: 根据 chunk_list 从对象存储拉取数据块
    ↓
客户端: 按 offset 顺序拼接 → 完整文件
```

### 5.2 版本存储示意

```
时间线 (Commits):
────────────────────────────────────────────────────────────►
commit:  a3f2c7d    ←──    b8e9f1a    ←──    c4d5e2b
         │                    │                    │
         ▼                    ▼                    ▼
文件清单: [块A,块B,块C]   [块A,块D,块C]   [块A,块E,块C]
         │                    │                    │
         └────────────────────┼────────────────────┘
                              ▼
                    对象存储实际占用:
                    块A (复用) + 块B + 块C + 块D + 块E
                    (5个块 vs 3个完整文件副本)
```

**存储节省原理**：3 个版本共享块 A 和块 C，仅存储各自独有的块。


## 六、核心流程

### 6.1 上传流程（git push）

```
┌──────────┐     ┌──────────┐     ┌──────────┐     ┌──────────┐
│  用户    │     │  客户端  │     │  映射服务 │     │  CAS存储 │
└────┬─────┘     └────┬─────┘     └────┬─────┘     └────┬─────┘
     │                │                │                │
     │ git push       │                │                │
     │───────────────>│                │                │
     │                │                │                │
     │                │ 1. 读取文件    │                │
     │                │ 2. CDC分块     │                │
     │                │ 3. 计算哈希    │                │
     │                │                │                │
     │                │ 4. 发送哈希列表 │                │
     │                │───────────────>│                │
     │                │                │                │
     │                │ 5. 返回已存在块 │                │
     │                │<───────────────│                │
     │                │                │                │
     │                │ 6. 仅上传新块   │                │
     │                │───────────────────────────────>│
     │                │                │                │
     │                │ 7. 注册映射    │                │
     │                │───────────────>│                │
     │                │                │                │
     │                │ 8. 上传指针文件 │                │
     │                │ (Git原生)      │                │
     │                │───────────────>│                │
     │                │                │                │
     │ 上传完成       │                │                │
     │<───────────────│                │                │
     │                │                │                │
```

**关键优化**：典型的 5GB 数据库文件追加 1MB 数据，LFS 需重新上传 5GB（约 13 分钟），Xet 仅需上传变化块（约 0.1 秒）。

### 6.2 下载流程（git clone / git checkout）

```
┌──────────┐     ┌──────────┐     ┌──────────┐     ┌──────────┐
│  用户    │     │  客户端  │     │  映射服务 │     │  CAS存储 │
└────┬─────┘     └────┬─────┘     └────┬─────┘     └────┬─────┘
     │                │                │                │
     │ git clone      │                │                │
     │───────────────>│                │                │
     │                │                │                │
     │                │ 1. 拉取元数据   │                │
     │                │ (Git原生)      │                │
     │                │───────────────>│                │
     │                │                │                │
     │                │ 2. 获取指针文件 │                │
     │                │<───────────────│                │
     │                │                │                │
     │                │ 3. 请求文件清单 │                │
     │                │───────────────>│                │
     │                │                │                │
     │                │ 4. 返回块列表   │                │
     │                │<───────────────│                │
     │                │                │                │
     │                │ 5. 并行下载块   │                │
     │                │───────────────────────────────>│
     │                │                │                │
     │                │ 6. 校验哈希    │                │
     │                │ 7. 按offset拼接│                │
     │                │                │                │
     │ 文件就绪       │                │                │
     │<───────────────│                │                │
     │                │                │                │
```

**性能数据**（Hugging Face 实测）：

| 指标 | Git LFS | Xet (CDC) |
|------|---------|-----------|
| 平均下载时间 | 51 分钟 | 19 分钟 |
| 平均上传时间 | 47 分钟 | 24 分钟 |
| 存储占用 | 8.9 GB | 3.52 GB |


## 七、性能优化策略

### 7.1 本地缓存

客户端维护本地块缓存，已下载的块在本地保留，后续版本切换时无需重新下载。

### 7.2 并行传输

- 多线程并发下载/上传 Xorb
- 每个 Xorb 最大 64MB，包含最多 8,192 个块

### 7.3 压缩优化

Xorb 对象在存储前进行压缩，减少存储空间和传输带宽。

### 7.4 增量更新

- **上传**：仅传输新增块，已存在块直接复用
- **下载**：仅下载本地缺失的块

### 7.5 全局去重

跨文件、跨仓库的相同内容块自动去重。


## 八、安全与权限

### 8.1 数据完整性

- 每个块使用 Blake3 MerkleHash 标识
- 下载时校验哈希，确保数据完整性
- 文件重建时验证所有块的哈希匹配

### 8.2 访问控制

- 块级权限控制：用户只能读取其有权访问的文件所需的块
- 仓库级别的读写权限继承自 Hugging Face Hub
- Xet 令牌认证机制

### 8.3 隐私保护

- 块内容通过加密哈希保护
- 哈希不可逆，无法从哈希还原原始内容


## 九、部署架构

```
                    ┌──────────────────┐
                    │   Load Balancer  │
                    │   (Nginx/ALB)    │
                    └────────┬─────────┘
                             │
              ┌──────────────┼──────────────┐
              │              │              │
         ┌────▼────┐   ┌────▼────┐   ┌────▼────┐
         │ API节点1│   │ API节点2│   │ API节点3│  ← 无状态，水平扩展
         │ (服务层)│   │ (服务层)│   │ (服务层)│
         └────┬────┘   └────┬────┘   └────┬────┘
              │              │              │
              └──────────────┼──────────────┘
                             │
              ┌──────────────┼──────────────┐
              │              │              │
         ┌────▼────┐   ┌────▼────┐   ┌────▼────┐
         │  Git    │   │Postgre- │   │  Redis  │
         │ Server  │   │  SQL    │   │ (缓存)  │
         │(元数据) │   │(映射)   │   │         │
         └─────────┘   └─────────┘   └─────────┘
                             │
                        ┌────▼────┐
                        │  MinIO/ │
                        │  S3集群 │
                        │(对象存储)│
                        └─────────┘
```

**扩展性说明**：
- **API 节点**：无状态，可根据流量水平扩展
- **PostgreSQL**：读写分离 + 分库分表
- **Redis**：缓存热点块哈希和映射关系
- **MinIO/S3**：分布式对象存储，支持 PB 级扩展


## 十、与 Git LFS 的对比总结

| 维度 | Git LFS | Xet (CDC) |
|------|---------|-----------|
| **去重粒度** | 文件级 | 块级 (8-128KB) |
| **修改 5GB 文件中的 1MB** | 重新上传 5GB | 仅上传变化的块 |
| **存储效率** | 低（每个版本存完整文件） | 高（块级共享） |
| **上传速度** | 慢（全量传输） | 快（增量传输） |
| **跨文件去重** | 不支持 | 支持 |
| **Git 原生支持** | 是（官方扩展） | 需安装 Xet 客户端 |
| **向下兼容** | 完整 | LFS 桥接层 |


## 十一、实施路线图

### Phase 1: 基础架构搭建
- [ ] 部署 CAS 存储集群（MinIO/S3）
- [ ] 部署映射服务（PostgreSQL + Redis）
- [ ] 搭建 Git 元数据仓库服务

### Phase 2: 客户端开发
- [ ] 实现 CDC 分块引擎（GearHash）
- [ ] 实现 Git pre-push 钩子拦截
- [ ] 实现 Xorb 聚合与上传调度
- [ ] 实现下载与文件重建

### Phase 3: 服务端开发
- [ ] 实现全局块去重查询 API
- [ ] 实现映射注册 API
- [ ] 实现 LFS 桥接层（向下兼容）
- [ ] 实现认证与权限控制

### Phase 4: 测试与优化
- [ ] 单元测试 + 集成测试
- [ ] 大规模性能测试（PB 级数据）
- [ ] CDC 参数调优（块大小、边界条件）
- [ ] 监控与告警体系搭建

### Phase 5: 生产部署
- [ ] 灰度发布
- [ ] 全量迁移
- [ ] 文档与开发者工具完善