> ## 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.

> ClickHouse 中 QBit 数据类型的文档，该类型支持针对近似向量搜索进行细粒度量化

# QBit 数据类型

`QBit` 数据类型会重新组织向量的存储方式，以加快近似向量搜索。它不是将每个向量的元素存储在一起，而是将所有向量中相同二进制位位置的值分组存储。
这样既能以全精度存储向量，又能让你在搜索时选择细粒度的量化级别：读取更少的位可减少 I/O 并加快计算，读取更多的位则可获得更高精度。你既能享受量化在减少数据传输和计算量方面带来的速度优势，又能在需要时使用全部原始数据。

要声明一个 `QBit` 类型的列，请使用以下语法：

```sql theme={null}
column_name QBit(element_type, dimension[, stride])
```

* `element_type` – 每个向量元素的数据类型。允许的类型包括 `Int8`、`BFloat16`、`Float32` 和 `Float64`
* `dimension` – 每个向量中的元素个数
* `stride` – 可选。一起存储在同一组流中的维度数量。省略时，默认值为 `dimension` (单组) 。提供该参数时，`dimension` 必须是 `stride` 的倍数；并且当 `stride` 小于 `dimension` 时，`stride` 必须是 8 的倍数。`dimension` 个维度会被拆分为 `dimension / stride` 个连续分组，每个分组的位平面存储在单独的流中。这样一来，对前 `D` 个维度进行搜索时 (其中 `D` 是 `stride` 的倍数) ，只需读取覆盖这些维度的各分组对应的流，这对 Matryoshka 嵌入向量很有用。

<div id="creating-qbit">
  ## 创建 QBit
</div>

在表的列定义中使用 `QBit` 类型：

```sql theme={null}
CREATE TABLE test (id UInt32, vec QBit(Float32, 8)) ENGINE = Memory;
INSERT INTO test VALUES (1, [1, 2, 3, 4, 5, 6, 7, 8]), (2, [9, 10, 11, 12, 13, 14, 15, 16]);
SELECT vec FROM test ORDER BY id;
```

```text theme={null}
┌─vec──────────────────────┐
│ [1,2,3,4,5,6,7,8]        │
│ [9,10,11,12,13,14,15,16] │
└──────────────────────────┘
```

<div id="converting-arrays-to-qbit">
  ## 将数组转换为 QBit
</div>

当数组长度与 `QBit` 的维度一致时，数组会转换为 `QBit`。数组的元素类型不必与 `QBit` 的元素类型一致。任何数值类型的元素都会自动转换为 `QBit` 的元素类型。因此，你可以将现有的嵌入向量列直接迁移到 `QBit` 列中：

```sql theme={null}
CREATE TABLE embeddings (id UInt32, embedding Array(Float32)) ENGINE = Memory;
INSERT INTO embeddings VALUES (1, [0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8]), (2, [0.8, 0.7, 0.6, 0.5, 0.4, 0.3, 0.2, 0.1]);

CREATE TABLE vectors (id UInt32, vec QBit(Float32, 8)) ENGINE = Memory;
INSERT INTO vectors SELECT id, embedding FROM embeddings;

SELECT * FROM vectors ORDER BY id;
```

```text theme={null}
┌─id─┬─vec───────────────────────────────┐
│  1 │ [0.1,0.2,0.3,0.4,0.5,0.6,0.7,0.8] │
│  2 │ [0.8,0.7,0.6,0.5,0.4,0.3,0.2,0.1] │
└────┴───────────────────────────────────┘
```

这种转换也可以显式地通过 `CAST` 实现，例如 `CAST(embedding AS QBit(Float32, 8))`。

<div id="converting-qbit-to-arrays">
  ## 将 QBit 转换为数组
</div>

反向转换会根据按比特转置后的表示重建原始向量，因此将 `QBit` 转换为 `Array` 时会返回存储的值。这一过程与[将数组转换为 `QBit`](#converting-arrays-to-qbit)相反：

```sql theme={null}
SELECT [1, 2, 3, 4]::QBit(Float32, 4)::Array(Float32) AS vec;
```

```text theme={null}
┌─vec───────┐
│ [1,2,3,4] │
└───────────┘
```

重建后的数组会使用 `QBit` 的 元素类型，然后再将其中的元素转换为所请求的数组 元素类型。因此，连同 元素类型 一并更改的转换同样可行，例如从 `QBit(Float32, N)` 转换为 `Array(Float64)`。

对于 `Int8`、`Float32` 和 `Float64`，`Array` -> `QBit` -> `Array` 的往返转换是无损的。对于 `BFloat16`，其结果与直接转换为 `BFloat16` 一致——唯一损失的精度仅来自 `BFloat16` 本身。

当 `dimension` 不是 8 的倍数时，内部表示中的尾部 padding 元素会被丢弃，因此结果始终恰好包含 `dimension` 个元素。

<div id="converting-between-qbit-types">
  ## QBit 类型之间的转换
</div>

只要 `dimension` (向量元素的数量) 保持不变，`QBit` 就可以转换为另一种 `QBit`。`element_type` 和 `stride` 都可以改变；如果转换为 `dimension` 不同的 `QBit`，则会引发异常，因为这会改变向量本身。

更改 `element_type` 会重建向量，并将每个元素转换为新类型，这与对应的 `Array` 转换完全一致：扩宽 (例如从 `QBit(Float32, N)` 转换为 `QBit(Float64, N)`) 是精确的，而缩窄则会像缩窄 `Array` 转换一样损失精度。

```sql theme={null}
SELECT [1, 2, 3, 4]::QBit(Float32, 4)::QBit(Float64, 4) AS vec;
```

```text theme={null}
┌─vec───────┐
│ [1,2,3,4] │
└───────────┘
```

仅更改 [`stride`](#strides) (保持 `element_type` 不变) 只会在不改动数值的情况下重新组织已存储的比特平面，因此这一过程始终是无损的：

```sql theme={null}
SELECT range(16)::Array(Float32)::QBit(Float32, 16)::QBit(Float32, 16, 8)::Array(Float32)
     = range(16)::Array(Float32) AS is_lossless;
```

```text theme={null}
┌─is_lossless─┐
│           1 │
└─────────────┘
```

<div id="qbit-subcolumns">
  ## QBit 子列
</div>

`QBit` 实现了一种子列访问模式，使您可以访问已存储向量中的各个位平面。每个比特位都可以通过 `.N` 语法访问，其中 `N` 表示该比特位的位置：

```sql theme={null}
CREATE TABLE test (id UInt32, vec QBit(Float32, 8)) ENGINE = Memory;
INSERT INTO test VALUES (1, [0, 0, 0, 0, 0, 0, 0, 0]);
INSERT INTO test VALUES (1, [-0, -0, -0, -0, -0, -0, -0, -0]);
SELECT bin(vec.1) FROM test;
```

```text theme={null}
┌─bin(tupleElement(vec, 1))─┐
│ 00000000                  │
│ 11111111                  │
└───────────────────────────┘
```

可访问的子列数量取决于元素类型 (如果使用 stride，还取决于 stride 组的数量)：

* `Int8`：每个 stride 组 8 个子列 (1-8)
* `BFloat16`：每个 stride 组 16 个子列 (1-16)
* `Float32`：每个 stride 组 32 个子列 (1-32)
* `Float64`：每个 stride 组 64 个子列 (1-64)

这些子列遵循按组优先的顺序：一般来说，`vec.N` 读取 stride 组 `(N-1) / element_size` 中的比特平面 `(N-1) % element_size`。例如，对于 `QBit(BFloat16, 4096, 1024)`，4096 个维度被拆分为 4 个 1024 维的组，因此共有 64 个子列：`vec.1` … `vec.16` 是第一个 stride 组 (维度 1–1024) 的比特平面，`vec.17` … `vec.32` 属于第二个组 (维度 1025–2048) ，依此类推。

<div id="strides">
  ## Stride
</div>

默认情况下，`QBit` 会将每个位平面存储为一个跨越全部 `dimension` 维度的单个流，因此搜索时总是需要读取整个向量的完整位平面。可选的 `stride` 参数会将 `dimension` 维度划分为 `dimension / stride` 个连续分组，并将每个分组的位平面分别存储到独立的流中。这样，如果搜索只涉及前 `D` 个维度 (其中 `D` 是 `stride` 的倍数) ，就只需读取覆盖这些维度的那些分组对应的流。这对于 [Matryoshka 嵌入向量](https://arxiv.org/abs/2205.13147) 很有用，因为靠前的维度可以形成一个可用的低维嵌入向量。

```sql theme={null}
CREATE TABLE test (id UInt32, vec QBit(BFloat16, 4096, 1024)) ENGINE = MergeTree ORDER BY id;
```

这里将 4096 个维度拆分为 4 组，每组 1024 个。子列遵循按组优先的顺序：对于 `BFloat16` (16 个比特平面) ，`vec.1` … `vec.16` 是第一个 stride 组 (维度 1–1024) 的 16 个比特平面，`vec.17` … `vec.32` 属于第二个组 (维度 1025–2048) ，依此类推。一般情况下，`vec.N` 读取 stride 组 `(N-1) / element_size` 中的比特平面 `(N-1) % element_size`。

要执行 reduced-dimension 搜索，请将要读取的维度数作为转置距离函数的第四个参数传入 (见下文) 。参考向量必须至少包含这么多个元素 (任何额外的尾部元素都会被忽略) ，并且该值必须是 `stride` 的倍数。

<div id="vector-search-functions">
  ## 向量搜索函数
</div>

以下是用于向量相似搜索、且使用 `QBit` 数据类型的距离函数：

* [`L2DistanceTransposed`](/docs/zh/reference/functions/regular-functions/distance-functions#L2DistanceTransposed)
* [`cosineDistanceTransposed`](/docs/zh/reference/functions/regular-functions/distance-functions#cosineDistanceTransposed)
* [`dotProductTransposed`](/docs/zh/reference/functions/regular-functions/distance-functions#dotProductTransposed)

对于带 stride 的 `QBit`，这些函数接受一个可选的第四个参数 `used_dims`——即要读取的前几个维度——仅会读取覆盖这些维度的 stride 组。参考向量必须至少包含 `used_dims` 个元素 (任何额外的末尾元素都会被忽略，因此完整大小的查询向量可直接复用于降维搜索，而无需先对其进行切片) ，并且 `used_dims` 必须是 `stride` 的倍数。
