⚠️ 此页面为自动翻译,翻译可能不完美。
blog-post

Manticore 中的 UUID:主数据库与 Manticore 统一 ID

假设你在主数据库中的商品已经有了 ID 550e8400-e29b-41d4-a716-446655440000。它会出现在事件、日志和 API 响应里。但当同一个商品写入 Manticore 时,应用还得再给它分配一个新的数字 ID。

在 Manticore Search 28.5.0 之前,文档 ID 是一个无符号 64 位整数。UUID 可以保存在单独的字符串属性里,但这并不会让它变成文档标识。对于 UPDATEREPLACEDELETE,仍然需要数字 id

结果就是,必须在主数据库里的 UUID 和 Manticore 表中的数字 ID 之间维护对应关系。现在可以不再需要这层映射:Manticore 的 RT 表可以直接使用 UUID 作为文档 ID。

为什么第二个 ID 会添麻烦

ID 对应表当然不算特别复杂,但也只是在应用加载数据和执行搜索时如此。真正的麻烦会在文档变更时出现。

worker 收到带 UUID 的事件,先找到对应的数字 ID,然后才把更新发送到 Manticore。删除也按同样流程进行。如果对应关系记录丢失或过期,就可能更新错文档,或者变更根本不会进入 Manticore。

另一种方案是把 UUID 变成 64 位哈希。那样应用就必须自己考虑碰撞的可能性。也可以单独维护一个顺序计数器,但这又需要所有创建文档的进程之间协调一致。

数字 ID 还有一个细节问题,已经到了 API 层面。Manticore 内部把它当作 uint64,而 SQL 显示时把它当作有符号 BIGINT。因此,SQL 可能会把大于 2^63-1 的值以负数形式返回,客户端就得小心转换。UUID 以字符串传入和返回,所以不用担心有符号范围和溢出问题。

id uuid 从根上解决了这个问题:对象标识不再需要转换。同一个 UUID 会在主数据库、队列、Manticore、日志和外部 API 中统一使用。

什么是 UUID

UUID 本质上是一个 128 位标识符,生成它不需要中央注册中心。文本形式通常由 36 个字符组成:32 个十六进制数字和 4 个连字符。

550e8400-e29b-41d4-a716-446655440000

UUID 内置了版本和 variant。版本决定其余比特的组织方式。UUIDv4 基于随机或伪随机数据构造。UUIDv7 包含时间信息,并保持标识符的时间顺序。UUIDv8 中其余比特的布局由具体实现决定。

没有中央注册,就可以在写入共享数据库之前分配 ID。比如,两个互不依赖的服务可以并行创建对象,而某个连不上 LTE 的移动客户端也能在离线状态下准备数据。同步后,对象仍会保留同一个标识符。

不过,UUID 不能被视为绝对唯一的保证。实际唯一性取决于所选生成器是否正确。还要注意,UUID 不是密钥,不能替代密码、token 或权限校验。

Manticore 28.5.0 新增了什么

现在可以为 RT 表显式指定文档 ID 类型:

CREATE TABLE products_uuid (
    id uuid,
    title text,
    sku string,
    price int
);

INSERT INTO products_uuid (id, title, sku, price)
VALUES (
    '550e8400-e29b-41d4-a716-446655440000',
    'Mechanical keyboard',
    'KB-001',
    149
);

插入后,这个 UUID 也可以用于等值和 IN 过滤,以及 UPDATEREPLACEDELETE。SQL 会把 ID 作为字符串返回。通过 JSON API 也可以传入显式 UUID 或让 Manticore 自动生成 ID;更详细的请求和响应我们会在下一篇文章里讲。

如果再次 INSERT 一个已经存在的 UUID,Manticore 会像以前对数字 id 一样拒绝它。要按 ID 覆盖文档,请使用 REPLACE

需要注意的是,uuid 类型只适用于文档 ID。普通用户属性不能声明为 uuid 类型。

谁来生成 UUID

有两种方式:

文档写入方式谁生成 IDManticore 的行为
已传入 id 字段主数据库、客户端库或系统中的其他组件检查格式、版本和 variant,然后将 UUID 以小写保存
未提供 id 字段Manticore生成 UUIDv8,并在其中编码内部数字 auto-ID

如果 UUID 已经由应用生成,就不需要为 Manticore 特别调整。格式保持常规的五段式 8-4-4-4-12。版本可以是 v1 到 v8 中的任意一种,而 variant 位置必须是 89ab。大小写无关紧要:UUID 可以大写传入,但 Manticore 会以小写保存。

到这里校验就结束了:Manticore 会检查 UUID 格式,但不会检查生成质量。UUIDv4 的随机性和 UUIDv7 的时间部分是否正确,由客户端库负责。

服务端生成则不同。如果没有传入 id 字段,Manticore 会生成自有结构的 UUIDv8,并在其中编码内部数字 auto-ID。这既不是随机 UUIDv4,也不是秘密值。

应用生成的 UUIDv8,Manticore 会按原样校验并保存;服务端不会按自己的结构重新构造它。

技术细节。对外暴露的仍然是完整 UUID:Manticore 不会把它替换成 64 位哈希,也不要求应用保存对应表。UUID 在引擎内部的具体结构只是实现细节,不影响对外契约。

适用场景

UUID 可以作为普通 RT 表、带 engine='columnar' 的 RT 表,以及复制集群中的表的文档 ID。使用这类 ID 时,可以进行精确搜索、IN 过滤,以及常规文档操作:INSERTREPLACEUPDATEDELETE

需要注意这些限制:

  • uuid 类型不适用于普通属性:Manticore 会拒绝类似 guid uuid 的声明;
  • 在 plain、percolate/PQ 和 shard 表中,UUID 不能作为文档 ID 使用;
  • 现有表不能通过 ALTER TABLE 从数字 ID 切换到 UUID,或反过来;
  • 不支持范围条件 <<=>>=,以及这类 ID 的算术运算;
  • 文档 ID 不能通过 UPDATE 修改,这对 UUID 和数字 ID 都一样;
  • 要自动生成,必须省略 id 字段:这里的值 0 不会被当作特殊标记。

迁移旧代码时很容易忘记这一点。对于数字 RT 表,0 可能表示“自动创建 ID”。在 UUID 表里,id 字段只需不传即可。

UUIDv7 也不会让 id 适合按时间过滤。它可以作为外部标识符,但 Manticore 目前还不支持 id > ... 这类查询。

总结

如果 UUID 已经存在于主数据库中,现在可以随文档一起传给 Manticore。如果文档先出现在 Manticore 中,就不要指定 id,然后从响应里取回生成的 UUID。

之后在 UPDATEREPLACEDELETE 中继续使用同一个 UUID 就行了。无需再给文档分配单独的数字 ID,也不必维护两个标识符之间的对应关系。

完整契约和当前限制都收录在文档中:UUID document IDs 。UUID 作为文档 ID 的支持已在 Manticore Search 28.5.0 中加入。

下一篇文章,也就是 UUID 作为文档 ID 的实战指南 ,会一步一步演示如何通过 SQL 和 JSON 指定并生成 ID、搜索、更新、替换和删除文档,以及如何处理错误。

安装Manticore Search

在 Linux 或 macOS 上用一条命令安装 Manticore Search:

curl https://manticoresearch.com | sh

有关高级安装选项,请参阅完整安装指南手册

安装Manticore Search