Skip to main content

mysqlbot

对 MySQL/MariaDB 实例做**只读、确定性、无 agent** 的健康体检,输出结构化隐患清单(风险/延迟/容量/卫生四个维度 42 条规则,已实测兼容 MySQL 5.7 / 8.0 / 8.4 / 9.7,并支持离线推演 9.x)。适用于"给 MySQL 做体检""查 MySQL 有什么隐患""库慢在哪""有没有长事务/锁等待/元数据锁""表结构有什么问题""没主键的表""冗余索引""binlog 会不会撑盘""准备上线前扫一遍库""这套规则在 9.x 上能不能跑"。当用户要求巡检 MySQL、排查数据库风险、或需要把数据库体检做成 CI 门禁/SARIF 报告时使用。不写库、不改配置、不 kill 会话——只出结论与建议命令,变更由用户自己执行。

Ir a la instalación

Datos de origen

Repositorio
wgzhao/mysqlbot
Última actividad en el origen
17 de septiembre de 2026 a las 01:24
Idioma detectado de SKILL.md
chino
Estrellas
0
Forks
0

Opciones de instalación

De forma predeterminada está seleccionado el prompt que primero revisa el origen. Puedes cambiar a un comando directo o descargar una copia local.

Revisa los archivos de origen

Lee SKILL.md y los archivos complementarios que muestra SkillsMP antes de decidir si quieres instalarlo.

Mostrando SKILL.md

SKILL.md
Instrucciones de origen · Vista previa de solo lectura
name
mysqlbot
description
对 MySQL/MariaDB 实例做**只读、确定性、无 agent** 的健康体检,输出结构化隐患清单(风险/延迟/容量/卫生四个维度 42 条规则,已实测兼容 MySQL 5.7 / 8.0 / 8.4 / 9.7,并支持离线推演 9.x)。适用于"给 MySQL 做体检""查 MySQL 有什么隐患""库慢在哪""有没有长事务/锁等待/元数据锁""表结构有什么问题""没主键的表""冗余索引""binlog 会不会撑盘""准备上线前扫一遍库""这套规则在 9.x 上能不能跑"。当用户要求巡检 MySQL、排查数据库风险、或需要把数据库体检做成 CI 门禁/SARIF 报告时使用。不写库、不改配置、不 kill 会话——只出结论与建议命令,变更由用户自己执行。
agent_created
true
# mysqlbot —— MySQL 只读体检探头 参考 pgbot(PostgreSQL 侧同类工具)的设计思路实现,代码在 `~/code/personal/mysqlbot`。 ## 铁律 1. **只读**。不改配置、不 kill 会话、不写任何表。rule 只有查询,工具链里没有写路径。 报告里给出的 `DROP INDEX` / `KILL` / `SET GLOBAL` 都只是**建议**,由用户本人执行。 2. **"跳过"不等于"没问题"**。能力位缺失、版本不符、实例运行时间不足时,规则会被 **显式跳过**并在报告里列出原因。汇报时必须把"未覆盖"那一栏一起讲出来,绝不能把 报告读成"全绿"。这是本工具最重要的输出纪律。 3. **每条结论都要能落到证据**。规则返回的每一行都是证据(对象名、数值、SQL 样本)。 不要用"看起来""可能是"来描述,直接给行。 4. **不碰生产写操作**。用户的线上变更自己执行;我们负责准备命令与定位问题。 ## 快速上手 ```bash cd ~/code/personal/mysqlbot # 0. 自检:确认客户端、规则目录、连通性、能力位,并把信号登记表与实例核对一遍 ./bin/mbot doctor --host <host> --port 3306 -u mbot_reader -p "$MYSQLBOT_PASSWORD" # 1. 体检(默认输出人读表格) ./bin/mbot check --host <host> -u mbot_reader -p "$MYSQLBOT_PASSWORD" # 2. 机器可消费的完整契约(含能力位、跳过原因与类型、每条命中的原始行) ./bin/mbot check --host <host> -u mbot_reader -p "$MYSQLBOT_PASSWORD" -o json --out-file report.json # 3. 生成可贴进工单/文档的 markdown,或给 CI 用的 SARIF ./bin/mbot check ... -o markdown --out-file report.md ./bin/mbot check ... -o sarif --out-file report.sarif # 4. 只关心某一类 ./bin/mbot check ... --dimension risk # 只看风险 ./bin/mbot check ... --only 'blocking_chains,metadata_lock_wait,idle_in_transaction' ./bin/mbot check ... --skip 'unused_index,redundant_index' ./bin/mbot check ... --min-severity warn # 只报 warn 以上 # 5. 只看能力位(判断账号给够了没有) ./bin/mbot probe --host <host> -u mbot_reader -p "$MYSQLBOT_PASSWORD" # 6. 列出/校验/生成规则文档 ./bin/mbot list ./bin/mbot lint ./bin/mbot docs --out-file docs/findings.md # 7. 离线推演:不连库,回答"这一版上哪些规则会跑" ./bin/mbot coverage --at 9.7 # 单个版本 ./bin/mbot coverage --matrix # 5.7/8.0/8.4/9.0/9.7 全版本网格 ``` **连接方式**:`--dsn mysql://user:pass@host:port/`、或 `--host/--port/-u/-p`、或 `--socket`、或 `--defaults-file ~/.my.cnf`(复用 login-path)。密码也可走 `MYSQLBOT_PASSWORD` / `MYSQL_PWD` 环境变量,避免进 shell 历史。 ⚠️ **`-p` 必须带值**(`-p '密码'`)。它是普通的 argparse 选项,不是 mysql 客户端那种 "留空则交互提示"的写法 —— 省略值会直接以 `expected one argument` 报错退出。 > 注意 `--defaults-file` 必须是命令行上的**第一个**参数——这是 mysql 客户端的硬要求, > 工具已按此构造命令,但你手工敲 mysql 时要注意。 **退出码**:`0` 无命中(达到 `--fail-on` 阈值以上)/ `1` 有命中 / `2` 连接或用法失败 / `3` 规则契约问题。默认 `--fail-on warn`,CI 里可直接当门禁用。 > `doctor` 里有一行 `信号登记表`:它把 `mbot/signals.py` 里登记的 21 个**变量**信号 > 逐条与实例核对存在性。**这一行为 ✗ 说明工具自带的版本知识过期了**,推演结论不再可信, > 且会让 `doctor` 返回失败。不是数据库的问题,是工具要更新。 ## 怎么读输出 ``` 🟡 WARN 命中 2 干净 26 跳过 12 失败 0 (共 42 条规则) ``` 四个数要分别汇报,其中 **跳过**最容易被忽略: - **命中**:规则返回了行,每行都是证据。 - **干净**:规则跑通了且返回 0 行 —— 这才叫"检查过、没问题"。 - **跳过**:**没检查**。原因会逐条列出,并且带一个机器可读的 `skip_kind`: - `capability` 缺能力位(账号权限不够,如 `缺少能力位 process`) - `uptime` 实例运行时间不足(累计型计数器此刻无意义) - `version` 版本门禁(`需要 MySQL >= 8.0.13` / `MySQL >= 8.0 已移除该信号源`),设计如此 - `permission` 运行时被拒(1142 等) - `missing_object` 运行时对象不存在 —— **这个要警惕**:如果对象是因为版本才有/没有的, 说明规则的版本声明漏了,应补进 `mbot/signals.py` 的信号表 - **失败**:规则执行报错。**这属于工具缺陷,要报上来**——它专指"SQL 与目标版本不匹配" (引用了目标版本不存在的列/变量),也就是规则的 `@since`/`@removed_in` 门禁漏了。 工具**不会**把它降级成"跳过",因为那样规则坏掉时会伪装成"环境限制"混过去。 所以:**看到 `跳过` 想的是"该给权限"或"版本不适用",看到 `失败` 想的是"工具要修"。** 另外两点读报告时必须注意: - **行数可能是下限**。规则带 `LIMIT` 防刷屏,命中数达到上限时报告会把它标成 `≥N` (JSON 里是 `truncated: true`)。**不要把"N 行"当全量读。** - 结构类规则(无主键表、冗余索引、超大表…)**只覆盖账号有 SELECT 权限的库**。 报告里的 `capabilities.visible_schemas` 就是实际覆盖范围,没有全局 SELECT 时还会附一条 note。**汇报时必须把它一起讲出来**——否则用户会把"没报无主键表"读成"整个实例都没有"。 ## 规则库概览 42 条,分四个维度,完整目录见 `docs/findings.md`(由 `mbot docs` 从规则头部生成)。 | 维度 | 条数 | 覆盖内容 | |---|---|---| | `risk` | 16 | 长事务、空闲事务、行锁等待链(8.0+ 与 5.7 各一条)、元数据锁等待、undo 堆积、复制中断、从库可写、无主键表、非 InnoDB 表、binlog 关闭、提交不落盘 | | `latency` | 12 | 缓冲池命中率、redo 容量与等待、临时表落盘、排序归并、表缓存/线程缓存未命中、全表扫描语句、累计最耗时语句 | | `capacity` | 7 | 超大表、连接数余量、表缓存水位、redo 容量、binlog 保留策略(8.0+ 与 5.7 各一条) | | `hygiene` | 7 | 冗余索引、未使用索引、索引统计失真、慢日志、统计信息缓存、强制主键 | 作用域(`schema` / `workload` / `instance` / `cluster` / `history`)与维度正交。 **有两对规则是"同一条发现的两个版本变体"**:`blocking_chains` / `blocking_chains_57`、 `binlog_retention_unbounded` / `binlog_retention_unbounded_57`。这些信号在 5.7 与 8.0+ 用了两套完全不同的表达,单条 SQL 无法通吃;两条用 `@since`/`@removed_in` 互斥, 同一实例上只会启用其中一条。所以**规则总数是 42,但单次巡检最多启用 40 条**。 变体关系现在用 `@variant_of` **显式声明**(不再是命名巧合),`lint` 会断言 "每个逻辑规则的变体集合无缝覆盖 `[5.7, +∞)`"——没有空洞、没有重叠。 **覆盖空洞的后果是规则静静地不跑**,报告里连"跳过"都不会出现。 每条规则头部都带 `@remediation`(怎么修)、`@caveats`(什么情况下会误报)。 **汇报前先读 `@caveats`**,它写明了每条结论的可靠边界。 规则即纯 SQL,可以脱离工具单独跑:**返回 0 行 = 未命中,返回行 = 命中,每行必须含 `severity` 列**。所以用户完全可以在 DBeaver 里挑一条规则手工验证工具的结论。 ## 版本兼容(被问到时怎么答) **结论:42 条里 38 条天然跨 5.7~9.x 通用,真正需要版本变体的只有 2 条。** 所以没有按大版本切目录——那解决不了问题,反而会让覆盖空洞变得不可见。 三种机制各管一段,问起来可以这样解释: 1. **`@since` / `@removed_in`** —— 整条规则的可用区间(声明式门禁) 2. **软查表 vs 硬引用** —— 同一个信号两种用法,后果完全不同: `@@binlog_expire_logs_auto_purge` 缺了会让整条规则报 1193;改成 `MAX(CASE WHEN VARIABLE_NAME='...') FROM performance_schema.global_variables` 则只是那次查询返回 NULL,规则自动退化。**8.4 移除的三个变量全靠这个写法兜住**, 所以 8.4/9.x 不需要改一行 SQL。 3. **`mbot/signals.py` 信号登记表** —— `lint` 用它校验"声明的区间"与 "正文实际引用的信号"是否矛盾;`coverage` 用它离线推演目标版本。 **它能回答单台实例回答不了的问题**:`@since: 5.7` 但正文要 5.7.3 才有的 `metadata_locks`——我们手上的 5.7 都是 5.7.44,永远跑不出问题来。 被问到"9.x 支持吗",正确回答是: ```bash ./bin/mbot coverage --at 9.7 # 先看有没有「已知」版本风险 ``` **9.7 已经从"推演"升级为"实测"**(2026-09-16,本机 MySQL 9.7.2 一次性实例): | 项 | 结果 | |---|---| | 端到端回归(种 17 处违规) | 42 条 · 命中 19 · 干净 21 · 跳过 2 · **失败 0**;17 条硬断言全中 | | 推演对账 | 门禁 预测 2 / 实测 2 ✓;版本风险 预测 0 / 实测失败 0 ✓ | | 与 8.4.11 对比 | **逐项相同**,而且规则一行没改 | 其中最值得说的一点:9.1 重新设计了 `performance_schema.data_locks` / `data_lock_waits` (官方理由是降低高并发 mutex 争用),实测**列集未变**,`blocking_chains` 照常命中。 但这类"同名而语义可能变了"的风险登记表**永远看不出来**,只能靠真实实例—— 所以加新版本时"上真实实例"那一步是权威步骤。完整论述与五步流程见 `docs/versioning.md`。 `9.0` 那一列仍是推演值(非 LTS,没有实例)。 ## 账号授权 见 `sql/readonly_account.sql`,两档(权限到规则的完整对照见 `docs/compat-matrix.md`): - **A 档(零数据访问)**:`PROCESS` + `REPLICATION CLIENT` + `SELECT ON performance_schema.*` + `SELECT ON sys.*`。覆盖 42 条里的 **36 条**, 一行业务数据都读不到。看不到业务表结构与容量,因此结构类规则会被**显式跳过**。 - **B 档(完整覆盖)**:A 档 + 对业务 schema 的 `SELECT`。这样才能看到表结构/索引/容量。 代价是"读数据"的权限也一并给出——这是 MySQL 的固有限制(没有 `pg_monitor` 那种 "能看元信息但碰不到数据"的角色)。 ⚠️ **最容易配错的一点**:以为"给了 `PROCESS` 就能读 performance_schema"。实测不行—— `performance_schema.threads` / `data_locks` / `metadata_locks` / `events_statements_summary_by_digest` / `table_io_waits_summary_by_index_usage` / `replication_*` 在只有 `PROCESS` 的账号下**全部返回 1142**(只有 `global_status` 与 `global_variables` 是默认开放的)。少了 `SELECT ON performance_schema.*`, `blocking_chains` / `metadata_lock_wait` / `full_table_scan_heavy` / `statement_high_total_latency` / `unused_index` 这 5 条会整条被跳过。 授权脚本里写了两者的取舍与验证查询,用之前先读一遍。 ## 已知边界(汇报时要如实说明) - **已在 Percona Server 5.7.44、Percona Server 8.0.43、MySQL 8.4.11、MySQL 9.7.2 上跑过完整巡检,均为 0 条规则执行失败,且离线推演与实测完全吻合。** 逐条结果、版本敏感信号对照表与权限依赖见 `docs/compat-matrix.md` ——**换新实例前先看一眼那张信号表**。 - **`9.0` 那一列仍是推演值**(非 LTS,且没有 9.0 实例)。`9.7.2` 已实测通过。 - **登记表只覆盖它登记过的东西**:新出现的信号会被 `lint` 提示,但同名而语义变了 的信号不能。兜底有两道:`live_compat.sh` 的 `missing_object` 检查(覆盖硬引用), 以及 `doctor` 的变量信号核对(覆盖软查表那种"哑失败")。 - **版本维度仍有空白**:8.4 只测过 8.4.11、5.7 只测过 5.7.44、9.x 只测过 9.7.2 一个点。 `@since: 5.7` 表示"已在 5.7.44 上实测执行通过";`@since: 5.7.3` / `5.7.8` 这类 补丁级下界来自官方文档与单测,没有那些版本的实例。 - **5.7 那一轮是用 `root` 跑的**,验证的是"SQL 与版本是否兼容", 不是"最小权限账号下能跑几条";权限维度的实测结论来自 8.0.43 的业务账号。 - **两条锁等待规则未在真实争用下比对过**(`blocking_chains` 走 8.0 的 `data_lock_waits`、 `blocking_chains_57` 走 5.7 的 `INNODB_LOCK_WAITS`),只验证了"无争用返回 0 行" 以及在一次性实例的**植入**争用场景下各自命中。 - **复制类规则未在真实从库上测**(四个实例都不是从库)。 - **不依赖 `sys` 的格式化视图**。实测结论:`sys` 视图是 `SQL SECURITY INVOKER`, 且其函数(`format_time` 等)`DEFINER=mysql.sys` 只有 `USAGE` —— 所以最小权限账号 查 `sys.statement_analysis` 会拿到 `ERROR 1356`,`GRANT SELECT ON sys.*` 并不够用。 本工具的做法是核心发现一律直读 `performance_schema`,只保留两个经实测可读的 `sys` 视图依赖(`schema_redundant_indexes` / `schema_unused_indexes`)。 - **没有 `advise` 类能力**。pgbot 能用 hypopg 造假设索引、让规划器确认成本下降才敢建议; MySQL/MariaDB 都没有假设索引,所以本工具**不给"加了这个索引会快多少"的承诺**, 只指出"这条语句没走索引、扫了 N 行"。 - **采样类结论天生不可靠**。`unused_index` 需要实例运行满 3 天才可信,`sampled` 精确度 的规则要配合业务周期(月末、季末)复核。 ## 自测(改了规则一定要跑) ```bash # ① 单测(不连库,秒级) tests/unit.sh # = test_grants + test_classify + test_signals # ② 端到端回归:种违规 → 只读账号巡检 → 硬断言 tests/local_instance.sh start # /tmp 下起一个一次性 MySQL 实例 tests/run_tests.sh tests/local_instance.sh stop # 本机装了多个版本时,指定要测哪一个(数据目录与端口也要隔离,避免撞车) MYSQLD_BIN=/opt/homebrew/Cellar/mysql@9.7/9.7.2_1/bin/mysqld \ MYSQLBOT_TEST_HOME=/tmp/mysqlbot-test97 MYSQLBOT_TEST_PORT=13307 \ tests/local_instance.sh start MYSQLBOT_TEST_HOME=/tmp/mysqlbot-test97 tests/run_tests.sh MYSQLBOT_TEST_HOME=/tmp/mysqlbot-test97 tests/local_instance.sh stop # ③ 跨版本兼容性:对任意真实实例断言"42 条规则全部执行成功、0 条报错" # 并做「离线推演 ↔ 实测」对账,登记表过期会直接报出来 tests/live_compat.sh --defaults-file ~/.my.cnf --label '5.7.44' ``` ②做三件事:断言**没有任何规则执行报错**、断言 17 条植入的违规**必须被抓到**、 把跳过项摊开列出。`tests/expected_hits_soft.txt` 里还备案了哪些规则在一次性实例上 **无法确定性复现**及原因。 ②有个盲区:它跑在**本机默认那个 mysqld** 上,所以只能抓到那一个版本的 SQL 不兼容。 其余版本的问题(比如某个列是 8.0.22+ 才有的)只有连到对应版本的真实实例、 或用 `MYSQLD_BIN` 另起一个才现形——补这块的是③。 ③只发 SELECT,可以直接对生产实例跑;**改完规则后至少要在目标版本上跑一次③**。 ③还会把离线推演与实测结果对账,不一致就报错——**这是让现实纠正信号登记表的机制**。 ①里三个单测守的都是**"不报错但结论错"**的类别,端到端抓不到: `test_grants.py` 管"能力位算错但不报错"(规则照样命中、报告照样出,只是门禁失效了); `test_classify.py` 管"把工具缺陷降级成环境限制"(规则坏掉时会伪装成"跳过"); `test_signals.py` 管"版本推演给出合理但错误的答案"(声明过宽、变体覆盖有空洞、 部分版本号被当成 .0 从而比现实悲观、**软查表的哑失败**)。 规则库的改动流程:改 `rules/*.sql` → `./bin/mbot lint` → `tests/unit.sh` → `tests/run_tests.sh` → `tests/live_compat.sh`(在有目标版本实例时)→ `./bin/mbot docs --out-file docs/findings.md`。 > 注意:`tests/local_instance.sh` 起的实例是**后台进程**,随父 shell 退出会被回收。 > 要 `start && run_tests` 写在同一条命令里。 ## 目录结构 ``` mysqlbot/ ├── SKILL.md 本文件 ├── README.md 仓库说明(**英文**,面向公开仓库;结构对齐 pgbot) ├── README_CN.md 同上中文版(两份内容一一对应,改一份要同步另一份) ├── LICENSE MIT 许可证 ├── bin/mbot 启动器 ├── mbot/ 实现(rule 解析 / signals / conn / probe / runner / report / cli) ├── rules/*.sql 42 条规则(核心资产) ├── sql/readonly_account.sql 只读账号授权脚本(按实测权限模型写) ├── docs/findings.md 规则目录(自动生成) ├── docs/design.md 设计决策与踩过的坑(20 条) ├── docs/versioning.md 版本兼容架构(信号登记表 / 离线推演 / 为什么不切目录) ├── docs/compat-matrix.md 版本验证矩阵(5.7/8.0/8.4/9.7 结果、版本敏感信号对照表、权限-规则对照) └── tests/ unit.sh(单测)+ run_tests.sh(端到端)+ live_compat.sh(跨版本+对账) ```
Ver en GitHub