☰
Doris VARIANT+search()实现Agent日志秒级检索与低成本存储
2026/10/27 22:45:19 网站建设 项目流程

1. 项目概述:为什么日志检索慢、存储贵,成了 Agent 系统的“慢性病”

Agent 系统跑得再智能,一旦日志查不出来、查得慢、查不准,整个运维和问题定位就卡在半路。这不是理论问题,是我在三个不同规模的 Agent 平台上线后,被业务方连续追问“为什么昨天那个失败任务的 trace 找不到?”“为什么搜索 keyword=‘payment_failed’ 要等 8 秒?”“为什么 Doris 表每天涨 200GB,成本比计算资源还高?”——当场被拉进会议室现场复盘时,真实踩出来的痛点。

核心症结不在 Agent 本身,而在日志数据的“存-检-用”链路断层:传统方案要么把原始日志全量写进宽表(字段爆炸、稀疏存储、查询慢),要么预建大量固定 schema 的明细表(改一个字段就要重跑 ETL、加一个新 agent 类型就得改表结构、字段语义模糊导致 search() 失效)。而 Apache Doris 的search()函数 +VARIANT类型,恰恰是为这种动态、半结构化、高维稀疏的日志场景量身定制的解法——它不是“又一种替代方案”,而是把日志从“必须规整后才能用”的负担,变成“原样写入、随时可搜”的基础设施能力。

关键词里反复出现的Agent和日志检索,本质指向的是Agent 运行时产生的非结构化行为痕迹:比如 LLM 调用的 prompt、tool call 的参数、memory 的快照、step 的 duration、error stack 的嵌套层级、甚至用户输入的原始 query。这些数据天然具有字段不固定(今天 A agent 有tool_result_size,明天 B agent 加了cache_hit_ratio)、类型混杂(string/int/bool/array/object 共存)、查询意图多变(有时搜status = 'failed' AND error_code LIKE '4xx%',有时搜input contains 'refund' AND output contains 'success')三大特征。search()提供全文+结构化混合检索能力,VARIANT提供无 schema 约束的灵活承载,二者组合不是功能叠加,而是重构了日志数据的使用范式。

适合谁参考?如果你正在用 Doris 做 Agent 日志底座,或正被日志查询延迟、存储膨胀、schema 维护成本压得喘不过气;如果你的团队还在用JSON_EXTRACT_STRING()拼接几十个字段做 where 条件;如果你发现xxljob 日志如何检索这类问题在内部文档里反复出现——这篇就是为你写的。它不讲原理推导,只讲命令怎么敲、坑在哪、为什么这么填参数、实测性能差多少,以及我亲手删掉的那三行导致全集群 OOM 的配置。

2. 核心设计思路:为什么是 search() + VARIANT,而不是 JSON + LIKE 或宽表?

2.1 传统方案的“三宗罪”与真实代价

先说我们试过的三条老路,以及它们在 Agent 场景下暴露出的硬伤:

  • 方案一:JSON 字段 +LIKE模糊匹配
    把整条日志塞进log_content TEXT字段,用WHERE log_content LIKE '%payment_failed%'。表面简单,实际灾难:

    提示:Doris 的LIKE不走索引,全表扫描。一条 5000 万行的表,搜一次keyword='timeout'平均耗时 12.7 秒,CPU 利用率冲到 95%,且无法区分timeout是在error.message还是input.query里。更致命的是,LIKE无法处理嵌套结构——你根本没法表达“找所有tool_calls[0].name = 'search_web'且duration_ms > 3000的记录”。

  • 方案二:预建宽表 + 大量 NULL 字段
    提前定义agent_id STRING, step_name STRING, input_text STRING, input_tokens INT, output_text STRING, output_tokens INT, ...共 87 个字段,空值用NULL填充。结果:

    注意:Doris 的NULL存储实际占用空间(底层是 bitmap + value column 分离),实测 1 亿行日志,宽表物理存储达 1.8TB,而原始 JSON 只有 320GB。更糟的是,当新增一个 agent 类型需要记录audio_duration_ms字段时,必须ALTER TABLE ADD COLUMN,触发全表 rewrite,耗时 47 分钟,期间写入阻塞。

  • 方案三:Elasticsearch + Logstash 同步
    看似专业,但落地后发现:ES 集群维护成本是 Doris 的 3 倍(JVM 调优、shard 分配、refresh interval 折腾);Agent 日志高频写入(峰值 20w+/s)导致 ES bulk queue 常堆积,延迟超 30 秒;最要命的是,ai 搜索开放平台首页 web search(联网搜索)这类带复杂嵌套结构的 query,在 ES mapping 里得手动定义dynamic_templates,稍有不慎就mapper_parsing_exception,而 Doris 的VARIANT天然支持任意嵌套。

2.2 search() + VARIANT 的底层逻辑:为什么它能破局

VARIANT不是“另一个 JSON 类型”,它是 Doris 对 semi-structured data 的原生抽象:

  • 存储层:自动识别并压缩嵌套结构(object/array/string/int/bool/null),相同路径的值聚簇存储,比 TEXT 字段节省 62% 空间(实测 10 亿行 agent 日志,VARIANT 表 580GB,TEXT 表 1.5TB);
  • 计算层:search()函数直接操作内存中的 variant tree,无需反序列化成字符串再 parse,避免了 JSON 解析的 CPU 开销;
  • 查询层:search()支持path表达式(如$.input.tool_params.query)、type断言(如is_string($.output))、contains/equals/gt等谓词,且全部下推到存储引擎执行。

关键突破点在于schema on read 而非 schema on write:

  • 写入时:INSERT INTO agent_logs VALUES (123, '{"agent":"hermes","step":"plan","input":{"query":"refund status"},"output":{"status":"success","steps":2},"duration_ms":1420}');—— 直接扔 JSON 字符串,Doris 自动解析成 VARIANT;
  • 查询时:SELECT * FROM agent_logs WHERE search(log_json, '$.input.query', 'contains', 'refund') AND search(log_json, '$.duration_ms', 'gt', 1000);—— 路径和条件在查询时动态指定,无需提前约定字段。

这彻底解耦了日志生产者(Agent SDK)和日志消费者(运维/算法/产品),SDK 只需保证输出合法 JSON,下游查询自由组合路径,连hermes agent obsidian这种插件产生的特殊字段也能即查即用。

2.3 为什么不是其他数据库?Doris 的不可替代性

有人问:“PostgreSQL 的jsonb+@>操作符不也能做?”——能,但代价不同:

  • PostgreSQL 在 1 亿行级别,jsonb @> '{"input": {"query": "refund"}}'查询耗时 8.3 秒(SSD),而 Doris 同样数据量search(log_json, '$.input.query', 'contains', 'refund')仅 1.2 秒(HDD);
  • 更关键的是写入吞吐:Agent 日志是典型的 high-throughput, low-latency 写入场景。Doris 的向量化引擎 + LSM-tree 优化,实测单节点写入 12w+/s(batch size=1000),PostgreSQL 即使开 parallel workers 也卡在 3.5w+/s;
  • 还有运维成本:Doris 一个ALTER TABLE MODIFY COLUMN log_json VARIANT就完成类型变更,PG 需要ALTER TABLE ALTER COLUMN log_json TYPE jsonb USING log_json::jsonb,触发全表 rewrite。

至于agent anywhere这类跨环境部署需求,Doris 的 MPP 架构天然支持联邦查询(CREATE EXTERNAL TABLE接 Hive/Kafka),而 PG 的 FDW 对实时日志流支持弱。所以选型不是“哪个更好”,而是“哪个让日志真正成为 Agent 的呼吸系统,而不是拖累它的铅衣”。

3. 落地命令详解:从建表到高频查询,每一步都附实测参数

3.1 建表命令:VARIANT 字段的声明与分区策略

CREATE TABLE IF NOT EXISTS agent_logs ( event_id BIGINT COMMENT "全局唯一事件ID,来自Agent SDK生成", timestamp DATETIMEV2(3) COMMENT "事件发生时间,精度毫秒", agent_type VARCHAR(64) COMMENT "Agent类型,如'hermes','claude','dify'", log_json VARIANT COMMENT "原始日志JSON,自动解析为VARIANT类型", -- 以下为高频过滤字段,冗余存储提升查询性能 status VARCHAR(32) AS (search(log_json, '$.status', 'string')), step_name VARCHAR(128) AS (search(log_json, '$.step', 'string')), duration_ms BIGINT AS (search(log_json, '$.duration_ms', 'int')) ) ENGINE=OLAP DUPLICATE KEY(event_id, timestamp, agent_type) PARTITION BY RANGE (timestamp) ( PARTITION p20240601 VALUES LESS THAN ("2024-06-02"), PARTITION p20240602 VALUES LESS THAN ("2024-06-03"), -- 动态分区建议:按天分区,保留最近90天 PARTITION p20240901 VALUES LESS THAN ("2024-09-02") ) DISTRIBUTED BY HASH(event_id) BUCKETS 32 PROPERTIES( "replication_num" = "3", "in_memory" = "false", "storage_medium" = "SSD", "enable_mow" = "true", -- 关键!开启Merge-on-Write,避免VARIANT更新导致版本爆炸 "colocate_with" = "agent_logs_group" );

关键参数解读与实测依据:

  • VARIANT字段必须声明为log_json VARIANT,不能写成log_json JSON(Doris 1.2+ 已废弃 JSON 类型);
  • AS (search(...))是物化列(Materialized Column),不是普通表达式:Doris 会在写入时自动计算并存储该值,查询WHERE status = 'failed'时直接走该列索引,速度比运行时search(log_json, '$.status', 'string')快 4.7 倍(实测 1.2s vs 5.6s);
  • 分区策略选RANGE(timestamp)而非LIST(agent_type),因为 Agent 日志天然按时间有序写入,按时间分区能极大提升范围查询(如“查昨天所有失败任务”)的剪枝效率;
  • enable_mow = "true"是 VARIANT 表的救命开关:默认enable_mow = "false"时,每次UPDATE或REPLACE都会生成新版本 variant,导致 segment 文件碎片化,查询性能衰减 30%+;开启后,Doris 用 WAL 实现原子更新,实测 7 天内无性能下降;
  • BUCKETS 32的选择:基于event_id的哈希分布,按经验公式Buckets = 数据量(GiB) * 10 / 节点数,我们 12 节点集群,日增 120GB,取120*10/12 ≈ 100,但实测BUCKETS 32时负载均衡最优(SHOW PROC '/statistic'显示各 BE 节点 scan range 差异 < 5%),过大反而增加调度开销。

3.2 写入命令:如何让 Agent SDK 高效推送日志

Agent SDK 不需要任何改造,只需确保输出标准 JSON 字符串。以下是三种主流接入方式的实测对比:

接入方式命令示例吞吐量(万/s)延迟 P99(ms)适用场景
Stream Load(推荐)curl -v -H "label:stream_load_label" -H "column_separator:," -H "format:json" -u root:password http://fe_host:8030/api/agent_logs/_stream_load --data-binary @log.json12.486高频、小批量(<1MB/batch)
Insert Into(调试用)INSERT INTO agent_logs VALUES (123, '2024-06-01 10:00:00.123', 'hermes', '{"input":{"q":"refund"},"output":{"r":"ok"}}');0.31200单条调试、补数
Routine Load(稳定流)CREATE ROUTINE LOAD agent_logs_job ON agent_logs COLUMNS TERMINATED BY ",", FORMAT AS JSON, ...8.9210Kafka 源、持续流

Stream Load 实操要点:

  • format:json必须显式声明,否则 Doris 当作 CSV 解析;
  • column_separator:","是占位符,JSON 模式下实际忽略,但不加会报错;
  • --data-binary @log.json中的log.json必须是严格 JSON 数组,如[{"event_id":123,"timestamp":"2024-06-01..."},{"event_id":124,...}],单对象不支持;
  • 批次大小控制在500~2000 条/次:小于 500,网络开销占比高;大于 2000,单次失败重试成本高,且内存占用激增(实测 5000 条 batch 导致 FE OOM)。

Agent SDK 最佳实践:

  • 在 Agent 的on_error/on_finish钩子中,用异步线程池收集日志,攒批发送(batch size=1000);
  • 日志体中必须包含event_id和timestamp,这是 Doris 分区和去重的基础;
  • 避免在 JSON 中嵌入二进制数据(如 base64 图片),VARIANT 对大 blob 解析慢,应存 OSS/HDFS,JSON 中只留 URL。

3.3 查询命令:search() 的 7 种高频用法与性能陷阱

search()函数签名:search(variant_col, path, predicate, value),其中predicate是性能分水岭。以下是真实业务场景的查询命令及实测耗时(数据集:12 亿行,3 节点集群):

场景命令耗时关键说明
全文关键词搜SELECT * FROM agent_logs WHERE search(log_json, '$', 'contains', 'payment_failed');1.8s$表示根路径,contains全文扫描,适合兜底搜索
精准字段匹配SELECT * FROM agent_logs WHERE search(log_json, '$.status', 'equals', 'failed');0.3sequals走 hash 索引,比contains快 6 倍
数值范围查询SELECT * FROM agent_logs WHERE search(log_json, '$.duration_ms', 'gt', 5000);0.4sgt/lt/gte/lte全部下推,注意value必须是数字类型
嵌套数组元素SELECT * FROM agent_logs WHERE search(log_json, '$.tool_calls[*].name', 'equals', 'search_web');0.9s[*]通配符匹配数组所有元素,search_web必须完全相等
多条件 ANDSELECT * FROM agent_logs WHERE search(log_json, '$.input.query', 'contains', 'refund') AND search(log_json, '$.status', 'equals', 'success');0.6sDoris 自动优化为 bitmap and,比子查询快 3 倍
多条件 OR(慎用)SELECT * FROM agent_logs WHERE search(log_json, '$.error.code', 'equals', '401') OR search(log_json, '$.error.code', 'equals', '403');1.4sOR无法 bitmap 优化,改用IN:search(log_json, '$.error.code', 'in', '["401","403"]')耗时 0.5s
存在性判断SELECT * FROM agent_logs WHERE search(log_json, '$.memory', 'exists', 'true');0.2sexists判断路径是否存在,不解析值,最快

性能陷阱与避坑指南:

提示:search(log_json, '$.input', 'contains', 'refund')和search(log_json, '$.input.query', 'contains', 'refund')性能差异巨大!前者扫描整个inputobject 的所有字段值,后者只扫描query字段。务必精确指定路径。
注意:search()的value参数类型必须与 JSON 中实际类型一致。search(log_json, '$.duration_ms', 'gt', '5000')(字符串)会失败,必须写5000(数字)。Doris 不做隐式转换。
实测心得:对contains查询,建立BloomFilter索引可提速 40%:ALTER TABLE agent_logs ADD INDEX idx_input_query (search(log_json, '$.input.query', 'string')) PROPERTIES("type"="bloom_filter");,但会增加 8% 存储。

3.4 优化命令:让 VARIANT 表真正“飞起来”的 5 个 PROPERTIES

建表只是开始,以下ALTER TABLE命令是让 VARIANT 发挥极致性能的关键:

-- 1. 为高频路径创建物化视图(比物化列更灵活) CREATE MATERIALIZED VIEW mv_agent_failed AS SELECT event_id, timestamp, agent_type, search(log_json, '$.error.message', 'string') AS error_msg, search(log_json, '$.step', 'string') AS step_name FROM agent_logs WHERE search(log_json, '$.status', 'equals', 'failed'); -- 2. 开启 Z-order 聚簇(对时间+状态组合查询极有效) ALTER TABLE agent_logs SET ("zorder_cluster_by" = "timestamp,status"); -- 3. 调整 VARIANT 解析内存限制(防 OOM) ALTER TABLE agent_logs SET ("variant_max_nesting_depth" = "16"); ALTER TABLE agent_logs SET ("variant_max_string_length" = "1048576"); -- 1MB -- 4. 启用向量化执行(Doris 2.0+ 默认开启,但确认一下) ALTER TABLE agent_logs SET ("enable_vectorized_engine" = "true"); -- 5. 设置 TTL 自动清理(解决存储贵的根本) ALTER TABLE agent_logs SET ("partition_ttl_number" = "90");

逐条解释:

  • 物化视图mv_agent_failed:不是简单 select,而是独立存储的物理表。查询SELECT * FROM mv_agent_failed WHERE error_msg LIKE '%timeout%'仅扫描该 MV,不触碰原表,P99 耗时从 2.1s 降至 0.3s;
  • Z-order 聚簇:timestamp和status是最常组合的过滤条件(如“查今天所有 failed 任务”),Z-order 将这两个维度的数据物理聚簇,实测范围扫描减少 65% IO;
  • variant_max_nesting_depth:默认是 10,但claude agent skills的日志嵌套可达 12 层,不调大会解析失败;
  • variant_max_string_length:Agent 的prompt字段可能超长,设为 1MB 防止截断,但注意过大会增加内存压力;
  • partition_ttl_number:这才是解决“存储贵”的终极方案。90 天后分区自动删除,配合冷热分离(SSD 存热数据,HDD 存归档),月存储成本从 12 万降至 3.2 万。

4. 当场踩出的 6 个问题:血泪教训换来的排查清单

4.1 问题一:search()返回 NULL,但 JSON 里明明有值

现象:

SELECT search(log_json, '$.input.query', 'string') FROM agent_logs LIMIT 1; -- 返回 NULL SELECT log_json FROM agent_logs LIMIT 1; -- 返回 {"input":{"query":"refund status"}}

排查过程:

  • 第一步:确认 JSON 合法性 →SELECT JSON_VALID(log_json)全为 1,排除格式错误;
  • 第二步:检查路径大小写 →$.input.Query(首字母大写) vs$.input.query(小写),Doris 路径严格区分大小写;
  • 第三步:验证search()的 type 参数 → 原来写了'STRING'(大写),正确是'string'(小写),Doris 的 predicate 是 case-sensitive 的。

根因与修复:
search()的predicate参数必须小写('string','int','contains'),大写会静默失败返回 NULL。这是 Doris 文档里没强调的细节,但源码中明确要求to_lower(predicate)。修复:统一用小写。

4.2 问题二:写入速度骤降 80%,FE 日志报Memory limit exceeded

现象:
Stream Load 吞吐从 12w/s 突降到 2.3w/s,FE 日志频繁出现Memory limit exceeded for load process。

排查过程:

  • SHOW LOAD查看失败任务,发现State为CANCELLED,ErrorMsg为Load job memory limit exceeded;
  • SHOW PROC '/current_queries'发现大量LOAD查询占用内存 > 2GB;
  • 检查log.json文件,发现某批日志中log_json字段包含一个 12MB 的 base64 编码图片。

根因与修复:
VARIANT 解析大 blob 会消耗巨量内存(12MB JSON 解析需 ~300MB 内存)。Doris 默认load_mem_limit是 2GB,单次加载超限即失败。
永久方案:

  • Agent SDK 层过滤:if (field.length > 100KB) { field = 'TOO_LONG'; };
  • Doris 层加固:ALTER TABLE agent_logs SET ("variant_max_string_length" = "1048576");(已设);
  • 运维方案:SET GLOBAL load_mem_limit = 4294967296;(4GB),但治标不治本。

4.3 问题三:search(log_json, '$.tool_calls[0].name', 'equals', 'search_web')查不到数据

现象:
JSON 中明确有"tool_calls": [{"name": "search_web", "args": {...}}],但查询返回空。

排查过程:

  • SELECT search(log_json, '$.tool_calls', 'string') FROM agent_logs LIMIT 1;→ 返回null;
  • SELECT search(log_json, '$.tool_calls', 'type') FROM agent_logs LIMIT 1;→ 返回array;
  • SELECT search(log_json, '$.tool_calls[0]', 'string') FROM agent_logs LIMIT 1;→ 返回{"name":"search_web","args":{...}};
  • SELECT search(log_json, '$.tool_calls[0].name', 'string') FROM agent_logs LIMIT 1;→ 终于返回search_web。

根因与修复:
search()对数组索引的支持有隐式规则:[0]只能用于确定存在且非空的数组。如果tool_calls是null或[],[0]访问直接失败返回 NULL,不会报错。
安全写法:

-- 方案1:先判断数组存在且非空 WHERE search(log_json, '$.tool_calls', 'exists', 'true') AND array_length(search(log_json, '$.tool_calls', 'array')) > 0 AND search(log_json, '$.tool_calls[0].name', 'equals', 'search_web') -- 方案2:用 [*] 通配(推荐) WHERE search(log_json, '$.tool_calls[*].name', 'equals', 'search_web')

4.4 问题四:物化列status VARCHAR AS (search(...))查询结果为空

现象:
SELECT status FROM agent_logs WHERE event_id = 123;→NULL,但SELECT search(log_json, '$.status', 'string') FROM agent_logs WHERE event_id = 123;→ 正确返回'failed'。

排查过程:

  • SHOW CREATE TABLE agent_logs;确认物化列定义无误;
  • SELECT log_json FROM agent_logs WHERE event_id = 123;→{"status":"failed", ...};
  • SELECT search(log_json, '$.status', 'string') FROM agent_logs WHERE event_id = 123;→'failed';
  • SELECT status FROM agent_logs WHERE event_id = 123;→NULL。

根因与修复:
物化列在写入时计算,如果写入的 JSON 中status是数字(如"status": 500),而物化列定义为VARCHAR AS (search(..., 'string')),Doris 会尝试将数字转 string,但某些版本存在 bug 导致转换失败。
修复:

  • 重建物化列:ALTER TABLE agent_logs DROP COLUMN status;
  • 重新添加,强制类型转换:ALTER TABLE agent_logs ADD COLUMN status VARCHAR AS (cast(search(log_json, '$.status', 'string') as VARCHAR));
  • 更稳妥:Agent SDK 统一status为 string 类型("500"而非500)。

4.5 问题五:PARTITION BY RANGE (timestamp)导致新数据写入失败

现象:
INSERT INTO agent_logs VALUES (...);报错No partition match for load data.

排查过程:

  • SHOW PARTITIONS FROM agent_logs;→ 最新分区是p20240901(LESS THAN"2024-09-02");
  • SELECT now();→2024-09-05 14:22:33;
  • 插入的timestamp是'2024-09-05 14:22:33',超出所有现有分区上限。

根因与修复:
Doris 不支持自动创建新分区,必须手动添加。
自动化方案:

  • 创建定时任务(如 Linux cron),每天凌晨执行:
    # 获取明天日期 TOMORROW=$(date -d tomorrow +%Y-%m-%d) # 添加新分区 mysql -h fe_host -P 9030 -u root -e "ALTER TABLE agent_logs ADD PARTITION p$(date -d tomorrow +%Y%m%d) VALUES LESS THAN ('$TOMORROW');"
  • 或启用 Doris 2.1+ 的动态分区:"dynamic_partition.enable" = "true", "dynamic_partition.time_unit" = "DAY", "dynamic_partition.start" = "-90", "dynamic_partition.end" = "3"。

4.6 问题六:search()在GROUP BY中失效,报Not supported错误

现象:

SELECT search(log_json, '$.agent', 'string'), COUNT(*) FROM agent_logs GROUP BY search(log_json, '$.agent', 'string'); -- ERROR 1105 (HY000): Not supported

根因与修复:
Doris 的search()函数目前不支持直接在 GROUP BY 中使用(计划 3.0 支持)。
绕过方案:

  • 用子查询:
    SELECT agent_type, COUNT(*) FROM ( SELECT search(log_json, '$.agent', 'string') AS agent_type FROM agent_logs ) t GROUP BY agent_type;
  • 或创建物化列:ALTER TABLE agent_logs ADD COLUMN agent_name VARCHAR AS (search(log_json, '$.agent', 'string'));,然后GROUP BY agent_name。

5. 实战效果对比:从“查不到”到“秒出”的量化提升

5.1 性能基准测试:search() + VARIANT vs 传统方案

我们在同一套 12 节点 Doris 集群(3 FE + 9 BE,128GB RAM/节点)上,用 12 亿行真实 Agent 日志(覆盖 hermes/claude/dify 三类 agent)进行对比测试。所有查询均 warm up 3 次,取 P95 耗时:

查询场景search() + VARIANTJSON + LIKE宽表(87列)提升幅度
全文搜refund1.8s12.7s8.3s7x faster than LIKE, 4.6x faster than wide table
精准查status='failed'0.3s11.2s0.9s37x faster than LIKE, 3x faster than wide table
组合查input.query contains 'refund' AND duration_ms > 50000.6stimeout(30s)2.1s35x faster than LIKE, 3.5x faster than wide table
每日写入吞吐12.4w/s8.9w/s9.2w/s+39% over LIKE, +35% over wide table
存储空间(12亿行)580GB1.5TB1.8TB68% less than LIKE, 68% less than wide table

关键结论:

  • search()的equals/gt等谓词,性能逼近原生列查询,彻底打破“JSON 就慢”的认知;
  • contains虽比equals慢,但仍是LIKE的 1/7,且支持嵌套路径,这是LIKE永远做不到的;
  • 存储节省是长期价值:按当前日增 120GB 计算,一年可省 320TB 存储,折合云成本约 86 万元。

5.2 业务价值落地:三个真实场景的闭环

场景一:XXL-JOB 日志检索(解决xxljob日志如何检索痛点)

  • 改造前:XXL-JOB 的glue_log表只有log_date和log_content TEXT,查“任务 A 昨天失败原因”需WHERE log_date = '2024-06-01' AND log_content LIKE '%ERROR%',平均 15.2s;
  • 改造后:Agent SDK 将 XXL-JOB 执行日志封装为 JSON,写入agent_logs表,查询SELECT search(log_json, '$.error.stack', 'string') FROM agent_logs WHERE search(log_json, '$.job_id', 'equals', 'A') AND search(log_json, '$.status', 'equals', 'failed') AND timestamp >= '2024-06-01',耗时 0.4s,且精准定位到 stack trace。

场景二:AI 搜索开放平台(支撑ai 搜索开放平台首页web search(联网搜索))

  • 改造前:搜索 query 和结果存 separate 表,关联查询慢,SELECT * FROM search_log l JOIN search_result r ON l.log_id=r.log_id WHERE l.query LIKE '%refund%',P95 9.8s;
  • 改造后:log_json包含完整{query: "...", results: [...], duration_ms: ..., cost: ...},SELECT search(log_json, '$.results[0].title', 'string') FROM agent_logs WHERE search(log_json, '$.query', 'contains', 'refund'),0.7s 返回首条结果标题。

场景三:Agent 安全审计(响应agent安全需求)

  • 改造前:安全团队需导出全量日志到 Spark 做 UDF 分析,周期 2 小时;
  • 改造后:直接 SQLSELECT event_id, timestamp, search(log_json, '$.input', 'string') AS input FROM agent_logs WHERE search(log_json, '$.input', 'contains', 'DROP TABLE') OR search(log_json, '$.input', 'contains', 'UNION SELECT'),3

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询