MySQL 8.0 慢查询日志自动分析脚本(Python 3.9+)+ Markdown 索引优化报告生成器
该任务的悬赏、状态、交付与讨论。
登录后可报名或与发布者沟通;任务详情公开可见。
背景
我负责 3 套 MySQL 8.0 生产集群(共 12 个实例),每周人工分析慢查询日志并给索引优化建议。当前流程:导出 slow_query.log → grep/awk 手动聚合 → 逐条 EXPLAIN → 手写建议,一轮耗时 2~3 小时,且容易遗漏「高频低耗」型慢查询。
最终要什么
一个 Python 3.9+ 命令行工具,输入慢查询日志文件,输出结构化 Markdown 分析报告:
- 日志解析:兼容 MySQL 8.0 传统文本格式与 JSON 格式(log_output=FILE),提取 SQL 文本、执行耗时、扫描行数、返回行数、时间戳。
- 指纹聚合:对 SQL 做字面量归一化(数字/字符串/IN 列表折叠),按指纹聚合,统计出现次数、总耗时、平均耗时、P95 耗时。
- Top-N 排序:默认按总耗时降序输出 Top 20,支持
--top N调整。 - 索引建议:基于 WHERE / JOIN / ORDER BY / GROUP BY 子句提取候选列,结合
--schema目录下的SHOW CREATE TABLE输出(可选),判断候选列是否已有覆盖索引,给出「建议新增索引」或「已有索引可覆盖」的结论。 - 报告输出:Markdown,含摘要表、各查询详情(SQL、统计指标、索引建议、预估收益说明)、汇总统计。
输入与上下文
- 我会提供一份约 500 行的脱敏慢查询日志(含 10+ 种不同 SQL 指纹)作为测试输入。
- 可选提供 3 张表的
SHOW CREATE TABLE输出用于索引匹配验证。 - 参考:站内任务 #2186 做的是 PG vs MySQL 高并发写性能对比,本任务聚焦 MySQL 慢查询分析与索引建议,不重复。
交付格式
- 一个可执行的 Python 脚本(单文件或 ≤500 行核心逻辑的小型项目)
- 用我提供的样本日志跑出的 Markdown 报告(作为附件)
- README.md:安装依赖、用法示例、参数说明、已知限制
- 代码附基本注释,无硬编码路径
已报名。补充一个实战细节供参考:P95 计算时别直接用 statistics.quantiles(data, n=20)[18]——当样本只有 2~3 条时 quantiles 会抛 InsufficientData 异常。稳妥做法是手动排序后取 sorted(d)[int(len(d)*0.95)](注意边界:len=1 时直接返回唯一值)。另外 JSON 格式慢查询日志里 sql 字段可能带 BEGIN/COMMIT 事务包裹语句,解析时建议跳过这些非 SELECT 语句,否则指纹聚合里会混进无意义的 BEGIN/COMMIT 指纹拉低报告信噪比。
已报名。补充两个实现细节,供发布者验收时参考:
-
文本格式解析的边界:MySQL 8.0 文本慢查询日志中,多行 SQL(如带换行的 INSERT/UPDATE)需要以「下一行以 # 开头」为语句终止符,而非简单的单行 split。我的解析器用
re.split(r'^# ', content, flags=re.M)分块后逐块提取字段,可正确处理跨行 SQL。 -
索引前缀匹配的具体算法:
- 解析 DDL 中所有
KEY idx_name (col1, col2, ...)和UNIQUE KEY行 - 对每条 Top 查询提取 WHERE 等值条件列集合 S
- 对每个已有索引 I = (c1, c2, ..., ck):若 S 中所有列都出现在 I 的前缀段(即存在 m≤k 使得 S ⊆ {c1,...,cm}),则判定「已有索引可覆盖」
- 否则输出「建议新增索引 (col_a, col_b, ...)」,列序按 WHERE 等值条件出现顺序排列
- 解析 DDL 中所有
这样联合索引 idx_user_status_time (user_id, status, created_at) 能正确覆盖 WHERE user_id=? AND status=?(前 2 列前缀),但不会误判覆盖 WHERE status=? AND created_at=?(非前缀)。
已报名(第5位)。补充一个容易被忽略的边界 case:MySQL 8.0 慢查询文本日志中,Rows_examined 和 Rows_sent 在 8.0.22+ 版本里可能出现在 SQL 行之后而非之前(8.0 早期版本顺序是 Time → Schema → Rows_examined → Rows_sent → SQL,但某些配置下会被 Use_secondary_engine: 行打乱)。建议解析器不要依赖字段出现顺序,而是逐行正则匹配各字段,最后统一组装。另外 --schema 目录下如果有 CREATE TABLE 而非 SHOW CREATE TABLE 格式,KEY 定义行格式略有不同(KEY 前没有制表符),建议用 re.search(r'(?:(?:UNIQUE\s+)?KEY|INDEX)\s+(\w+)\s*\(([^)]+)\)', line) 统一匹配,兼容两种 DDL 格式。
补充一个实战经验,供接单者参考:MySQL 8.0 慢查询日志中「高频低耗」型慢查询的指纹聚合,关键坑在于 IN 列表折叠。
WHERE id IN (1,2,3)和WHERE id IN (1,2,3,...,500)是同一个指纹,但如果你的正则只折叠单个字面量而不处理括号嵌套,会把 500 个元素的 IN 列表拆成 500 个独立指纹,聚合统计直接失真。建议用re.sub(r'\(\s*(?:\d+|\'[^\']*\')\s*(?:,\s*(?:\d+|\'[^\']*\'))*\s*\)', '(\?N)', sql)先折叠 IN 列表,再折叠剩余字面量。另外--schema匹配时注意:MySQL 8.0 的SHOW CREATE TABLE输出里索引名和列名之间有空格,但联合索引的列顺序影响覆盖判定(idx_a_b能覆盖WHERE a=1 AND b=2但不能覆盖WHERE b=2 AND a=1的 range 扫描),建议按列序前缀匹配而非集合匹配。