文章目录
- 每日一句正能量
- 1. 背景与问题
- 2. 环境与数据
- 2.1 文档表
- 2.2 知识块和向量表
- 2.3 时序表
- 2.4 规则和问题表
- 3. 复现过程
- 3.1 文档已发布但没有有效知识块
- 3.2 文档和向量内容不一致
- 3.3 向量模型和维度不一致
- 3.4 孤儿向量
- 3.5 跨租户关联错误
- 3.6 时序数据时间倒退和缺口
- 3.7 JSONB 标签值域异常
- 4. 方案实施
- 4.1 质量规则分层
- 4.2 规则元数据设计
- 4.3 规则执行器
- 4.4 问题去重与状态流转
- 4.5 统一质量分数
- 4.6 跨模联合检测
- 4.7 质量时序指标
- 4.8 真实质量查询
- 5. 结果对比
- 6. 风险与复盘
- 6.1 常见风险
- 6.2 上线检查清单
- 6.3 复盘结论
每日一句正能量
“愿你眼角带笑,岁月不染眉梢。”
愿时光流逝,留下的是笑意,而非沧桑;是经历后的清澈,而非磨损的痕迹。
1. 背景与问题
多模数据平台同时管理关系、文档、时序和向量数据。不同数据类型承担不同职责:
关系数据:客户、租户、设备、文档状态和权限; 文档数据:正文、标签、版本、来源和附件; 时序数据:指标、日志、事件和监控点; 向量数据:文档块、会话摘要和语义检索特征。这几类数据通常不是独立存在的。一篇数据库故障文档可能有多个知识块,每个知识块有一个 Embedding;故障文档还关联某个系统、某个事故编号和一段时间窗口内的监控指标。
因此,数据质量问题也会跨模型出现:
- 文档已经更新,但对应向量仍是旧版本。
- 向量维度与模型配置不一致。
- 时序指标时间戳倒退或出现大段缺口。
- 文档有
tenant_id,向量或事件表没有租户边界。 - JSONB 中同一标签使用多个值,例如
database、db、数据库。 - 文档标记为已发布,但没有任何有效知识块。
- 向量存在,但原始文档已经删除。
- 设备或客户关系主数据已经失效,时序事件仍在持续写入。
- 备份和质量检查只关注表行数,没有检查跨表关联。
如果没有自动化检测,问题通常直到搜索结果错误、问答引用过期文档或监控看板出现空洞时才被发现。
质量治理不能只建立一个“脏数据数量”指标,而应覆盖:
完整性:必填字段是否存在; 唯一性:业务主键是否重复; 一致性:文档、向量、关系和时序是否相互匹配; 及时性:派生数据是否在规定时间内更新; 准确性:值域、单位、时间和状态是否符合约束; 可追溯性:数据是否能沿血缘回溯到来源; 安全性:租户、权限和密级边界是否完整。2. 环境与数据
测试环境:
| 组件 | 用途 |
|---|---|
| PostgreSQL 15 | 关系、文档、规则和问题记录 |
| pgvector | 向量字段和向量检索 |
| TimescaleDB | 时序指标和质量趋势 |
| JSONB | 动态标签、质量上下文和规则参数 |
| 调度 Worker | 定时执行规则 SQL |
| 数据质量看板 | 展示问题数量、等级和趋势 |
2.1 文档表
CREATETABLEknowledge_document(document_id UUIDPRIMARYKEY,tenant_idTEXTNOTNULL,titleTEXTNOTNULL,contentTEXTNOTNULL,content_hashTEXTNOTNULL,version_noINTNOTNULLDEFAULT1,statusTEXTNOTNULLDEFAULT'draft',owner_teamTEXTNOTNULL,metadata JSONBNOTNULLDEFAULT'{}'::jsonb,updated_at TIMESTAMPTZNOTNULLDEFAULTnow());2.2 知识块和向量表
CREATEEXTENSIONIFNOTEXISTSvector;CREATETABLEknowledge_chunk(chunk_id UUIDPRIMARYKEY,document_id UUIDNOTNULL,tenant_idTEXTNOTNULL,version_noINTNOTNULL,chunk_noINTNOTNULL,contentTEXTNOTNULL,content_hashTEXTNOTNULL,embedding_modelTEXTNOTNULL,embedding_dimensionINTNOTNULL,embedding VECTOR(768)NOTNULL,statusTEXTNOTNULLDEFAULT'active',metadata JSONBNOTNULLDEFAULT'{}'::jsonb,created_at TIMESTAMPTZNOTNULLDEFAULTnow(),UNIQUE(document_id,version_no,chunk_no));CREATEINDEXidx_chunk_embedding_hnswONknowledge_chunkUSINGhnsw(embedding vector_cosine_ops);2.3 时序表
CREATETABLEplatform_event(ts TIMESTAMPTZNOTNULL,tenant_idTEXTNOTNULL,entity_idTEXTNOTNULL,event_typeTEXTNOTNULL,valueDOUBLEPRECISION,labels JSONBNOTNULLDEFAULT'{}'::jsonb);SELECTcreate_hypertable('platform_event',by_range('ts'),if_not_exists=>TRUE);2.4 规则和问题表
CREATETABLEquality_rule(rule_idTEXTPRIMARYKEY,rule_nameTEXTNOTNULL,asset_typeTEXTNOTNULL,severityTEXTNOTNULL,rule_sqlTEXTNOTNULL,owner_teamTEXTNOTNULL,enabledBOOLEANNOTNULLDEFAULTTRUE,parameters JSONBNOTNULLDEFAULT'{}'::jsonb,created_at TIMESTAMPTZNOTNULLDEFAULTnow());CREATETABLEquality_issue(issue_id UUIDPRIMARYKEY,rule_idTEXTNOTNULLREFERENCESquality_rule(rule_id),tenant_idTEXT,asset_nameTEXTNOTNULL,record_keyTEXT,issue_typeTEXTNOTNULL,severityTEXTNOTNULL,issue_detail JSONBNOTNULLDEFAULT'{}'::jsonb,detected_at TIMESTAMPTZNOTNULLDEFAULTnow(),statusTEXTNOTNULLDEFAULT'open',resolved_at TIMESTAMPTZ);规则 SQL 不应直接修改业务数据。检测任务只负责发现问题,修复由经过审批的独立任务完成。
3. 复现过程
3.1 文档已发布但没有有效知识块
SELECTd.document_id,d.tenant_id,d.title,d.version_noFROMknowledge_document dLEFTJOINknowledge_chunk cONc.document_id=d.document_idANDc.version_no=d.version_noANDc.status='active'WHEREd.status='published'GROUPBYd.document_id,d.tenant_id,d.title,d.version_noHAVINGcount(c.chunk_id)=0;这个问题会导致文档在管理后台显示“已发布”,但向量检索无法找到它。
3.2 文档和向量内容不一致
SELECTd.document_id,d.title,d.version_noASdocument_version,c.version_noASchunk_version,d.content_hashASdocument_hash,c.content_hashASchunk_hashFROMknowledge_document dJOINknowledge_chunk cONc.document_id=d.document_idWHEREd.status='published'ANDc.status='active'AND(d.version_no<>c.version_noORd.content_hash<>c.content_hash);实际生产中,文档正文哈希和知识块哈希的口径可能不同。更稳妥的方式是给每个分块保存source_content_hash,表示该块对应的原始文档版本哈希,而不是直接将整篇文档哈希与每个块比较。
3.3 向量模型和维度不一致
SELECTembedding_model,embedding_dimension,count(*)ASrow_countFROMknowledge_chunkGROUPBYembedding_model,embedding_dimensionORDERBYembedding_model,embedding_dimension;检测异常配置:
SELECTchunk_id,embedding_model,embedding_dimensionFROMknowledge_chunkWHEREembedding_model<>'embedding-model-v2'ORembedding_dimension<>768;模型版本混用会导致相似度不可直接比较。即使数据库允许写入,也可能造成排序结果不稳定。
3.4 孤儿向量
SELECTc.chunk_id,c.document_id,c.tenant_idFROMknowledge_chunk cLEFTJOINknowledge_document dONd.document_id=c.document_idWHEREd.document_idISNULLORd.statusIN('deleted','archived');孤儿向量应该进入待清理队列,而不是继续参与在线检索。
3.5 跨租户关联错误
SELECTc.chunk_id,c.tenant_idASchunk_tenant,d.tenant_idASdocument_tenant,c.document_idFROMknowledge_chunk cJOINknowledge_document dONd.document_id=c.document_idWHEREc.tenant_id<>d.tenant_id;这是高优先级问题。跨租户数据关联错误不仅影响质量,还可能造成权限和数据隔离风险。
3.6 时序数据时间倒退和缺口
时间倒退:
WITHorderedAS(SELECTtenant_id,entity_id,event_type,ts,lag(ts)OVER(PARTITIONBYtenant_id,entity_id,event_typeORDERBYts)ASprevious_tsFROMplatform_event)SELECT*FROMorderedWHEREprevious_tsISNOTNULLANDts<previous_ts;按分钟检查指标缺口:
WITHexpectedAS(SELECTgenerate_series(date_trunc('minute',now()-interval'1 hour'),date_trunc('minute',now()),interval'1 minute')ASminute),actualAS(SELECTDISTINCTdate_trunc('minute',ts)ASminuteFROMplatform_eventWHEREtenant_id='tenant_a'ANDentity_id='db-primary'ANDevent_type='active_connections'ANDts>=now()-interval'1 hour')SELECTe.minuteFROMexpected eLEFTJOINactual aONa.minute=e.minuteWHEREa.minuteISNULLORDERBYe.minute;3.7 JSONB 标签值域异常
SELECTmetadata->>'domain'ASdomain,count(*)ASrow_countFROMknowledge_documentGROUPBYmetadata->>'domain'ORDERBYrow_countDESC;检查不在标准字典中的值:
SELECTdocument_id,title,metadata->>'domain'ASdomainFROMknowledge_documentWHEREmetadata->>'domain'ISNOTNULLANDmetadata->>'domain'NOTIN('database','network','security','application');4. 方案实施
4.1 质量规则分层
建议按照数据生命周期建立规则层次。
接入质量:
文件是否可读取; 字段是否齐全; 时间格式是否合法; 向量维度是否正确; 必填主键是否为空。存储质量:
主键是否重复; 租户字段是否一致; 文档状态是否合法; JSONB 标签是否符合值域; 时序时间是否倒退。关联质量:
文档是否有知识块; 知识块是否有原文; 向量是否有模型版本; 事件是否能关联业务实体; 文档和向量版本是否匹配。服务质量:
已发布文档是否可检索; 过期向量是否仍被召回; 权限过滤是否生效; 时序查询是否覆盖关键窗口; 空结果率是否异常。4.2 规则元数据设计
插入规则:
INSERTINTOquality_rule(rule_id,rule_name,asset_type,severity,rule_sql,owner_team,parameters)VALUES('DOC-001','已发布文档必须存在有效知识块','document','high',$sql$SELECTd.tenant_id,d.document_id::textASrecord_key,jsonb_build_object('title',d.title,'version_no',d.version_no)ASissue_detailFROMknowledge_document dLEFTJOINknowledge_chunk cONc.document_id=d.document_idANDc.version_no=d.version_noANDc.status='active'WHEREd.status='published'GROUPBYd.tenant_id,d.document_id,d.title,d.version_noHAVINGcount(c.chunk_id)=0$sql$,'knowledge_team','{"max_allowed":0}'),('VEC-001','向量维度必须符合模型配置','vector','critical',$sql$SELECTtenant_id,chunk_id::textASrecord_key,jsonb_build_object('model',embedding_model,'dimension',embedding_dimension)ASissue_detailFROMknowledge_chunkWHEREembedding_dimension<>768ORembedding_model<>'embedding-model-v2'$sql$,'ai_platform','{"expected_dimension":768}');4.3 规则执行器
规则 SQL 建议约定统一返回字段:
tenant_id record_key issue_detail执行器伪代码:
defexecute_rule(conn,rule):withconn.cursor()ascur:cur.execute(rule["rule_sql"])rows=cur.fetchall()fortenant_id,record_key,issue_detailinrows:cur.execute(""" INSERT INTO quality_issue (issue_id, rule_id, tenant_id, asset_name, record_key, issue_type, severity, issue_detail) VALUES ( gen_random_uuid(), %s, %s, %s, %s, 'detected', %s, %s ) """,(rule["rule_id"],tenant_id,rule["asset_type"],record_key,rule["severity"],issue_detail,),)生产中应增加:
规则超时; 单条规则最大返回问题数; 失败重试; 问题去重; 问题状态恢复; 规则版本; 执行耗时; 执行人和任务批次。4.4 问题去重与状态流转
如果同一个问题每天都被检测到,不能每天插入一条完全重复的问题。可以按以下维度生成问题指纹:
rule_id + tenant_id + record_key + issue_type示例:
CREATEUNIQUEINDEXuq_quality_issue_openONquality_issue(rule_id,tenant_id,record_key,issue_type)WHEREstatus='open';问题状态:
open -> acknowledged -> fixing -> resolved -> ignoredignored必须记录原因和有效期,不能成为永久垃圾桶。
4.5 统一质量分数
可以按规则严重程度计算质量分数:
critical:扣 10 分 high:扣 5 分 medium:扣 2 分 low:扣 1 分示例 SQL:
SELECTtenant_id,100-sum(CASEseverityWHEN'critical'THEN10WHEN'high'THEN5WHEN'medium'THEN2WHEN'low'THEN1ELSE0END)ASquality_scoreFROMquality_issueWHEREstatus='open'GROUPBYtenant_id;质量分数只能作为管理视图,不能替代具体问题清单。一个关键向量模型错误,可能比数百条低优先级标签格式问题更严重。
4.6 跨模联合检测
检查“已发布文档、有效向量和近 24 小时事件”的完整关联:
WITHpublished_docAS(SELECTdocument_id,tenant_id,metadata->>'system'ASsystem_nameFROMknowledge_documentWHEREstatus='published'),valid_vectorAS(SELECTDISTINCTdocument_id,tenant_idFROMknowledge_chunkWHEREstatus='active'ANDembedding_dimension=768),recent_eventAS(SELECTDISTINCTtenant_id,labels->>'document_id'ASdocument_idFROMplatform_eventWHEREts>=now()-interval'24 hours')SELECTd.tenant_id,d.document_id::textASrecord_key,jsonb_build_object('system',d.system_name,'has_vector',v.document_idISNOTNULL,'has_recent_event',e.document_idISNOTNULL)ASissue_detailFROMpublished_doc dLEFTJOINvalid_vector vONv.document_id=d.document_idANDv.tenant_id=d.tenant_idLEFTJOINrecent_event eONe.document_id=d.document_id::textANDe.tenant_id=d.tenant_idWHEREv.document_idISNULLORe.document_idISNULL;这里不一定所有文档都必须有近期事件,因此规则应根据资产类型和业务场景配置。质量规则必须有业务语义,不能只追求“所有表都完整”。
4.7 质量时序指标
CREATETABLEquality_metric(ts TIMESTAMPTZNOTNULL,rule_idTEXTNOTNULL,tenant_idTEXTNOTNULL,issue_countBIGINTNOTNULL,execution_msDOUBLEPRECISIONNOTNULL,labels JSONBNOTNULLDEFAULT'{}'::jsonb);SELECTcreate_hypertable('quality_metric',by_range('ts'),if_not_exists=>TRUE);查询规则趋势:
SELECTtime_bucket('1 day',ts)ASday,rule_id,tenant_id,max(issue_count)ASmax_issue_count,avg(execution_ms)ASavg_execution_msFROMquality_metricWHEREts>=now()-interval'30 days'GROUPBYday,rule_id,tenant_idORDERBYdayDESC,rule_id;关注两个方向:
问题数量是否下降; 规则执行耗时是否随数据增长而恶化。如果问题数下降,但规则执行耗时持续上升,说明治理体系本身也需要容量规划和优化。
4.8 真实质量查询
检查文档发布状态和向量查询结果:
SELECTd.document_id,d.title,d.status,count(c.chunk_id)ASactive_chunk_count,max(c.created_at)ASlatest_vector_timeFROMknowledge_document dLEFTJOINknowledge_chunk cONc.document_id=d.document_idANDc.version_no=d.version_noANDc.status='active'GROUPBYd.document_id,d.title,d.statusHAVINGd.status='published'ANDcount(c.chunk_id)=0;检查向量模型版本:
SELECTembedding_model,embedding_dimension,count(*)ASvector_count,min(created_at)ASfirst_created,max(created_at)ASlast_createdFROMknowledge_chunkGROUPBYembedding_model,embedding_dimension;检查时序实体是否存在:
SELECTe.entity_id,count(*)ASevent_countFROMplatform_event eLEFTJOINsystem_registry sONs.entity_id=e.entity_idWHEREs.entity_idISNULLGROUPBYe.entity_id;5. 结果对比
以下为示例测试结果,数据规模为 100 万篇文档、800 万知识块和每日约 2 亿条时序事件。
| 方案 | 质量问题发现延迟 | 跨模关联问题发现率 | 规则平均耗时 | 重复问题率 |
|---|---|---|---|---|
| 人工抽查 | 1 至 7 天 | 35% | 不稳定 | 高 |
| 单表规则 SQL | 1 小时 | 58% | 420 ms | 中 |
| 分层规则 SQL | 15 分钟 | 82% | 680 ms | 低 |
| 分层规则 + 血缘校验 | 10 分钟 | 94% | 760 ms | 低 |
| 分层规则 + 指标闭环 | 10 分钟 | 96% | 790 ms | 低 |
示例问题样例:
DOC-001: 已发布文档没有有效知识块,导致检索不可见。 VEC-001: Embedding 维度不是 768,或者模型版本不是当前服务版本。 VEC-002: 文档 content_hash 与向量 source_content_hash 不一致。 TS-001: 指标在连续时间窗口内缺少数据点。 REL-001: 时序事件 entity_id 不存在于主数据表。 SEC-001: 同一资产的 tenant_id 在关系、文档和向量表中不一致。结果对比时应同时观察:
问题发现速度; 问题准确率; 规则执行耗时; 问题重复率; 自动修复比例; 跨模关联覆盖率; 线上查询受影响程度。质量规则不是越多越好。低价值规则过多会造成告警噪声,使真正严重的问题被淹没。
6. 风险与复盘
6.1 常见风险
| 风险 | 表现 | 应对 |
|---|---|---|
| 规则只检测单表 | 跨模问题发现不了 | 增加文档、时序、向量联合规则 |
| 规则 SQL 无索引 | 检测任务拖慢线上 | 为高频条件建索引或分批执行 |
| 质量阈值不合理 | 告警过多或漏报 | 用真实问题样本校准 |
| 只记录问题数量 | 无法判断严重程度 | 增加等级、资产和责任人 |
| 问题不去重 | 每天生成重复工单 | 使用问题指纹和状态流转 |
| 自动修复过度 | 误删或误改数据 | 高风险修复必须审批 |
| 向量过期未检测 | 搜索返回旧内容 | 比较版本号和内容哈希 |
| 时序缺口被忽略 | 故障分析不完整 | 按关键时间窗口检测连续性 |
| 质量规则自身超时 | 治理任务无法运行 | 设置超时、分区和限流 |
6.2 上线检查清单
[ ] 已覆盖完整性、唯一性、一致性、及时性和准确性 [ ] 已覆盖关系、文档、时序和向量跨模关联 [ ] 已制定统一规则 SQL 返回格式 [ ] 规则已登记负责人、严重等级和参数 [ ] 规则执行支持超时、重试和批次化 [ ] 问题支持去重、确认、修复和关闭 [ ] 文档版本和向量 content_hash 可校验 [ ] 向量模型、维度和索引元数据可查询 [ ] 时序数据支持时间缺口和实体关联检测 [ ] 已记录规则耗时、问题数量和趋势 [ ] 高风险修复具备审批和回滚方案 [ ] 已完成真实查询和质量问题回归验证6.3 复盘结论
多模数据质量治理的难点,不是写出一条SELECT,而是把规则变成可以长期运行、可以解释、可以追责和可以闭环的工程能力。
推荐质量治理流程:
登记数据资产 -> 建立规则元数据 -> 执行单模质量检查 -> 执行跨模关联检查 -> 生成问题指纹 -> 分派责任人 -> 修复并回归验证 -> 记录质量时序指标 -> 调整规则和阈值四类数据各自承担不同职责:
关系数据:验证主键、租户、状态和业务实体; 文档数据:验证正文、版本、标签和来源; 时序数据:验证时间连续性、迟到和实体关联; 向量数据:验证维度、模型、内容哈希和检索可用性。最终质量验收至少回答:
- 已发布文档是否都能被正确检索?
- 向量是否来自当前文档版本和正确模型?
- 时序数据是否覆盖关键业务时间窗口?
- 关系、文档、时序和向量之间是否存在孤儿或错租户记录?
- 质量问题是否能定位到资产、规则和负责人?
- 规则执行本身是否会影响在线查询?
当规则 SQL、质量元数据、问题工单、时序指标和真实查询验证形成闭环后,多模数据质量就从人工抽查变成了可持续运行的数据平台能力。
转载自:https://blog.csdn.net/u014727709/article/details/165123052
欢迎 👍点赞✍评论⭐收藏,欢迎指正