# 存储内存使用量基准测试

> etcd 存储的性能指标（内存索引与页面缓存）

---

LLMS 索引： [llms.txt](/zh/llms.txt)

---

<!---todo: link storage to storage design doc-->
etcd 存储的两个组件会占用物理内存。etcd 进程分配了一个 *内存索引*，用于加速键的查找。进程的 *页缓存* 由操作系统管理，用于存储从磁盘读取的最近访问数据，以便快速重用。

内存索引将所有键以 [B 树][btree] 数据结构的形式存储，并附带指向磁盘数据（即值）的指针。B 树中的每个键可能包含多个指针，指向其值的不同版本。因此，内存索引的理论内存消耗可近似表示为以下公式：

`N * (c1 + avg_key_size) + N * (avg_versions_of_key) * (c2 + size_of_pointer)`

其中 `c1` 为键元数据开销，`c2` 为版本元数据开销。

图表展示了内存中索引 B 树的详细结构。

```


                                In mem index

                               +------------+
                               | key || ... |
  +--------------+             |     ||     |
  |              |             +------------+
  |              |             | v1  || ... |
  |   disk    <----------------|     ||     | Tree Node
  |              |             +------------+
  |              |             | v2  || ... |
  |           <----------------+     ||     |
  |              |             +------------+
  +--------------+       +-----+    |   |   |
                         |     |    |   |   |
                         |     +------------+
                         |
                         |
                         ^
                      ------+
                      | ... |
                      |     |
                      +-----+
                      | ... | Tree Node
                      |     |
                      +-----+
                      | ... |
                      |     |
                      ------+
```

[页面缓存内存][pagecache] 由操作系统管理，本文档不对其进行详细说明。

## 测试环境 {#testing-environment}

etcd 版本
- git 提交哈希 https://github.com/etcd-io/etcd/commit/776e9fb7be7eee5e6b58ab977c8887b4fe4d48db

GCE n1-standard-2 机器类型

- 7.5 GB 内存
- 2 个 CPU

## 内存中索引内存使用量 {#in-memory-index-memory-usage}

本测试仅针对内存索引的内存使用情况进行基准测试。目标是查找上述 `c1` 和 `c2`，并了解存储系统的内存消耗上限。

我们通过 Go 运行时的 ReadMemStats 计算内存使用量。通过对比创建索引前后的已分配字节数差异来估算内存使用情况。该方法无法完全反映内存中索引本身的内存使用量，但可展示大致的消耗趋势。

| N    | 版本数 | 键大小 | 内存占用 |
|------|--------|--------|----------|
| 100K | 1      | 64 字节 | 22 MB    |
| 100K | 5      | 64 字节 | 39 MB    |
| 1M   | 1      | 64 字节 | 218 MB   |
| 1M   | 5      | 64 字节 | 432 MB   |
| 100K | 1      | 256 字节 | 41 MB   |
| 100K | 5      | 256 字节 | 65 MB   |
| 1M   | 1      | 256 字节 | 409 MB  |
| 1M   | 5      | 256 字节 | 506 MB  |


根据结果，我们可以计算 `c1=120bytes`、`c2=30bytes`。仅需两组数据即可计算 `c1` 和 `c2`，因为它们是公式中唯一的未知变量。`c1=120bytes` 和 `c2=30bytes` 是我们计算出的 4 组 `c1` 和 `c2` 的平均值。对于小键值对，键元数据开销仍相对显著（50%）。然而，这相较于旧存储系统已实现显著改进，后者至少存在 1000% 的开销。

## 整体内存使用情况 {#overall-memory-usage}

整体内存使用量反映了 etcd 在存储系统上的 RSS 内存占用情况。值的大小对 etcd 的整体内存使用量影响极小，因为值数据存储在磁盘上，仅将热值保留在内存中，由操作系统的页面缓存进行管理。

| N    | versions | 键大小 | 值大小 | 内存占用 |
|------|----------|--------|--------|----------|
| 100K | 1        | 64 字节 | 256 字节 | 40 MB    |
| 100K | 5        | 64 字节 | 256 字节 | 89 MB    |
| 1M   | 1        | 64 字节 | 256 字节 | 470 MB   |
| 1M   | 5        | 64 字节 | 256 字节 | 880 MB   |
| 100K | 1        | 64 字节 | 1 KB   | 102 MB   |
| 100K | 5        | 64 字节 | 1 KB   | 164 MB   |
| 1M   | 1        | 64 字节 | 1 KB   | 587 MB   |
| 1M   | 5        | 64 字节 | 1 KB   | 836 MB   |

根据结果可知，值的大小对内存消耗的影响并不显著。由于操作系统页缓存中存储了更多数据，存在轻微的内存增长。

[btree]: https://en.wikipedia.org/wiki/B-tree
[pagecache]: https://en.wikipedia.org/wiki/Page_cache
