news 2026/9/12 4:27:17

达梦与人大金仓Prometheus监控利器:sql_zh_exporter深度解析

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
达梦与人大金仓Prometheus监控利器:sql_zh_exporter深度解析

简介:这是一款面向达梦与人大金仓等国产数据库的Prometheus SQL监控导出器,主要解决这两类数据库监控指标难以接入Prometheus生态的问题。通过自定义YAML配置文件,使用者可灵活设定需采集的指标项与采集频率,按需监控CPU、内存、事务处理等性能数据,从而掌握数据库运行状态并辅助调优;配置门槛低,便于运维人员快速定制专属监控方案。资源包共24个文件,核心为8个Go源码文件、5个YAML配置样例,另附Dockerfile、shell脚本、markdown与docx说明文档等,整体约73KB,结构紧凑,便于直接阅读改造或容器化部署;同时提供达梦、Kingbase等作业配置示例,帮助理解采集逻辑。已有231人浏览学习,适合需要为国产数据库搭建Prometheus监控方案的团队或个人参考,可显著缩短监控体系搭建周期,对国产化替代场景下的可观测性建设有实用价值。

1. 达梦与人大金仓的Prometheus监控缺口,为什么需要这个SQL导出器

国产数据库替换浪潮里,达梦和人大金仓的部署量已经不小了,但监控侧一直是块短板。官方自带的管理工具能看到实时状态,却很难把这些指标接进Prometheus + Grafana这套统一的监控体系里。你在Grafana上看MySQL、PostgreSQL的看板看得好好的,一换到达梦就是一片空白,只能眼盯着SQL手工执行几条性能视图,出问题全靠人肉盯。

这个sql_zh_exporter解决的就是这个断层。它用Go实现(文件里能看到exporter.go、job.go、query.go、config.go这些标准Prometheus exporter工程结构),本质上是一个独立的HTTP服务,按你配置的YAML规则周期性地连上达梦或人大金仓的库,执行你指定的性能SQL,拿到结果后转成Prometheus指标格式暴露在/metrics端点。Prometheus服务器定期来拉取,剩下的告警、看板全都复用现有生态。

它不像mysqld_exporter那样把监控项写死,而是把「查什么SQL」「多久查一次」全部外置成了dm_job_config.ymlkingbase_job_config.yml这些配置文件。换一套SQL就能监控不同的维度,不需要改一行Go代码。下面从配置模型、源码机制、部署方式、生产验证四个维度把这个项目拆开讲清楚。

2. YAML配置驱动的多Job模型设计

2.1 双数据库各自的独立配置入口

进入项目根目录,会看到三份基础的YAML配置文件:dm_job_config.yml负责达梦,kingbase_job_config.yml负责人大金仓,job_config.yml是通用或者示例性质的配置。这种把不同数据库类型拆成独立配置的设计,在多实例混合部署时非常省心——你不需要在一份配置里同时维护两套连接串和两套SQL逻辑,各管各的,互不污染。

job_config.yml里还有一个全局性质的顶层参数global_scrape_interval,它定义了导出器主动抓取数据库指标的基础节拍。虽然Prometheus拉取exporter的频率由Prometheus服务端自己的scrape_interval决定,但在exporter内单独设一个采集节拍,是为了避免客户端的每一次HTTP请求都实时去查数据库——那会把数据库压垮,尤其是监控频繁刷新的时候。

提示:项目正文提到「自定义YAML配置文件灵活定义监控指标和采集频率」,本质上就是在这里做文章:采集频率改global_scrape_interval,监控指标改jobs下的queries列表。

2.2dm_job_config.yml的字段结构拆解

打开dm_job_config.yml,核心结构大致是这个模式:

global_scrape_interval: 30s jobs: - name: dm_system_stats enabled: true database: type: dm dsn: "dm://SYSDBA:SYSDBA@127.0.0.1:5236" max_open_conns: 3 max_idle_conns: 1 conn_max_lifetime: 30m queries: - name: dm_sessions_total help: 当前会话总数 interval: 60s sql: | SELECT COUNT(*) FROM V$SESSIONS metrics: - type: gauge value_column: COUNT(*) labels: []

这里逐个字段说下作用:

  • name:Job的名字,会变成指标名的前缀,后面最终暴露出来的是dm_sessions_total这种带上Job维度前缀的指标名。
  • database.type:声明是dm还是kingbase。这个字段决定了它用哪一套驱动去建立数据库连接池,两个库的协议完全不同,不能混。
  • database.dsn:达梦走的是达梦自有的通信协议,DSN格式是dm://用户名:密码@主机:端口;人大金仓底层基于PostgreSQL协议(V8和V9在某些行为上略有不同但基础协议兼容),DSN格式更接近host=127.0.0.1 port=54321 user=system password=123456 dbname=test
  • max_open_connsmax_idle_conns:控制连接池上限。监控采集本身不该把连接池开太大,给业务预留资源才是正路,通常max_open_conns不超过5,如果业务库连接本来就很紧张,压到2也能跑。
  • queries下每一个查询对象,代表一条独立的采集SQL,interval可以覆盖全局采集频率,让不同指标拥有不同节拍。

2.3 多维度指标如何组织进一份配置里

一份配置里可以同时包含多个Job和多个Query,比如加一个专门采集表空间使用率的Job:

- name: dm_tablespace_usage enabled: true database: type: dm dsn: "dm://SYSDBA:SYSDBA@127.0.0.1:5236" max_open_conns: 2 max_idle_conns: 1 queries: - name: dm_tablespace_bytes help: 表空间使用字节数 interval: 120s sql: | SELECT TABLESPACE_NAME, TOTAL_SIZE * PAGE_SIZE AS total_bytes, FREE_SIZE * PAGE_SIZE AS free_bytes FROM DBA_TABLESPACE_USAGE_MONITOR metrics: - type: gauge value_column: total_bytes labels: ["TABLESPACE_NAME"] - type: gauge value_column: free_bytes labels: ["TABLESPACE_NAME"]

逻辑上,sql执行后返回每一行,metrics里声明了把哪一列当作数值(value_column),哪几列当作标签(labels)。TABLESPACE_NAME作为标签维度的好处是,在Grafana里可以按表空间维度切分总大小和剩余空间,一个面板就能把多个表空间的容量趋势全画出来。

2.4 从YAML到内部数据模型的映射

config.go里定义了JobConfigQueryConfig这些结构体,read_config.go负责把YAML内容解析出来并做基础校验。它会检查jobs列表是否为空、dsn是否缺失、enabled字段为false的Job是否会被跳过加载。

把配置文件和指标的逻辑顺序理清楚之后,下一步就要看这层配置是怎么被Go代码用起来的,核心在job.goquery.go里。

3. 采集核心机制:从配置加载到指标暴露的完整链路

3.1 Job是协程还是独立调度器

job.go这个文件是采集调度的核心。读取配置后,程序会根据jobs数组的长度和每个Job的enabled状态,为每个Job创建一个独立的调度协程。每个协程内部是一个循环,用time.Ticker来控制执行节奏。

伪代码逻辑大概是这样的:

func (j *Job) Run(ctx context.Context) { ticker := time.NewTicker(j.getEffectiveInterval()) defer ticker.Stop() for { select { case <-ctx.Done(): return case <-ticker.C: j.collectOnce() } } }

collectOnce内部做的事情依次是:从连接池拿一个连接、执行配置里声明的SQL、把结果集按metrics映射规则转换成Prometheus的prometheus.Descprometheus.Metric对象、最后统一暴露到注册表。这里用的是Go里挂Prometheus client库的标准做法:实现prometheus.Collector接口的Collect方法,然后由/metrics请求触发数据的最终聚合输出。

参数上有个值得留意的点:getEffectiveInterval会判断Query自己的interval是否为零,零就沿用Job级别的间隔,Job级别没配才用全局的global_scrape_interval。这种三级优先级的策略,让精细化的监控调整不需要动其他地方。

3.2 同一套查询如何同时适配达梦与金仓

达梦的V$视图(比如V$SESSIONSV$LOCK)和金仓的pg_stat_activitypg_stat_database几乎是两套完全不同的系统视图。query.go里对结果的解析方式是通用的:不管SQL返回什么列,它都是先按列名映射到value_columnlabels声明。

如果要在同一种Job里切换数据库类型,配置里的type字段会改变驱动加载逻辑。Go的database/sql接口对上层是透明的,换驱动只是换DSN前缀和导入的驱动包的差异。实际工程中,更常见的做法是达梦和金仓各自维护专属的YAML配置,这样SQL写起来干净,不会被跨方言语法干扰。

3.3 处理查询结果为空或SQL出错时的边缘情况

国产数据库在监控时最烦的一点是视图层级不一样——达梦有些V$视图需要DBA权限才能访问,普通用户跑一条SELECT COUNT(*) FROM V$SESSIONS可能直接报ORA-00942: table or view does not exist这种类似的权限错误。这个exporter是包一层错误捕获的,不会因为某一条监控SQL执行失败就把整个进程搞崩,而是把 ошибка记到日志里,继续跑下一条查询。

如果SQL返回的结果集有空行,处理逻辑上会跳过这轮采集,指标不更新,保持上一次的数值。这在Grafana里会显示成一根横线,比采集异常导致断线要好排查一些。如果你想识别这种状态,可以在SQL上做文章:

SELECT COALESCE(COUNT(*), 0) FROM V$SESSIONS

3.4 指标命名规范和标签设计的固化逻辑

最终暴露出来的指标名,格式上是job_name + "_" + query_name拼接。比如Job名dm_system_stats、Query名dm_sessions_total,最终Prometheus看到的就是dm_system_stats_dm_sessions_total。如果job_name里本身带了连字符或者点号,Prometheus对指标名的字符集有严格限制(只允许[a-zA-Z0-9:_]),所以命名上建议全程用下划线。

标签方面,YAML里labels数组里写列名,不写列值。例如:

labels: ["TABLESPACE_NAME", "STATUS"]

就会产生{TABLESPACE_NAME="xxx", STATUS="ONLINE"}这样的标签集合。标签基数越大,Prometheus的存储膨胀就越快,监控表空间或者会话这类维度可以接受,但如果想把SQL文本(比如SQL_TEXT)当作标签塞进去,累积生成的时序数会非常惊人,这个务必要避开。

3.5 水印机制:gorun.sh在启动流程中的角色

gorun.sh是一个启动辅助脚本,从命名和行为来推断,它的作用是解决Go编译出的exporter二进制在后台运行时读取相对路径配置文件的问题。常见做法是这样:

#!/bin/bash BASE_DIR=$(cd "$(dirname "$0")" && pwd) cd "$BASE_DIR" || exit 1 if [ ! -f ./dm_job_config.yml ]; then echo "Missing dm_job_config.yml in $BASE_DIR" exit 1 fi nohup ./sql_zh_exporter -config ./dm_job_config.yml > exporter.log 2>&1 & echo $! > exporter.pid

这个脚本的含义是:无论你在哪个目录执行gorun.sh,都会切回到脚本所在的目录,找到配置文件和二进制,然后后台启动并把PID记录到exporter.pid方便后续管控。它对本地原生部署非常实用,配合kill $(cat exporter.pid)就能完成优雅的进程管理。

4. Docker容器化部署与原生部署两种落地路径

4.1 从DockerFile看镜像构建的要点

项目根目录有DockerFile(注意这里的大小写),结合.goreleaser.yaml可以推断出这个项目的标准镜像构建方式是:先用Golang基础镜像编译出静态二进制,再拷贝到精简运行镜像里。这么做的显著收益是最终镜像体积小,且不携带Go编译器,攻击面更小。

Docker部署的核心命令大致是:

docker build -t sql-zh-exporter:latest . docker run -d --name dm-exporter \ -p 9399:9399 \ -v /opt/monitor/dm_job_config.yml:/app/dm_job_config.yml:ro \ -e SCRAPE_INTERVAL=30s \ sql-zh-exporter:latest

命令中的参数解释:

  • -p 9399:9399:把容器内的监听端口映射出来,Prometheus从宿主机这个端口抓数据。如果你的网络环境里Prometheus和exporter跑在不同机器上,这个端口映射就是唯一的入口。
  • -v /opt/monitor/dm_job_config.yml:/app/dm_job_config.yml:ro:把宿主机上的配置文件挂载进容器,覆盖镜像内的默认配置。ro保证容器内不会篡改配置。
  • -e SCRAPE_INTERVAL=30s:环境变量方式的参数注入。如果config.go里实现了读取这个环境变量的逻辑,它会覆盖YAML里的对应字段,这是12-factor风格的配置管理方式。
  • 容器网络建议使用host模式,能让DSN里的127.0.0.1:5236直接命中宿主机上的达梦数据库,不用额外维护bridge网络。

4.2 Docker Compose的多服务编排

如果你的监控体系里还同时挂着Prometheus和Grafana,用docker-compose.yaml来一键拉起整个监控栈会舒服得多:

services: dm-exporter: image: sql-zh-exporter:latest container_name: dm-exporter network_mode: host volumes: - ./dm_job_config.yml:/app/dm_job_config.yml:ro restart: always prometheus: image: prom/prometheus:latest container_name: prometheus ports: - "9090:9090" volumes: - ./prometheus.yml:/etc/prometheus/prometheus.yml:ro command: - "--config.file=/etc/prometheus/prometheus.yml" - "--storage.tsdb.retention.time=15d" restart: always

Prometheus侧要能抓到指标,prometheus.yml里的scrape_configs需要加上:

scrape_configs: - job_name: 'dm-database' static_configs: - targets: ['127.0.0.1:9399'] scrape_interval: 30s

scrape_interval与exporter内部的采集频率相互独立,建议Prometheus的拉取间隔略大于或等于exporter的采集间隔。如果你配置exporter每60秒查一次数据库,Prometheus却每15秒拉一次,那么中间几次拉取拿到的都是上一次缓存的旧值,造成指标曲线呈阶梯状,这不算故障,但看着非常困惑。

4.3 原生.zip部署的步骤与坑位

开发环境或内网服务器上没有Docker的情况下,项目提供的原生.zip安装包就派上用场了。解压后的目录结构大约是:

sql_zh_exporter/ ├── sql_zh_exporter # 编译好的二进制 ├── dm_job_config.yml # 达梦配置 ├── kingbase_job_config.yml # 金仓配置 ├── job_config.yml # 通用配置 └── gorun.sh

启动前需要确认几件事,都是实际部署中容易踩的坑:

第一,二进制是否有执行权限。从Windows下解压出来的文件到Linux服务器上,默认权限可能是0644,需要手动加上:

chmod +x sql_zh_exporter gorun.sh

第二,DSN里的连接信息是否对。很多人直接拿示例配置里默认的SYSDBA密码去连生产库,结果报权限或登录失败。连达梦的系统管理员账户和普通业务账户属于两套体系,建议单独创建专用监控账户:

达梦侧(在disql里执行):

CREATE USER MONITOR IDENTIFIED BY "Monitor@123"; GRANT SELECT ON V$SESSIONS TO MONITOR; GRANT SELECT ON V$LOCK TO MONITOR; GRANT SELECT ON DBA_TABLESPACE_USAGE_MONITOR TO MONITOR;

金仓侧(在ksql里执行):

CREATE USER monitor WITH PASSWORD 'Monitor@123'; GRANT CONNECT TO monitor; GRANT SELECT ON pg_stat_activity TO monitor; GRANT SELECT ON pg_stat_database TO monitor;

第三,网络连通性。exporter所在机器能不能访问数据库端口,达梦默认监听5236,金仓默认监听543215433,被防火墙挡住的概率非常大。验证连通性可以先用telnet 127.0.0.1 5236这种基础命令探一下端口。

4.4 原生部署与容器化部署的选型

容器化部署在配置管理上更规范化,镜像内包含了二进制和依赖,只需要通过挂载覆盖配置文件就能完成配置更新,回滚也只是回退镜像版本。原生部署的好处是省去了镜像仓库的维护成本,在等保、密评这类严格受限的内网环境里反而更容易通过合规审核。

两者在监控指标暴露的逻辑上完全一致。容器只是进程隔离的一种形式,/metrics端点的行为不受影响。真正影响选型的变量只有一个:你的运维体系里有没有镜像仓库和容器编排平台。

从维护角度看更推荐Docker,因为升级exporter版本时的回滚路径很干净:docker rm -f dm-exporter && docker run ...就完成了。原生部署需要手动覆盖二进制,并且要记住重启进程,操作步骤多一点,但管理和排错的门槛低。都行,关键是团队内哪个更熟。

4.5config.yml里的隐藏配置项

项目里还有一份config.yml(没有_job后缀),它在整个配置层级中的位置偏高。里面更可能存储的是exporter自身运行时的参数,比如监听端口、日志级别之类的。启动时它会作为主配置,通过某种方式引入或关联到dm_job_config.ymlkingbase_job_config.yml

server: port: 9399 log_level: info # 关联的Job配置文件,支持多份 job_config_files: - ./dm_job_config.yml - ./kingbase_job_config.yml

如果你启动时只传了一个-config参数,大概率传的是config.yml,然后程序内部再自行加载job_config_files列表里指向的文件。这样一份主配置能同时管理多套Job配置,比你启动两个进程分别监控达梦和金仓要方便得多。在验证部署是否成功时,这点要格外留意,配置文件不要传错层级。

5. 生产环境验证指标与自定义监控SQL的落地实践

5.1 如何验证exporter暴露的指标正确

exporter启动后,第一步不是急着接Prometheus,而是先直接请求一次暴露端点,确认数据形态没问题:

curl -s http://127.0.0.1:9399/metrics | grep -E "^dm_|^kingbase_" | head -30

返回内容里应该能看到类似这样的格式:

# HELP dm_system_stats_dm_sessions_total 当前会话总数 # TYPE dm_system_stats_dm_sessions_total gauge dm_system_stats_dm_sessions_total 5.0

如果这个输出为空,大概率是采集失败或配置里的Job没启用;如果输出里只有HELP没有数值,说明SQL返回空结果集或连接异常。接下来看exporter自身的日志:

tail -20 /opt/monitor/exporter.log

日志里若出现connection refusedinvalid username or password这类信息,先排查DSN和网络,不要盲目改SQL。

5.2 慢SQL和有状态视图的监控SQL实战

用这套exporter监控达梦会话中最耗资源的SQL,可以写这样的查询配置:

- name: dm_slow_sql_detail help: 慢SQL详细信息 interval: 30s sql: | SELECT SESS_ID, SQL_TEXT, RUN_TIME FROM V$SQL_HISTORY WHERE RUN_TIME > 1000 AND ROWNUM <= 20 metrics: - type: gauge value_column: RUN_TIME labels: ["SESS_ID", "SQL_TEXT"]

这段配置的价值在黑屏排查时尤为明显:你可以直接在Prometheus里按SQL_TEXT维度看到哪条SQL的运行时间超过了1000毫秒,Grafana上再叠加一个状态面板展示最近15分钟是否有慢SQL出现,省掉了每次连数据库执行SQL的功夫。

但注意SQL_TEXT直接作为高基数的标签会让时序库膨胀,生产上更合理的做法是只保留SESS_ID作为标签,SQL_TEXT放到日志里由日志系统分析。

人大金仓侧,慢SQL通常看pg_stat_statements或者pg_stat_activity

- name: kingbase_active_queries help: 当前活跃查询和运行时长 interval: 30s sql: | SELECT pid, usename, state, EXTRACT(EPOCH FROM (now() - query_start)) AS duration_seconds FROM pg_stat_activity WHERE state = 'active' AND query NOT ILIKE '%pg_stat_%' metrics: - type: gauge value_column: duration_seconds labels: ["pid", "usename", "state"]

EXTRACT(EPOCH FROM ...)把时间间隔转成秒数,是为了让Prometheus的数值型和时间类函数可以正常处理。如果直接导出interval类型,Prometheus里没法做比较运算,这是实战中容易踩的典型坑。

5.3 告警规则配置速查

接好Prometheus之后,告警规则是监控价值的最终体现。建议配两条最基础的规则,验证exporter和Prometheus告警链路的连通性:

groups: - name: dm_database_alerts rules: - alert: DMSessionsHigh expr: dm_system_stats_dm_sessions_total > 200 for: 5m labels: severity: warning annotations: summary: "达梦会话数超过200,当前值{{ $value }}" - alert: DMTablespaceExhausted expr: dm_tablespace_usage_dm_tablespace_bytes < 107374182400 for: 10m labels: severity: critical annotations: summary: "达梦表空间剩余不足100GB,当前剩余{{ $value }}字节"

for: 5m的含义是持续5分钟都满足才触发,避免瞬时抖动造成告警轰炸。表空间的阈值不要直接用死数,不同业务库的容量规划差异极大,前期可以先观察两周的曲线趋势再定。

5.4 采集频率和连接池参数的调优

采集频率对整个数据库性能的影响比很多人想象中大。默认30秒一次、每个Job最多5个连接,对一个混合负载的达梦库来说还算温和。但如果某个Job的SQL本身要跑几秒才能返回(比如某些集群视图查询),那max_open_conns开太大会瞬间占满连接池,挤压业务线程。起步建议max_open_conns=2,观察数据库的等待事件有没有上升再逐步加。

conn_max_lifetime尤其建议显式设置,达梦对长时间空闲的连接可能踢掉,不设这个参数会出现奇怪的连接断断续续异常。设置成15到30分钟,配合连接池自动重建,能显著减少偶发性的采集失败。

5.5 把导出器的指标接入Grafana做国产数据库看板

最后一步是到Grafana里搭一个最基础的面板。数据源指向Prometheus,查询语句用promql

rate(dm_system_stats_dm_sessions_total[5m])

展示的是每秒会话变化速率。表空间使用率面板则用:

dm_tablespace_usage_dm_tablespace_bytes

配合Grafana的$__interval变量和legend里设置{{TABLESPACE_NAME}},就可以在图例上区分不同表空间。整个面板组做完,国产数据库的监控就和MySQL、PostgreSQL在同一个界面里统一管理了。这套体系搭完之后,新接入一台数据库的成本就只是多写一份YAML配置,其余全部复用。

本文还有配套的精品资源,点击获取

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/12 4:27:16

ProtocolBuffer核心特性与高效数据交换实践

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/12 4:25:39

51单片机篮球计分器Proteus仿真与实物制作全攻略

简介&#xff1a;面向单片机学习者的51单片机篮球计分器课程设计资源&#xff0c;涵盖24秒倒计时、两队比分数码管显示和矩阵键盘加1、2、3分等核心功能&#xff0c;适合电子设计作业或练手。压缩包共34个文件&#xff0c;约827KB&#xff0c;含C源码、汇编程序、Keil工程、Pro…

作者头像 李华
网站建设 2026/9/12 4:22:38

lw.PPOCR.C:纯C实现的Java原生OCR运行时

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华