> ## Documentation Index
> Fetch the complete documentation index at: https://clickhouse.com/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# 表 parts

> 什么是 ClickHouse 中的表 parts

export const Image = ({img, alt, size = "lg"}) => {
  const normalizedSize = ["sm", "md", "lg"].includes(size) ? size : "lg";
  return <div className={`ch-image-${normalizedSize}`}>
      <Frame>
        <img src={img} alt={alt} />
      </Frame>
    </div>;
};

<div id="what-are-table-parts-in-clickhouse">
  ## 什么是 ClickHouse 中的表 parts？
</div>

<br />

ClickHouse [MergeTree 引擎家族](/docs/zh/reference/engines/table-engines/mergetree-family/index)中每个表的数据，都会以一组不可变的 `数据分区片段` 形式存储在磁盘上。

为了说明这一点，我们使用[这张](https://sql.clickhouse.com/?query=U0hPVyBDUkVBVEUgVEFCTEUgdWsudWtfcHJpY2VfcGFpZF9zaW1wbGU\&run_query=true\&tab=results)表 (改编自 [UK property prices dataset](/docs/zh/get-started/sample-datasets/uk-price-paid)) ，它记录了英国已售房产的成交日期、城镇、街道和价格：

```sql theme={null}
CREATE TABLE uk.uk_price_paid_simple
(
    date Date,
    town LowCardinality(String),
    street LowCardinality(String),
    price UInt32
)
ENGINE = MergeTree
ORDER BY (town, street);
```

你可以在我们的 ClickHouse SQL Playground 中[查询此表](https://sql.clickhouse.com/?query=U0VMRUNUICogRlJPTSB1ay51a19wcmljZV9wYWlkX3NpbXBsZTs\&run_query=true\&tab=results)。

每当一组行被插入到该表中时，就会创建一个数据分区片段。下图对此进行了示意：

<Image img="https://mintcdn.com/private-7c7dfe99/-DTs8Nf-Dydrn3iN/images/managing-data/core-concepts/part.webp?fit=max&auto=format&n=-DTs8Nf-Dydrn3iN&q=85&s=659675a5d7d016f02ce2e470defbd89b" size="lg" width="3272" height="2256" data-path="images/managing-data/core-concepts/part.webp" />

<br />

当 ClickHouse server 处理上图中示意的包含 4 行的示例插入 (例如通过 [INSERT INTO 语句](/docs/zh/reference/statements/insert-into)) 时，会执行以下几个步骤：

① **排序**：按表的排序键 `(town, street)` 对这些行进行排序，并为排序后的行生成一个[稀疏主索引](/docs/zh/guides/clickhouse/data-modelling/sparse-primary-indexes)。

② **拆分**：将排序后的数据拆分为各列。

③ **压缩**：对每一列进行[压缩](https://clickhouse.com/blog/optimize-clickhouse-codecs-compression-schema)。

④ **写入磁盘**：压缩后的各列会以二进制列文件的形式保存到一个新目录中，该目录对应此次插入生成的数据分区片段。稀疏主索引也会被压缩并存储在同一目录中。

根据表使用的具体引擎，在排序的同时还[可能](/docs/zh/reference/settings/session-settings)会发生其他转换。

数据分区片段是自包含的，包含了解释其内容所需的全部元数据，无需依赖中心目录。除稀疏主索引外，数据分区片段还包含其他元数据，例如二级[数据跳过索引](/docs/zh/concepts/features/performance/skip-indexes/skipping-indexes)、[列统计信息](https://clickhouse.com/blog/clickhouse-release-23-11#column-statistics-for-prewhere)、校验和、最小-最大索引 (如果使用了[分区](/docs/zh/concepts/core-concepts/partitions)) 以及[更多](https://github.com/ClickHouse/ClickHouse/blob/a065b11d591f22b5dd50cb6224fab2ca557b4989/src/Storages/MergeTree/MergeTreeData.h#L104)。

<div id="part-merges">
  ## 分片 合并
</div>

为了控制每个表中的 分片 数量，[后台合并](/docs/zh/concepts/core-concepts/merges)任务会定期将较小的 分片 合并成较大的 分片，直到其达到[可配置](/docs/zh/reference/settings/merge-tree-settings#max_bytes_to_merge_at_max_space_in_pool)的压缩大小 (通常约为 150 GB) 。合并后的 分片 会被标记为非活动，并在经过一个[可配置](/docs/zh/reference/settings/merge-tree-settings#old_parts_lifetime)的时间间隔后删除。随着时间推移，这一过程会形成合并后 分片 的层级结构，这也正是它被称为 MergeTree 表的原因：

<Image img="https://mintcdn.com/private-7c7dfe99/-DTs8Nf-Dydrn3iN/images/managing-data/core-concepts/merges.webp?fit=max&auto=format&n=-DTs8Nf-Dydrn3iN&q=85&s=d5c751734a36bab62fa7cdcf3bd10323" size="lg" width="3332" height="1814" data-path="images/managing-data/core-concepts/merges.webp" />

<br />

为尽量减少初始 分片 的数量以及合并带来的开销，建议数据库客户端[批量](https://clickhouse.com/blog/asynchronous-data-inserts-in-clickhouse#data-needs-to-be-batched-for-optimal-performance)插入 Tuple，例如一次插入 20,000 行，或者使用[asynchronous insert 模式](https://clickhouse.com/blog/asynchronous-data-inserts-in-clickhouse)。在这种模式下，ClickHouse 会将写入同一表的多个传入 INSERT 中的行缓存在一起，只有当缓冲区大小超过可配置阈值或发生 timeout 时，才会创建新的 分片。

<div id="monitoring-table-parts">
  ## 监控表 parts
</div>

您可以使用[虚拟列](/docs/zh/reference/engines/table-engines/index#table_engines-virtual_columns) `_part`，[查询](https://sql.clickhouse.com/?query=U0VMRUNUIF9wYXJ0CkZST00gdWsudWtfcHJpY2VfcGFpZF9zaW1wbGUKR1JPVVAgQlkgX3BhcnQKT1JERVIgQlkgX3BhcnQgQVNDOw\&run_query=true\&tab=results)示例表中当前所有现存活动 parts 的列表：

```sql theme={null}
SELECT _part
FROM uk.uk_price_paid_simple
GROUP BY _part
ORDER BY _part ASC;
```

```response theme={null}
   ┌─_part───────┐
1. │ all_0_5_1   │
2. │ all_12_17_1 │
3. │ all_18_23_1 │
4. │ all_6_11_1  │
   └─────────────┘
```

上述查询会检索磁盘上各个目录的名称，其中每个目录都表示该表的一个活动数据分区片段。对想进一步了解的读者来说，这些目录名称的各个组成部分都有特定含义，相关文档见[此处](https://github.com/ClickHouse/ClickHouse/blob/f90551824bb90ade2d8a1d8edd7b0a3c0a459617/src/Storages/MergeTree/MergeTreeData.h#L130)。

或者，ClickHouse 会在 [system.parts](/docs/zh/reference/system-tables/parts) 系统表中跟踪所有表的所有数据分区片段信息，而下面的查询会为我们上面的示例表[返回](https://sql.clickhouse.com/?query=U0VMRUNUCiAgICBuYW1lLAogICAgbGV2ZWwsCiAgICByb3dzCkZST00gc3lzdGVtLnBhcnRzCldIRVJFIChkYXRhYmFzZSA9ICd1aycpIEFORCAoYHRhYmxlYCA9ICd1a19wcmljZV9wYWlkX3NpbXBsZScpIEFORCBhY3RpdmUKT1JERVIgQlkgbmFtZSBBU0M7\&run_query=true\&tab=results)当前所有活动数据分区片段的列表、它们的合并层级，以及这些数据分区片段中存储的行数：

```sql theme={null}
SELECT
    name,
    level,
    rows
FROM system.parts
WHERE (database = 'uk') AND (`table` = 'uk_price_paid_simple') AND active
ORDER BY name ASC;
```

```response theme={null}
   ┌─name────────┬─level─┬────rows─┐
1. │ all_0_5_1   │     1 │ 6368414 │
2. │ all_12_17_1 │     1 │ 6442494 │
3. │ all_18_23_1 │     1 │ 5977762 │
4. │ all_6_11_1  │     1 │ 6459763 │
   └─────────────┴───────┴─────────┘
```

每在该分片上额外执行一次合并，合并级别就会增加 1。级别为 0 表示这是一个尚未合并的新分片。
