假设你在主数据库中的商品已经有了 ID 550e8400-e29b-41d4-a716-446655440000。它会出现在事件、日志和 API 响应里。但当同一个商品写入 Manticore 时,应用还得再给它分配一个新的数字 ID。
在 Manticore Search 28.5.0 之前,文档 ID 是一个无符号 64 位整数。UUID 可以保存在单独的字符串属性里,但这并不会让它变成文档标识。对于 UPDATE、REPLACE 和 DELETE,仍然需要数字 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 过滤,以及 UPDATE、REPLACE 和 DELETE。SQL 会把 ID 作为字符串返回。通过 JSON API 也可以传入显式 UUID 或让 Manticore 自动生成 ID;更详细的请求和响应我们会在下一篇文章里讲。
如果再次 INSERT 一个已经存在的 UUID,Manticore 会像以前对数字 id 一样拒绝它。要按 ID 覆盖文档,请使用 REPLACE。
需要注意的是,uuid 类型只适用于文档 ID。普通用户属性不能声明为 uuid 类型。
谁来生成 UUID
有两种方式:
| 文档写入方式 | 谁生成 ID | Manticore 的行为 |
|---|---|---|
已传入 id 字段 | 主数据库、客户端库或系统中的其他组件 | 检查格式、版本和 variant,然后将 UUID 以小写保存 |
未提供 id 字段 | Manticore | 生成 UUIDv8,并在其中编码内部数字 auto-ID |
如果 UUID 已经由应用生成,就不需要为 Manticore 特别调整。格式保持常规的五段式 8-4-4-4-12。版本可以是 v1 到 v8 中的任意一种,而 variant 位置必须是 8、9、a 或 b。大小写无关紧要: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 过滤,以及常规文档操作:INSERT、REPLACE、UPDATE 和 DELETE。
需要注意这些限制:
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。
之后在 UPDATE、REPLACE 和 DELETE 中继续使用同一个 UUID 就行了。无需再给文档分配单独的数字 ID,也不必维护两个标识符之间的对应关系。
完整契约和当前限制都收录在文档中:UUID document IDs 。UUID 作为文档 ID 的支持已在 Manticore Search 28.5.0 中加入。
下一篇文章,也就是 UUID 作为文档 ID 的实战指南 ,会一步一步演示如何通过 SQL 和 JSON 指定并生成 ID、搜索、更新、替换和删除文档,以及如何处理错误。
