ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

archery使用手册:SQL审核工单、回滚与MySQL排障实践

archery使用手册:SQL审核工单、回滚与MySQL排障实践

简介:《archery使用手册》是一份面向DBA、后端开发与运维人员的SQL审核与MySQL运维实践文档,围绕Archery平台的核心能力展开,适合需要搭建SQL工单流程、排查数据库性能与并发问题、规范建表与上线操作的技术人员参考。压缩包共1个doc文件,约1.2MB,以单一文档形式集中呈现平台功能说明、实践案例与操作示例,便于按主题检索。内容覆盖SQL语法与规范审核、高危语句驳回、定时或立即执行及回滚语句生成,也包含SQL分析优化建议、慢SQL统计、实例管理中的binlog清理、会话与事务查看、锁信息排查、账号和动态参数配置,以及PTArchiver、Binlog2SQL、SchemaSync等插件的适用场景。已有1478人学习,读者可从中获得从审核流程设计到实例运维排错的一整套参考思路,并借助开发、测试环境的流程示例理解权限分工与执行闭环。

1. archery 使用手册:把 SQL 上线从口头约定变成可审计的工单

很多人第一次接触 SQL 审核平台,是因为线上出过一次“谁在什么时候改了这一列”的追问——翻聊天记录、翻 Git 提交、翻数据库慢日志,最后发现改表语句是某个人在客户端里手敲的。archery 这个资源要解决的正是这个场景:它把 SQL 提交、语法与规范检查、审批、执行、回滚语句生成串成一条有记录的链路,同时把 MySQL 日常运维里最常用的会话、事务、锁、账号、binlog 清理和几个工具插件收在同一个界面里。这份使用手册覆盖了 SQL 审核与优化、实例管理、pt-archiver 归档、binlog2sql 解析回滚、schema 比对同步这几块内容,适合正在建研发自测流程的团队、需要给非 DBA 开放受控查询入口的运维,以及想把“建表必须有主键、必须有注释”这类规范真正卡住的人。它不是拿来替代数据库本身的工具,而是一层把人和库隔开的审核闸门。下面按“流程怎么跑、SQL 怎么优化、实例怎么排障、插件怎么用、坑在哪”的顺序拆开讲。

2. SQL 审核工单的完整链路:从提交到回滚脚本落盘

archery 的核心不是“能执行 SQL”,而是“执行前必须先被规则过一遍”。理解这一点的关键,是把它看成一个工作流引擎加一个 SQL 解析器:解析器负责告诉你这条语句在语法上对不对、在规范上合不合格;工作流负责决定谁提交、谁审、审完什么时候执行。手册里给的两条流程线(研发到项目经理、测试自己提自己审)其实就是同一套引擎的两种审批节点配置。把这条链路跑通,后面所有功能才有意义。

2.1 审核流程的两个角色模型与选择依据

手册里的开发环境流程是“研发工程师 → 项目经理”两级,测试环境是“测试同事发起 → 测试同事审核并执行”一级自审。这两种配置不是随便定的,而是对应两种风险等级:开发环境的变更最终要进生产,所以需要一个不完全懂技术但能判断业务影响的人做最后一道闸;测试环境的数据可丢,所以让发起人自审,减少等待。

落到配置层面,常见做法是把审批节点按“环境 + 库”绑定,而不是按人绑。这样新增一个研发不需要改流程,只要把他放进对应角色组。判断标准很简单:如果某类变更出错后的恢复成本高于一次审批的等待成本,就加一级;否则就减到一级。

提示:一级自审不等于没有约束,规则检测这一关仍然会拦住语法错误、缺主键、缺注释、高危语句,人工审核只是第二道。

2.2 提交 SQL 工单时的字段与检测动作

发起工单需要填的信息一般包括:目标实例、目标库、是否需要回滚、执行方式、SQL 文本或上传文件。填完之后先点“SQL 检测”,这一步只做解析和规则匹配,不碰数据库。检测结果是 error 就不能提交,必须改到 warning 或通过。

下面这段不是 archery 的代码,而是把手册里“检测发现问题 → 修改 → 再检测”这个循环固化成脚本的思路,用于在提交前本地预检,减少反复:

#!/usr/bin/env bash # 本地预检脚本:把待提交的 SQL 先做一轮粗筛,减少在 archery 里来回改的次数 SQL_FILE="$1" # 1. 拦截明显的高危全表更新/删除 grep -inE '^[[:space:]]*(update|delete)[[:space:]]+[a-zA-Z0-9_]+[[:space:]]+set' "$SQL_FILE" \ | grep -viE 'where' \ && echo "[BLOCK] 检测到无 WHERE 的 UPDATE/DELETE,archery 高危规则会直接驳回" # 2. 建表语句缺主键、缺注释的粗查 grep -inE 'create[[:space:]]+table' "$SQL_FILE" >/dev/null && { grep -qi 'primary key' "$SQL_FILE" || echo "[WARN] 建表未发现主键定义" grep -qi 'comment' "$SQL_FILE" || echo "[WARN] 建表未发现注释" } # 3. 输出语句条数,便于和工单里填的数量核对 echo "语句条数(按分号估算): $(grep -o ';' "$SQL_FILE" | wc -l)"

这段脚本的定位是“后悔药前置”:在正式提交前把最容易被规则打回的三类问题先找出来。逻辑上它只做文本级匹配,不做语法树解析,所以会有误报,比如delete出现在字符串常量里。参数上$1指向 SQL 文件路径,grep -viE 'where'这一层是为了找出“UPDATE ... SET 后面没有 WHERE”的形状,实际使用时按自己的规则调整。

真正在 archery 里点“SQL 检测”时,看的是解析器返回的结果条目:每条会标明是 error 还是 warning、命中哪条规则、涉及哪一行。error 一律阻断,warning 可以带理由提交,由审核人判断。

2.3 审核通过后的执行策略与定时执行

最后一级审核通过,界面会给两个选项:立即执行、定时执行。选择依据是业务低峰期——DDL 在大表上会持锁,即使在线 DDL 也有元数据锁窗口,所以结构变更一般排到低峰。定时执行的价值还在于它把“审核通过”和“真正落库”解耦,审核人可以白天审,执行放在凌晨。

执行方式上有两种粒度:工单整体执行,或者按语句分批。大变更建议拆成多个工单,一条 DDL 一个工单,这样回滚时边界清晰,出问题只需回退其中一条,而不是整包回滚。

2.4 回滚 SQL 的生成与使用边界

手册里明确写了“下载回滚 SQL”和“查看回滚 SQL”两个入口。回滚语句是在提交工单时由平台根据变更前的数据状态生成的,这一点决定了它的可靠性边界:

变更类型是否可自动生成回滚说明
INSERT可生成对应的 DELETE,依赖主键或唯一键定位
UPDATE可生成反向 UPDATE,需要能定位到原行
DELETE可生成对应的 INSERT,要求原行数据可被完整取回
DDL(建表/改列/删列)不可结构变更无法自动反向,需人工准备回滚方案

注意:删列这类操作即使平台给了回滚入口,也不要指望它。执行前把表结构和数据做一份备份,是这条流程里唯一真正的后悔药。

3. SQL 优化与慢日志:把执行计划和规则命中翻译成动作

审核拦的是“能不能上”,优化解决的是“上了之后跑不跑得动”。archery 这块给了两个入口:对单条 SQL 做分析拿优化建议,以及查看慢 SQL 及其统计信息。这两者要配合用:单条分析解决已知问题,慢日志统计帮你找到还没被发现的那些。

3.1 用 SQL 优化入口读执行计划

手册里的示例是SELECT * FROM baidd.emp WHERE ename='dan_1';,分析后给出建议,把SELECT *改成具体字段后结果为 OK。这个例子的价值不在结论,而在它展示的判断路径:先看有没有走索引,再看取了多少列。

常见做法是把分析结果里的几项按顺序读:

  1. 访问类型(type),出现ALL说明全表扫描,出现index说明扫全索引,这两种都要关注;
  2. 预估扫描行数(rows),和表的总行数比,比例高就说明索引选择性不够;
  3. Extra 列,出现Using filesort、Using temporary意味着额外排序或临时表。

把这几项和 SQL 本身对照,改法通常落在三处:补索引、改写成能命中已有索引的形状、减少返回列。SELECT *的问题不只是多取数据,更在于它让覆盖索引失效,原本能只走索引的查询被迫回表。

3.2 慢日志统计的读法与阈值设定

慢日志这一块给出的信息是“哪些语句慢、慢了多少次、平均多久”。读的时候不要只按最慢排序,而要看“总耗时 = 平均耗时 × 次数”。一条平均 200ms 但每分钟跑上千次的查询,对系统的影响远大于一条偶发的 3 秒查询。

阈值设定上,long_query_time不建议一上来就设成 1 秒,那样会把大量中等查询漏掉。可以先按 0.5 秒采集一段时间,统计出业务查询的耗时分布,再回头定这个值。

-- 查看当前慢日志相关参数,确认采集是否真的开着 SHOW VARIABLES LIKE 'slow_query_log'; SHOW VARIABLES LIKE 'long_query_time'; SHOW VARIABLES LIKE 'log_queries_not_using_indexes'; -- 按总耗时排序看慢日志聚合(示意,实际表名依平台实现而定) SELECT sql_text, count_star AS exec_count, ROUND(sum_timer_wait / 1e12, 3) AS total_sec, ROUND(avg_timer_wait / 1e9, 3) AS avg_ms FROM slow_log_summary ORDER BY sum_timer_wait DESC LIMIT 20;

第一段是确认采集开关,log_queries_not_using_indexes打开后会记录未走索引的语句,排查阶段有用,但长期开着会让慢日志膨胀。第二段是聚合读法,按sum_timer_wait降序而不是按平均耗时,就是为了抓住“高频中等慢”的那批语句。字段名按平台的实际实现可能不同,思路是固定的:次数、总耗时、平均耗时三个一起看。

3.3 从建议到落地的判断:什么时候不该照做

优化建议是规则给的,规则不认识业务。三种情况要顶回去:一是建议补的索引和已有索引高度重复,加了只是徒增写入开销;二是建议改写的语句涉及分页深翻,改成子查询反而更慢;三是建议去掉的字段其实是业务必需,只是当前调用方没用。判断依据是这条 SQL 的调用频次和它在链路上的位置,不是建议本身的对错。

4. 实例管理排障:会话、事务、锁三张表怎么串起来看

实例管理是这套手册里最实用的部分,因为它对应的是“线上正在出事”的场景。手册给的顺序是:进程状态看正在执行的 SQL、事务信息看运行中的事务、锁信息看谁阻塞了谁,三张表靠 thread id 串起来。这个串联关系值得单独讲清楚,否则容易在界面上来回点却对不上号。

4.1 进程状态:command 类型过滤与完整 INFO

进入进程状态默认只显示command = query的会话,也就是正在执行 SQL 的那些。排查阻塞时第一步是把过滤改成All,因为阻塞方可能处于Sleep状态——它开了一个事务没提交,自己不干活,却握着锁。

第二步是勾选“完整 INFO”。不勾的话 SQL 文本会被截断,长语句只能看到开头,而问题往往出在 WHERE 条件或者 JOIN 的那一段。这个勾选是排查时的必选项,不是可选项。

第三步是看Time列,一个Sleep状态但 Time 很大的会话,通常就是忘了提交或忘了断开的连接。

4.2 事务信息与进程状态的 id 对应关系

事务信息里的trx_mysql_thread_id对应进程状态里的 THREAD ID,这是两个界面的唯一连接点。手册里给的终止路径是:先在事务信息里拿到trx_mysql_thread_id,再去进程状态里搜这个 id,选中后终止会话。这个顺序不能反,因为事务信息能告诉你这个事务持有哪些锁、跑了多久,而进程状态只能告诉你它在执行什么。

-- 当事务信息界面不好用时,可以直接在库里查(只读,不改状态) SELECT trx_id, trx_state, trx_started, TIMESTAMPDIFF(SECOND, trx_started, NOW()) AS trx_seconds, trx_mysql_thread_id, trx_query FROM information_schema.innodb_trx ORDER BY trx_started ASC; -- 锁等待关系:谁在等谁 SELECT r.trx_id AS waiting_trx_id, r.trx_mysql_thread_id AS waiting_thread, b.trx_id AS blocking_trx_id, b.trx_mysql_thread_id AS blocking_thread, b.trx_query AS blocking_query FROM information_schema.innodb_lock_waits w JOIN information_schema.innodb_trx r ON r.trx_id = w.requesting_trx_id JOIN information_schema.innodb_trx b ON b.trx_id = w.blocking_trx_id;

第一段按事务开始时间升序,最上面那条就是跑得最久的,trx_seconds直接给出持续时间,超过阈值就该处理。第二段是锁等待关系的直接映射,blocking_thread就是你要去进程状态里搜的那个 id。这两个查询都是只读的,platform 上对应的界面本质是在做同样的事。

4.3 复现一次锁等待:手册示例的完整操作

手册里给的模拟很简洁:会话 A 执行BEGIN; UPDATE baidd.t2 SET ename='baidd' WHERE id=2;不提交,会话 B 执行UPDATE baidd.t2 SET ename='dan' WHERE id=2;被阻塞。这个例子的关键在于会话 A 必须保持事务打开,B 才会挂住。

用 archery 观察时,锁信息界面会显示等待事务的 SQL,也就是 B 那条被阻塞的语句,同时给出阻塞方的线程 id。此时正确处理不是立刻杀掉阻塞方,而是先判断它是不是一个未提交的正常业务事务——如果是,杀掉会回滚它已经做的修改;如果它已经跑了很久且明显是连接泄漏,才终止。

注意:终止会话是破坏性操作,会回滚该事务的所有未提交变更。执行前确认这条会话对应的业务,别把正在跑批的任务杀掉。

4.4 binlog 清理与磁盘空间释放

实例列表里的 binlog 清理,操作语义是“选中某个 binlog 后,它之前的所有 binlog 都会被删除”。这是 MySQL 的PURGE BINARY LOGS TO语义,不是精确删除单文件。所以清理前要确认两点:从库是否已经消费到该位置,以及是否有基于 binlog 的恢复需求还在窗口期内。

常见做法是先看当前写入位置和最早文件,估算可清理范围,再决定目标文件。清理动作本身不可逆,删掉的 binlog 无法找回,这一条是手册里最需要放慢手速的地方。

5. 三个工具插件的落地姿势:归档、解析回滚、结构比对

手册里的三个插件对应三类重复劳动:历史数据越堆越大、误操作后要从 binlog 里捞数据、多套环境的表结构对不齐。它们都是把命令行工具包了一层可视化,理解底层工具的行为边界,比记住界面按钮更重要。

5.1 pt-archiver 归档:把流水表的老数据挪走

场景很典型:一张流水表只保留最近三个月,更早的数据要归档。pt-archiver 的做法是分批扫描、逐批搬迁、搬完删除,避免一条大 DELETE 把库压垮。

需要关注的参数不多,但每个都影响行为:

参数作用设置建议
--limit每批处理行数从 1000 起,按库的压力调整
--purge搬完后删除源表行不删除则只做复制
--where归档条件用时间列做范围,别用函数包列
--sleep每批之间暂停秒数低峰期可设 0,高峰期建议 1 以上
--txn-size每事务行数与 limit 配合,控制单事务大小

--where那里最容易翻车:写成DATE(create_time) < '2024-01-01'会导致索引失效,全表扫。写成create_time < '2024-01-01 00:00:00'才能走索引。

配置成自动任务时,务必让任务可中断、可续跑,归档不是一次性的活,而是持续跑的。第一次跑建议只加--dry-run看它打算搬多少行。

5.2 binlog2sql 解析:能回滚 DML,不能回滚 DDL

手册里说得很清楚:binlog2sql 从 binlog 解析出 SQL,可以生成回滚语句,但 DDL 操作无法回滚。这个边界必须记住,因为误删列、误改字段类型之后,靠这个工具是救不回来的,只能靠备份。

使用流程是给定位:指定 binlog 文件名和起止位置,拿到该区间内的语句。手册的例子是查master-bin.000009从 857 到 1183 之间的内容。位置信息可以从SHOW BINARY LOGS和SHOW BINLOG EVENTS里拿,也可以按时间范围让平台换算。

生成回滚语句时,工具的按钮语义是反转:把原 INSERT 变成 DELETE、原 DELETE 变成 INSERT、原 UPDATE 变成反向 UPDATE。前提是能定位到原行,所以表上有主键或唯一键是硬条件,没有主键的表解析出来的回滚语句可能影响多行。

注意:回滚语句生成后不要直接整段执行,先挑几条在测试库验证,确认 WHERE 条件唯一命中一行。

5.3 schema 比对:只比结构不比数据

schema 同步工具的输入是源库和目标库,输出是让目标库追上源库的 DDL,以及对应的回滚 DDL。手册的例子是251库有几张表、249库只有一张,比对后生成249相对251应该执行的 DDL。这里的语义方向要看清:从目标追源,别搞反。

它只比对表结构,不比对数据,所以索引差异、列类型差异、默认值差异会被发现,行数差异不会。用它做上线前检查很合适:把预发和生产的表结构比一遍,防止本地加了个字段忘了发布。

比对结果里的回滚语句同样有边界:加列的回滚是删列,删列的回滚拿不回来数据。所以这套流程适合“结构对齐”,不适合“结构回退”。

6. 把这些流程跑顺的几个关键习惯

用了一段时间之后,我总结出这套手册里最容易被忽略的不是功能,而是顺序和边界。功能都在界面上摆着,真正决定这套平台好不好用的是几个前置判断。下面几条是我反复验证过的。

第一,工单粒度按语句拆,不按需求拆。一个需求涉及三条 DDL,就发三个工单。好处是执行可以分开排期,回滚边界清晰,出问题时能精确定位是哪一条导致。整包工单看起来省事,实际是把风险捆在一起。

第二,回滚语句在提交前看一遍,不要等出事再看。平台的生成逻辑依赖主键和唯一键,如果某张表没主键,生成的回滚 UPDATE 可能命中多行。提交工单时顺手点开回滚 SQL,确认 WHERE 条件能唯一定位,这个动作只要一分钟。

第三,进程状态里默认只看 query 是个陷阱。阻塞排查的第一步永远是切到 All 并勾上完整 INFO,否则你看到的是“没有异常会话”,而真正的持锁者正躺在 Sleep 里。

第四,定时执行的时间点要避开归档、备份、统计任务。这些任务本身就在吃 IO,DDL 撞上去会让元数据锁的等待时间成倍放大。排期前看一眼实例的定时任务表。

第五,清理 binlog 前先用从库的消费位置做一次核对。手册里写的“选中某个 binlog 后它之前的全删”是真实语义,删完没有回收站。

第六,插件类的工具都建议先在测试库跑一遍同样的参数。尤其是 pt-archiver,--purge加上错误的--where,删错了数据只能靠备份恢复,而备份可能还没覆盖到那一刻。

有条血泪经验值得单独说:某次在预发环境验证 schema 同步,比对生成了 DDL 和回滚 DDL,我直接执行了同步那段,忘了回滚段里的删列操作是不可逆的。后来每次做结构比对,我都会先看清楚哪一半是“往前”、哪一半是“退后”,再决定点哪个按钮。从那以后,凡是带“自动生成回滚”字样的功能,我都强制自己先读一遍生成的语句本身,再决定是否执行。

希望这份手册的拆解能帮到你,尤其是那几条边界,早点知道能少走不少弯路。

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

RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进