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 会话——只出结论与建议命令,变更由用户自己执行。

الانتقال إلى التثبيت

معلومات المصدر

المستودع
wgzhao/mysqlbot
آخر نشاط في المصدر
١٧ سبتمبر ٢٠٢٦ في ٠١:٢٤
لغة SKILL.md المكتشفة
الصينية
النجوم
٠
التفرعات
٠

خيارات التثبيت

يُحدَّد Prompt الذي يراجع المصدر أولًا بشكل افتراضي. يمكنك التبديل إلى أمر مباشر أو تنزيل نسخة محلية.

مراجعة ملفات المصدر

اقرأ SKILL.md وأي ملفات مرافقة يعرضها SkillsMP قبل أن تقرر التثبيت.

عرض SKILL.md

SKILL.md
تعليمات المصدر · معاينة للقراءة فقط
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(跨版本+对账) ```
عرض على GitHub