跳到主要内容

索引时长(写入 → 可检索)

回答客户的高频问题:数据写入后多久可被检索。 验收项中常表述为「索引完成时间」或「索引专项测试」。

前提是区分两个环节:数据入库与数据可被检索并非同一件事。 写入数据库的耗时很短,但全文检索依赖独立的搜索索引, 新数据须先同步至索引才能被检索命中。这段时间差即本页测定的对象。

本页为实测数据而非估算,测试方法可复现(见文末)。

一、结论速查

指标实测值说明
写入 10,000 条工作项0.31 秒直连库批量落库本身
首批可检索T0 + 7.0 秒前 2,000 条
全部 10,000 条可检索T0 + 36.6 秒三次测量落在 34.6 ~ 37.4 秒
索引完整性100%(10000/10000)全文检索命中数 == 写入条数
索引吞吐约 280 条/秒单批 2,000 条耗时 6.85~7.41 秒
人工重建索引0 次全自动,见下文「三」

T0 = 数据写入完成时刻(最后一条落库),客户验收项的计时起点通常就是这个。

批量导入耗时 ≠ 索引完成时间,两者相差约 118 倍

写入 10,000 条仅需 0.31 秒,索引追平需 36.6 秒。 以导入耗时回答「写入后多久可检索」将少报两个数量级。两项指标必须分别测定与陈述。

第三个易混口径:索引完成时间不等于检索响应时间(输入关键词至返回结果的耗时)。

客户的问法实际所指
搜索会不会很慢检索响应时间
写入后多久能查到索引完成时间

应先确认客户询问的是哪一项。

二、索引为渐进可见

索引按批推进,每批 2,000 条约 7.4 秒,累计可检索条数呈阶梯式上升:

时刻累计可检索说明
T00数据写入完成(最后一条落库)
T0 + 7.0s2,000首批可检索
T0 + 14.8s4,000
T0 + 22.2s6,000
T0 + 29.6s8,000
T0 + 36.6s10,000全部可检索,完整性 100%

已完成索引的数据立即可检索,无需等待整批完成。

因此对客户应给出两个时间点(首批可检索、全部可检索)。 仅给出 37 秒会使客户误认为该时段内完全无法检索。

三、索引的自动同步机制

数据库每次写入都会在其日志(binlog)中留下记录。 系统中的同步服务持续读取该日志,发现新数据后同步至搜索索引。 该机制称为 CDC(变更数据捕获)。

由此得出关键结论:只要数据写入了数据库,即会被同步至索引,无需人工干预。 造数工具直连数据库写入的数据同样适用——实测两批各 10,000 条, 全程未执行任何重建索引操作,36.6 秒后全文检索 100% 命中。

一处需要纠正的旧说法

早期说法「直接向数据库插入工作项会绕过搜索索引,必须手动重建索引」, 对全文检索不成立

当时确实观察到索引中无数据,但根因大概率是同步服务或索引层处于异常状态 (反复重启或内存溢出)——同步服务停止后自然无法追赶日志, 并非数据绕过了索引通道。

先查同步服务健康,再谈重建索引

# 有没有 CrashLoop / RESTARTS 猛涨
kubectl -n ones get po | grep -E 'kilob-sync|tikv'

# 同步日志,event count 应与写入条数对得上
kubectl -n ones logs <kilob-sync-pod> --since=10m | grep -E "last table|event count"

正常同步时日志形如:

[INFO] event Count [2000],sync Total time elapsed[7.41s], Average [269.9/s]
[INFO] last table: item_issue, event count: 2000
检索无结果不等同于需要重建索引

应优先按索引层异常排查。同步服务反复重启多为索引层(TiKV)内存不足触发 OOM。 同步服务未恢复时重建索引无效,须先修复索引层。

未覆盖的部分

聚合与分组视图(项目分组、任务计数、状态分类筛选)本次未验证, 其是否同样随 CDC 自动生效尚不确定。本页结论仅覆盖全文检索。

四、复现方法

1. 准备:仅生成工作项本体

关键在于关闭全部衍生数据,使被索引对象保持单一, 并将写入耗时压至可忽略,以确保 T0 时刻的准确性。

reuse_projects: ["<空项目 uuid>"]   # 复用一个空项目,整条 API 路径都不走
project_items: [10000]
title_prefix: "IDXT20260825" # ★ 唯一标记:检索时精确圈出本批数据,便于算召回

custom_fields: {single_select: 0, multi_select: 0, number: 0, text: 0, multi_line_text: 0}
field_fill_ratio: 0
comments_per_item: {avg: 0, max: 0}
manhours_per_item: {avg: 0, max: 0}
links_per_item: {avg: 0, max: 0}
watchers_per_item: {avg: 0, max: 0}
sprints_per_project: {avg: 0, max: 0}
members_per_project: {avg: 0, max: 0}
batch: 2000

目标团队应使用空团队:索引中仅含本批数据,T1-T0 不会混入存量,测量结果才具可比性。 空团队中至少需有 1 个已授权用户(负责人与经办人从真实授权用户中选取,无用户将导致造数失败)。

2. 造数,取 T0

不得使用带加速开关的常规造数脚本

常规脚本包含临时关闭 sync_binlog 的提速逻辑(约 2 倍)。 关闭 binlog 将直接干扰基于 binlog 的索引同步,污染测量结果。 须直接调用底层命令。

T0 取自日志中 [gen] project 1/1 done 一行的毫秒时间戳, 不应使用命令返回时刻——kubectl exec 自身有数秒开销,会使 T0 后移。

3. 轮询检索,取 T1

检索接口是 ONESQL:

POST /project/api/ones-project/team/<TEAM>/workitems/onesql
Authorization: Bearer <token>
Content-Type: application/json

{"query":"select count(uuid) from issue where search('<你的 title_prefix>');"}

返回 {"data":[{"type":"aggregate","aggregate":{"count(uuid)":10000}}]}。 轮询这个 count 直到等于写入条数,记下时刻即 T1。

必须使用 search()

search() 为全文检索函数。使用 where uid(field006)=uid('<项目>') 这类字段过滤 可能走 SQL 路径,测不到索引

4. 交叉验证

单侧测量易出错。使用同步服务日志独立测算一次,两侧结果应吻合在 1 秒内(日志为秒级精度):

kubectl -n ones logs <kilob-sync-pod> --since=15m | grep "last table: item_issue"

本次实测三条证据链:

测量来源批次T0T1索引耗时
同步服务日志第 1 批15:57:45.37115:58:2034.6s
同步服务日志第 2 批16:07:45.64216:08:2337.4s
检索接口轮询第 2 批16:07:45.64216:08:22.24236.6s

五、对客户的表述

在 10,000 条业务数据的批量写入场景下,数据写入完成后约 7 秒开始可被检索, 37 秒内全部 10,000 条进入检索索引且均可正常检索,索引完整性 100%,无数据遗漏。

索引由系统自动完成,无需人工干预或重建索引操作。索引过程为渐进式, 已完成索引的数据可立即被检索,不需要等待整批完成。

两个要点:给出两个时间点而非单一数值;明确无需人工干预, 以回应客户对运维负担的隐含关切。

先测定基线,再商定阈值

客户验收项常写为「索引阈值为 ___」。应先按本页方法测出基线, 再依据实测数据与客户商定,避免填入无法达成的数值。

须与客户对齐写入方式

本页测定的是批量写入后的索引追平耗时。 若客户所指为在界面上逐条创建的日常场景,延迟会显著更短(单条无需排队)。 两种口径的数值差异较大,应先确认数据的写入方式。

六、外推的适用边界

按 280 条/秒线性外推:10 万条约 6 分钟,100 万条约 1 小时。

该外推值不得直接引用给客户

大数据量下率先成为瓶颈的是索引层(TiKV)内存而非吞吐能力—— 容器内存上限不足时触发 OOM,同步服务随之反复重启、同步中断。

百万级量级必须在达标环境中另行实测。

附:本次实测环境

  • dev 单节点 k3s(amd64),目标为空团队 + 空项目,索引中无其他存量;
  • 两批各 10,000 条工作项,仅工作项本体,无字段值 / 评论 / 工时 / 关联 / 关注人 / 迭代;
  • 检索查询响应 1.25 ~ 1.32 秒(含公网往返,非纯服务端耗时,仅供参考)。