Nightingale Doris 数据源查询指南:query-datasource 技能下的 SQL 日志与时序查询实战
【免费下载链接】nightingaleNightingale is to monitoring and alerting what Grafana is to visualization.项目地址: https://gitcode.com/GitHub_Trending/ni/nightingale
Nightingale 的 AI Agent 内置query-datasource技能(定义见 SKILL.md),允许 Agent 通过统一的 HTTP API 查询 Prometheus、Elasticsearch、ClickHouse、Doris 等多种数据源。本文聚焦其中doris插件类型:围绕 doris.md 展开,完整讲解 Doris 数据源的元数据查询、日志查询与时序查询 API 的请求格式与参数语义,并结合仓库源码剖析其连接配置、只读安全校验与结果行数限制等底层实现。读完本文,你将能在自己的 Nightingale 环境中直接调用这些 API,或让 Agent 正确构造 Doris 查询请求。
一、Doris 数据源在 query-datasource 技能中的定位
在 query-datasource 技能说明 的数据源类型速查表中,Doris 的定位如下:
| plugin_type | 数据源 | 查询语言 | 适用场景 |
|---|---|---|---|
doris | Doris | SQL(MySQL 兼容方言) | 日志查询、时序查询 |
Doris 使用 SQL 作为查询语言,语法与 MySQL 高度兼容,因此在 Nightingale 中它被归类为 SQL 型数据源。技能文档明确指出:ClickHouse、MySQL、PostgreSQL、Doris 共享同一套元数据查询端点(db-databases、db-tables、db-desc-table),而 TDengine 则使用专用端点。这一划分与源码中 datasource_query.go 的sqlDatasourceTypes集合一致——mysql、ck、pgsql、doris、tdengine均走 SQL 查询路径,由GetSQLDatasource获取插件实例后再调用QueryData/QueryLog。
二、查询前置条件:登录与数据源定位
所有 Doris 查询请求都需要Authorization: Bearer <token>请求头,token 的获取与数据源 ID 的确定遵循技能文档的统一执行步骤:
- 登录获取 Token:
POST /api/n9e/auth/login,请求体为{"username":"<username>","password":"<password>"},从响应中提取dat.access_token; - 查询数据源列表:
POST /api/n9e/datasource/list,请求体为空{},响应中的每个数据源包含id、name、plugin_type三个字段,据此确定 Doris 数据源对应的datasource_id; - 发起查询:根据
plugin_type为doris,按本文后续 API 格式构造请求。
注意:技能文档强调,所有查询都要求先通过数据源列表拿到
datasource_id,并且所有 API 响应统一包裹在{"dat": <data>}结构中。
三、元数据查询:库、表、表结构
Doris 与 ClickHouse、MySQL、PostgreSQL 共用以下三个元数据端点(路由注册见 router.go,接口实现见 router_datasource_db.go):
1. 查询数据库列表
POST /api/n9e/db-databases Authorization: Bearer <token> Content-Type: application/json Body: {"cate": "doris", "datasource_id": 1, "query": []}服务端通过dscache.DsCache.Get(cate, datasourceId)获取 Doris 插件实例,调用其ShowDatabases方法。底层执行SHOW DATABASES(见 doris.go),返回数据库名数组。
2. 查询表列表
POST /api/n9e/db-tables Authorization: Bearer <token> Content-Type: application/json Body: {"cate": "doris", "datasource_id": 1, "query": ["database_name"]}query数组只接受一个字符串参数——数据库名。服务端调用ShowTables(ctx, database),底层执行SHOW TABLES INdatabase``(见 doris.go)。
3. 查询表结构
POST /api/n9e/db-desc-table Authorization: Bearer <token> Content-Type: application/json Body: {"cate": "doris", "datasource_id": 1, "query": [{"database": "logs_db", "table": "access_log"}]}query数组中的对象需包含database与table两个字段。服务端调用DescribeTable(ctx, query)(见 datasource/doris/doris.go),底层执行DESCRIBE db.table并返回列属性数组。源码中 DescTable 还会将 Doris 原生类型映射为内部类型(Type2字段),例如:
double、decimal*→ float;datetime、date、date*→ date;text、varchar*、char*→ text;- 含
int的类型 → long。
并标注该列是否可建立索引(Indexable),供日志报表做字段提取时参考。
四、日志查询:POST /api/n9e/logs-query
POST /api/n9e/logs-query Authorization: Bearer <token> Content-Type: application/json{ "cate": "doris", "datasource_id": 1, "query": [ { "sql": "SELECT * FROM logs_db.access_log WHERE log_time >= FROM_UNIXTIME($from) AND log_time < FROM_UNIXTIME($to) AND message LIKE '%error%' ORDER BY log_time DESC LIMIT 100", "from": 1712000000, "to": 1712003600, "database": "logs_db" } ] }要点说明:
sql中的$from与$to是时间变量占位符,由系统自动替换为请求中from/to的秒级 Unix 时间戳,无需手写具体数值;FROM_UNIXTIME()将秒级时间戳转换为 Doris 的日期时间类型,用于与log_time字段比较;- 建议显式添加
ORDER BY ... DESC与LIMIT控制返回条数;在 Agent 工具侧,日志查询默认limit为 50,上限 500(见 datasource_query.go); - 服务端实现见 QueryLog:解析参数后调用
doris.QueryLogs(见 logs.go,内部等价于Query()),响应中返回日志条目数组及total总数。
五、时序查询:POST /api/n9e/ds-query
POST /api/n9e/ds-query Authorization: Bearer <token> Content-Type: application/json{ "cate": "doris", "datasource_id": 1, "query": [ { "sql": "SELECT DATE_TRUNC(log_time, INTERVAL 1 MINUTE) AS ts, COUNT(*) AS value FROM logs_db.access_log WHERE log_time >= FROM_UNIXTIME($from) AND log_time < FROM_UNIXTIME($to) GROUP BY ts ORDER BY ts", "from": 1712000000, "to": 1712003600, "database": "logs_db", "keys": { "valueKey": "value", "labelKey": "", "timeKey": "ts" } } ] }要点说明:
sql必须产出时间列 + 数值列的结构(上例中ts为时间列、value为数值列),GROUP BY按时间粒度聚合;keys.timeKey指定 SQL 结果中的时间列名,keys.valueKey指定数值列名,keys.labelKey可留空,若有多个分组维度列,用空格分隔多个列名(技能文档的统一约定);- 服务端实现见 QueryData:
valueKey为必填,缺失会直接返回错误valueKey is required;随后按from/to或默认interval=60秒计算时间范围,再调用doris.QueryTimeseries(见 timeseries.go)并通过sqlbase.FormatMetricValues按keys组装为时序点列。
六、Query Parameters 参数总表
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
sql | string | 是 | SQL 查询语句,支持$from、$to时间变量 |
from | int64 | 是 | 开始时间,Unix 秒级时间戳 |
to | int64 | 是 | 结束时间,Unix 秒级时间戳 |
database | string | 是 | 数据库名(Doris 查询必须指定) |
keys.valueKey | string | 否 | 数值列名(时序查询必填) |
keys.labelKey | string | 否 | 标签/分组列名 |
keys.timeKey | string | 否 | 时间列名 |
注意:from/to为秒级时间戳。上例1712000000与1712003600对应一个 1 小时的时间窗口;若请求中未显式携带from/to而只有interval(如告警规则预览场景),QueryLog 会以当前时间向前回退interval秒自动补全时间范围。
七、底层实现:连接配置、只读校验与行数上限
1. 数据源连接配置
Doris 插件通过 dskit/doris/doris.go 的Doris结构体承载连接配置,关键字段(也是数据源设置中的 JSON key)如下:
| 配置项 | 默认值 | 说明 |
|---|---|---|
doris.addr | 无 | FE 的 MySQL 协议地址(center 可达),必填 |
doris.internal_addr | 无 | edge 进程使用的 FE MySQL 地址,edge 场景下优先使用 |
doris.fe_addr | 无 | FE 的 HTTP 端点 |
doris.user/doris.password | 无 | 只读账号,Validate中 user 必填 |
doris.timeout | 60000 | 查询超时,单位毫秒 |
doris.max_idle_conns | 10 | 连接池最大空闲连接数 |
doris.max_open_conns | 100 | 连接池最大打开连接数 |
doris.conn_max_lifetime | 14400 | 连接最大存活时间,单位秒 |
doris.max_query_rows | 500 | 单次查询最大返回行数 |
doris.cluster_name | 无 | 集群名 |
doris.enable_write/doris.user_write/doris.password_write | 无 | 写用户开关与写账号,写路径使用独立连接 |
实现上,Doris 使用标准 MySQL 驱动(go-sql-driver/mysql)建立连接,DSN 格式为user:pass@tcp(addr)/db?charset=utf8,连接按addr:user:password:database组合缓存于连接池(见 NewConn),并按库名隔离复用。
2. 只读安全校验(双层防线)
Doris 查询严格只读,有两层校验:
- 服务端插件层:timeseries.go 维护
DorisBannedOp黑名单,包含CREATE、INSERT、ALTER、REVOKE、DROP、RENAME、ATTACH、DETACH、OPTIMIZE、TRUNCATE、SET,SQL 按空格分词后命中即拒绝; - Agent 工具层:datasource_query.go 的
validateReadOnlySQL在 Agent 构造请求时前置拦截INSERT、UPDATE、DELETE、DROP、ALTER、CREATE、TRUNCATE、REPLACE、GRANT、REVOKE等写操作关键字,只允许SELECT类查询通过。
3. 结果行数上限(MaxQueryRows)
为避免大结果集拖垮服务,CheckMaxQueryRows 通过 SQL 分析跳过无需检查的聚合查询或LIMIT不超过上限的查询;对需要检查的查询,采用SELECT 1 FROM (<sql>) AS __probe_chk LIMIT maxRows+1的探测方式(见 probeRowCount),利用 Doris 对LIMIT的提前终止优化,以 O(maxRows) 的开销判定是否超限,优于COUNT(*)的全量扫描。
八、注意事项与最佳实践
- 只读约束:写操作被明令禁止,切勿在查询中使用
CREATE、INSERT、UPDATE、DELETE、ALTER、DROP等语句; database必填:Doris 查询必须显式携带database字段,服务端连接会按库隔离(连接缓存 key 包含 database);- 时间函数:SQL 中可直接使用
DATE_TRUNC()、NOW()、FROM_UNIXTIME()等函数,配合$from/$to变量完成时间窗口过滤与粒度聚合; - MySQL 兼容:Doris SQL 语法与 MySQL 高度兼容,可用反引号引用库名/表名,标准
SELECT ... WHERE ... GROUP BY ... ORDER BY ... LIMIT均可直接使用; - 响应结构:所有接口响应统一为
{"dat": <data>},日志查询返回total与条目列表,时序查询返回ref、metric标签与values时间点序列; - 调试提示:若返回
operation ... is forbid,说明 SQL 命中了只读黑名单;若返回query result rows count exceeds the maximum limit,请为查询补充LIMIT或WHERE过滤条件。
通过上述 API 与源码级的参数理解,你可以直接在脚本或 Agent 工具中完成对 Nightingale 中 Doris 数据源的库表探查、日志检索与时序聚合,并将结果接入告警、报表或对话式分析流程。
【免费下载链接】nightingaleNightingale is to monitoring and alerting what Grafana is to visualization.项目地址: https://gitcode.com/GitHub_Trending/ni/nightingale
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考