OneUptime SQL 监控(SQL Query Monitor)实战指南:只读查询探测、多层安全防线与告警标准配置
【免费下载链接】oneuptimeComplete open-source monitoring and observability platform.项目地址: https://gitcode.com/GitHub_Trending/on/oneuptime
本篇技术指南围绕 OneUptime 的 SQL 查询监控功能展开,讲解它如何从探针(Probe)按计划执行只读 SQL 查询、依据结果(行数、标量值、执行耗时、查询错误)触发告警的完整机制,覆盖支持的数据引擎、安全模型、配置项、查询编写规范、Monitor Secret 密码保护与告警标准(Criteria)搭建。读完本文,你将掌握在 OneUptime 中安全落地「跑一条查询、必要时开一个 Incident」这一典型场景的完整实操方案,并理解其探针侧实现的底层原理。
功能定位:为「执行查询并触发告警」而生
SQL 监控(SQL Query Monitor)解决的是这样一个经典可观测性需求:在业务数据库上按固定计划执行一条只读 SQL 查询,并根据查询结果判断系统是否健康。典型用例包括:
- 最近五分钟内订单取消数量突然飙升;
- 消息队列表堆积过大;
- 某条关键业务记录意外消失。
由于查询是在探针(Probe)上执行的,而探针部署在你的网络内部,OneUptime 云端永远不需要直连你的数据库。更关键的是,完整的结果集永远不会离开探针——探针只回传一个小的、有边界的投影(projection)结果。
支持的数据库引擎
SQL 监控官方支持以下三种引擎(默认端口与仓库中 SqlDatabaseType.ts 的getDefaultPort()实现一致):
| 引擎 | 默认端口 |
|---|---|
| PostgreSQL | 5432 |
| MySQL | 3306 |
| Microsoft SQL Server | 1433 |
官方测试覆盖仅限上述三种。使用相同网络协议与 SQL 方言的 MySQL 兼容、PostgreSQL 兼容引擎通常也能工作,但不在官方测试范围之内。从源码看,探针分别使用pg、mysql2/promise、mssql三个纯 JS 驱动连接对应引擎(见 SqlMonitor.ts)。
工作原理:探针回传的紧凑投影
每次检查时,探针完成以下动作:连接数据库 → 在只读上下文中执行你的查询 → 最多读取有界行数 → 向 OneUptime 回传一个紧凑投影。随后,你配置的监控标准(Criteria)针对该投影进行求值。
探针只回传五类数据:
- 行数(Row Count)——查询返回的行数(受「最大行数」参数约束);
- 标量值(Scalar Value)——第一行第一列的值,这是
SELECT COUNT(*)类查询最自然的取值; - 首行(First Row)——第一行以「列/值」键值对形式呈现,用于在检查摘要中提供上下文;
- 执行耗时(Execution Time)——查询执行耗时,单位毫秒;
- 查询错误(Query Error)——查询失败时经过净化的错误消息。
完整结果集永远不会发送到 OneUptime,因此客户数据不会被复制进 OneUptime 的存储。这一点在源码中有直接印证:shapeRows()方法只从结果中抽取rowCount、第一列的scalarValue和firstRow,并在行数超过上限时置isRowsCapped标志(见 SqlMonitor.ts)。
安全模型:多层防线保障生产库安全
在生产数据库上执行一条由客户提供的 SQL 属于敏感操作,因此 SQL 监控在设计上就是只读的,并叠加了多层控制:
1. 最小权限数据库用户(首要防线)始终使用一个专用的、只读的数据库用户连接,该用户仅能访问查询所需的表。这是最重要的一层控制,具体创建方法见下文「创建只读数据库用户」。
2. 只读执行(执行期强制)
- 在PostgreSQL和MySQL上,探针会开启
READ ONLY事务,无论查询文本如何,任何写入(包括可写 CTE)都会被拒绝; - Microsoft SQL Server没有只读事务,探针改为在**始终回滚(rollback)**的事务中执行查询。
源码印证:PostgreSQL 执行器会执行START TRANSACTION READ ONLY并配合SET LOCAL statement_timeout(见 SqlMonitor.ts);MySQL 执行器同样执行START TRANSACTION READ ONLY(见 SqlMonitor.ts);SQL Server 执行器则始终在finally中回滚事务(见 SqlMonitor.ts)。
3. 单语句 + 白名单首关键字查询必须是单条语句,且以SELECT、WITH、VALUES、TABLE之一开头。堆叠语句(如SELECT 1; DROP TABLE …)以及写入/DDL 会在探针连接数据库之前被拒绝。校验是**注释感知(comment-aware)和字符串字面量感知(string-literal-aware)**的,藏在注释或字符串里的关键字不会蒙混过关。
源码印证:SqlQueryValidator.getRejectionReason()会先剥离块注释/行注释(stripComments)和字符串字面量(stripStringLiterals),再检查分号、首 token 白名单(ALLOWED_FIRST_TOKENS)以及禁用关键字列表FORBIDDEN_CONSTRUCTS(覆盖insert/update/delete/merge/truncate/drop/alter/...、远程数据源访问openquery/openrowset/...、系统存储过程xp_/sp_等),见 SqlMonitor.ts。测试用例覆盖了「注释中隐藏写入关键字」「字符串中的分号不误判」「无分号的 T-SQL 第二条语句」等绕过手法(见 SqlMonitor.test.ts)。
4. 语句超时每条查询都有硬性时间上限,执行过久的查询会被取消。源码中 PostgreSQL 同时设置了服务端statement_timeout与客户端query_timeout兜底(statementTimeoutInMs + 2000),MySQL/SQL Server 则用withHardTimeout()做客户端硬超时竞速(见 SqlMonitor.ts)。
5. 有界行数探针最多读取「最大行数 + 1」行(多出的 1 行用于检测截断),从而限制探针内存占用与回传载荷大小。PostgreSQL 用游标FETCH FORWARD maxRows+1(SqlMonitor.ts)、MySQL 用流式读取到上限即销毁连接(SqlMonitor.ts)、SQL Server 用SET ROWCOUNT maxRows+1(SqlMonitor.ts)。
6. 凭据脱敏数据库错误在存储前会被净化——密码以及任何连接字符串都会被脱敏,凭据不会泄漏到错误消息中。源码中sanitizeError()会替换密码子串、其余 secret 型连接字段、postgres:///mysql://等连接 URI,以及password=形式的连接串键值对(见 SqlMonitor.ts),并有对应单测验证(SqlMonitor.test.ts)。
前置条件
- 一台探针(Probe),且对数据库主机和端口具备网络可达性。数据库可从公网访问时可用 OneUptime 托管探针;运行在你网络内部时使用自托管探针。
- 一个只读数据库用户及连接信息(主机、端口、数据库名、用户名、密码)。
配置 SQL 监控
新建一个监控器,选择SQL Query(SQL 查询)作为监控类型,然后填写连接信息:
- 数据库类型(Database Type)——PostgreSQL、MySQL 或 Microsoft SQL Server;选择类型会联动填充默认端口;
- 主机(Host)——探针可达的数据库主机(例如
db.internal); - 端口(Port)——数据库端口;
- 数据库名(Database Name)——执行查询的目标数据库;
- 用户名(Username)——只读、最小权限的数据库用户;
- 密码(Password)——强烈建议用 Monitor Secret 以
{{monitorSecrets.name}}形式引用,而非明文输入(见下文); - SQL 查询(SQL Query)——要执行的只读查询(见「编写查询」);
- 使用 SSL/TLS(Use SSL/TLS)——开启后通过 TLS 连接;若数据库使用自签名证书,可同时关闭验证服务器证书(Verify server certificate)。
高级选项
| 参数 | 默认值 | 最大值 | 说明 |
|---|---|---|---|
| 连接超时(ms) | 10000 | 30000 | 等待建立连接的时间 |
| 语句超时(ms) | 15000 | 60000 | 查询可运行时长的硬上限 |
| 最大行数(Max Rows) | 100 | 1000 | 从数据库回读行数的上限 |
这些上限在服务端/探针侧强制执行,用户无法在界面上调得更高——常量与钳位函数定义于 MonitorStepSqlMonitor.ts,execute()在执行前会对三个参数分别调用clampSqlStatementTimeoutInMs、clampSqlConnectionTimeoutInMs、clampSqlMaxRows做归一化(见 SqlMonitor.ts)。
补充:SQL Server Windows 集成身份验证
对于 Microsoft SQL Server,官方英文文档还支持Windows 集成身份验证(Windows Integrated Authentication)模式:以探针进程的运行身份建立受信连接,此模式下用户名与密码字段被忽略、不会传给驱动。由于探针需要一个被域信任的身份,此模式应使用自托管探针:
- Windows 探针:以拥有只读 SQL Server 登录的域账户运行探针服务;
- Linux / macOS 探针:为 SQL Server 所在域配置 Kerberos,并给探针进程提供有效票据(如通过 keytab)。官方 Linux 探针镜像内置 Microsoft ODBC Driver 18、unixODBC 与 Kerberos 客户端,需将 Kerberos 配置与票据缓存挂载进容器并保证探针进程可读,非默认路径时设置
KRB5_CONFIG或KRB5CCNAME。
驱动探测细节:探针宿主机需装有 Microsoft ODBC Driver。官方探针镜像内置ODBC Driver 18;自托管/自定义探针会自动探测宿主机上注册的最新ODBC Driver N for SQL Server(例如装的是 Driver 17 就用 17),无需严格匹配 18。如需固定某个版本,可设置探针环境变量SQL_SERVER_ODBC_DRIVER为精确驱动名。该逻辑实现于 SqlMonitor.ts(resolveSqlServerOdbcDriver的解析顺序为:环境变量覆盖 → 宿主机最新已注册驱动 → 镜像内置默认值)。另外还需满足:SQL Server 有合适的MSSQLSvc服务主体名(SPN)、探针与域控时钟同步、探针通过该 SPN 覆盖的主机名解析并访问 SQL Server。受信身份只需授予监控查询所需的数据库权限。
编写查询
查询必须是单条只读语句,以SELECT、WITH、VALUES、TABLE之一开头。允许结尾有一个分号;多条语句不允许。
由于查询每次检查都会执行,请保持查询廉价且有界:优先使用索引列和窄时间窗口。以下是三种引擎下「统计最近取消订单数」的等价写法:
-- 统计近期取消订单(PostgreSQL) SELECT COUNT(*) AS cancelled FROM orders WHERE status = 'CANCELLED' AND created_at > NOW() - INTERVAL '5 minutes';-- 同样的思路(MySQL) SELECT COUNT(*) AS cancelled FROM orders WHERE status = 'CANCELLED' AND created_at > NOW() - INTERVAL 5 MINUTE;-- 同样的思路(Microsoft SQL Server) SELECT COUNT(*) AS cancelled FROM orders WHERE status = 'CANCELLED' AND created_at > DATEADD(minute, -5, GETDATE());对COUNT(*)类查询有一个容易混淆的点:计数既可作为行数(Row Count)(此时恒为1,因为只返回一行),也可作为标量值(Scalar Value)(计数本身,取自第一列)。要针对「有多少」告警,应比较标量值。这也与shapeRows()的实现一致:标量值取第一行的第一列(见 SqlMonitor.ts)。
使用 Monitor Secret 保护密码
为避免数据库密码以明文存储在监控器上,可以创建 Monitor Secret 并在密码字段中引用它:
- 进入 OneUptime 仪表盘 →Monitors → Settings → Secrets → Create Monitor Secret;
- 创建一个 secret(例如
dbPassword),并授权该监控器访问它; - 在监控器的密码字段填入
{{monitorSecrets.dbPassword}}。
OneUptime 在服务端解析该 secret 之后,才会把配置下发给探针。OneUptime 永远不会替你创建这些 secret——是否引用由你决定。从源码看,MonitorStepSqlMonitor的敏感字段(密码,以及其他任意字段)都可包含{{monitorSecrets.name}}引用(见 MonitorStepSqlMonitor.ts),而sanitizeError()之所以脱敏用户名/主机/库名,正是因为它们同样可能由 secret 支撑。
配置告警标准(Criteria)
添加标准来决定监控器何时被视为在线(Online)、降级(Degraded)或离线(Offline)。SQL 监控支持以下检查项:
- SQL Is Online——数据库是否可达且查询是否成功;
- SQL Query Row Count——返回的行数,可与大于、小于、等于等操作符比较;
- SQL Query Scalar Value——第一行第一列的值;两侧均看似数值时按数值比较,否则按字符串比较;这是
COUNT(*)类查询应使用的检查项; - SQL Query Execution Time (in ms)——查询耗时,用于捕捉数据库变慢;
- SQL Query Error——查询错误消息;可在其为空/非空或匹配特定字符串时告警;
- JavaScript Expression——用自定义 JavaScript 表达式实现完全控制,参见 JavaScript Expressions。
示例:取消量突增时告警
沿用上面的查询:
- 降级(Degraded)标准——
SQL Query Scalar Value大于10; - 离线(Offline)标准——
SQL Query Scalar Value大于50,或SQL Is Online为false。
将告警标准关联到值班策略(On-Call Policy),即可在条件满足时通知到正确的人。
创建只读数据库用户
始终使用专用的只读用户连接数据库。以下是三种引擎的示例:
-- PostgreSQL CREATE USER oneuptime_ro WITH PASSWORD 'a-strong-password'; GRANT CONNECT ON DATABASE orders TO oneuptime_ro; GRANT USAGE ON SCHEMA public TO oneuptime_ro; GRANT SELECT ON ALL TABLES IN SCHEMA public TO oneuptime_ro; -- 覆盖未来新建的表: ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT SELECT ON TABLES TO oneuptime_ro;-- MySQL CREATE USER 'oneuptime_ro'@'%' IDENTIFIED BY 'a-strong-password'; GRANT SELECT ON orders.* TO 'oneuptime_ro'@'%'; FLUSH PRIVILEGES;-- Microsoft SQL Server CREATE LOGIN oneuptime_ro WITH PASSWORD = 'a-strong-password'; USE orders; CREATE USER oneuptime_ro FOR LOGIN oneuptime_ro; ALTER ROLE db_datareader ADD MEMBER oneuptime_ro;注意事项
- 查询每次检查都会执行,务必保持廉价:使用索引、窄时间窗口,并把语句超时作为最后兜底;
- 只回传行数、第一格(标量)与首行——设计查询时,把你要告警的值放在第一列;
- 若结果因超过最大行数被截断,检查摘要会标记为 capped(封顶)。仅在必要时调大最大行数,更大的结果集会占用更多探针内存;
- 写入与 DDL 永远被拒绝。如果你需要测试写入路径,这不是本监控器的用途;
- 优先使用 Monitor Secret 而非明文密码,确保凭据在静态时加密存储。
总而言之,OneUptime 的 SQL 查询监控通过「探针侧执行 + 只读事务 + 单语句白名单 + 超时与行数边界 + 凭据脱敏」的多层设计,把「对生产数据库执行客户查询」这一高风险操作约束在安全范围内,同时以紧凑投影的方式保证客户数据不出内网;配合行数、标量值、耗时、错误与 JavaScript 表达式等检查项,可以快速搭建覆盖数据库健康度与关键业务指标的可观测告警。
【免费下载链接】oneuptimeComplete open-source monitoring and observability platform.项目地址: https://gitcode.com/GitHub_Trending/on/oneuptime
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考