2026/10/31 7:34:49

Archery SQL审核与MySQL运维实战:工单、回滚与插件避坑

Archery SQL审核与MySQL运维实战:工单、回滚与插件避坑 简介《archery使用手册》面向研发工程师、测试人员与数据库管理员围绕SQL审核查询平台及MySQL运维场景说明如何完成工单审核、性能优化与实例排障适合需要快速上手并规范日常数据库操作流程的读者。压缩包仅有1个doc文档约1.2MB以文字说明结合实践案例集中整理平台功能内容涵盖SQL语法与规范审核、高危语句自动驳回、定时或立即执行、回滚语句生成并延伸至SQL分析优化、慢查询统计、日志清理、会话与事务管理、锁信息排查、账号授权和动态参数配置。文档还介绍数据归档、日志解析、结构同步等工具插件的使用方法给出开发与测试环境的完整审核流程示例便于对照日常实际场景查阅常见操作要点、降低误操作风险。已有1478人学习。1. 为什么先上审核再上监控一份 archery 使用手册的阅读顺序接手一个十几人的研发团队那年最让我加班到凌晨的不是慢查询是没人管的 DDL——有人本地改完表结构直接把语句贴进工单有人顺手在测试库又改了一遍等主库报警谁也说不清是哪条语句动的手。archery 使用手册这类资料的价值就在这儿它把一个喊成口号的「SQL 审核」拆成了可配置的规则、有审批人的工单、能下载的回滚语句。Archery 定位于 SQL 审核查询平台顺带把 MySQL 运维里最常伸手的几件事收进同一个界面会话、事务、锁、binlog 清理、账号授权、参数变更记录外加 pt-archiver、binlog2sql、schemasync 三个插件。写 SQL 的开发、做发布的测试、半夜被叫起来兜底的 DBA都能在里面找到自己那一栏。往下走先立审核链路再谈实操和坑。2. SQL 审核链路拆解解析、规则、工单与回滚语句怎么串起来很多人第一次用 Archery是把 SQL 贴进去点「SQL 检测」看到一片绿色就以为完事。真正决定这套东西好不好用的是后面三段链路规则怎么被解析命中、工单在谁手里流转、回滚语句由谁负责生成和验证。三段任何一段塌了平台就退化成一张记事本。2.1 审核规则的解析链路从语法树到规范检查提交的 SQL 先过语法解析这一层不依赖任何业务配置语句写错、关键字拼错、括号不闭合解析器直接吐 error工单连提交按钮都点不下去。解析通过之后才进入规范检查也就是大部分团队真正在维护的部分。常见做法是接一个审核执行引擎Archery 生态里默认是 Inception把待执行的 SQL 在影子环境里跑一遍得到「执行后」的库表结构快照再拿这个快照去跑每条规则配置的 SQL 查询。查询返回的行就是命中的违规项返回 0 行这条规则放行。理解这一点很关键规则不是写一段正则去匹配 SQL 文本而是写一条针对information_schema的查询。所以规则能覆盖到「字段有没有注释」「表有没有主键」「索引列顺序对不对」这类文本层面看不出来的东西。下面这条是我在项目里用得最多的一条建表必须有主键-- 规则名新建表必须包含主键 -- 规则级别errorerror 级别不允许提交工单 -- 规则说明影子环境执行后任何一张无主键的业务表都会被查出来 SELECT t.table_schema, t.table_name FROM information_schema.tables AS t WHERE t.table_schema %s -- %s 由平台替换为当前工单的库名 AND t.table_type BASE TABLE AND NOT EXISTS ( SELECT 1 FROM information_schema.statistics AS s WHERE s.table_schema t.table_schema AND s.table_name t.table_name AND s.index_name PRIMARY );%s是平台侧的库名占位符工单提交哪个库规则就在哪个库里跑table_type BASE TABLE把视图排除掉否则视图永远命中规则噪音会大到没人看。级别设为 error 还是 warn 是个管理问题不是技术问题主键、表注释、列注释这类我认为必须 error索引命名规范、字段字符集统一这类先给 warn 跑两周等命中率降下来再收紧。表注释的规则同理只是换成查table_comment-- 规则名表必须填写注释 SELECT table_schema, table_name FROM information_schema.tables WHERE table_schema %s AND table_type BASE TABLE AND (table_comment IS NULL OR table_comment );高危语句的处理不太一样它走的不是 SQL 查询规则而是内置的语句类型判断没有 WHERE 的 UPDATE / DELETE、WHERE 恒真的 DELETE、不带 LIMIT 的全表更新这类会被直接驳回。这里有个细节WHERE 11、WHERE id id、把条件写在注释里都会被识别成「无有效过滤条件」。我见过有人为了绕过审核把条件写成WHERE 11 AND id 5结果还是被拦——规则看的是过滤条件是否恒真不是看有没有 WHERE 这个词。规则配完之后工单页面的行为是这样点「SQL 检测」error 项直接拦住工单提交不上去只有 warn 的话可以点「SQL 提交」进入审批流。这个顺序别搞反先提交后检测的工单在审批人眼里就是黑匣子谁也不知道执行前长什么样。2.2 工单流转开发环境与测试环境的两套审核路径同一个平台开发和测试两条流程的审批人完全不同。这不是设计上的偷懒而是权限和责任的自然映射开发环境改的是研发库出错顶多阻塞半天测试环境是上生产前最后一道闸审批权必须和执行权绑在同一个人身上。环节开发环境测试环境SQL 来源研发本地分支从 git 取开发已提交的 SQL 文件发起人研发工程师测试工程师审核人项目经理测试同事自审执行人发起人或项目经理测试工程师回滚责任人发起人发起人开发环境的路径是「研发工程师 → 项目经理」。发起人在工单里填实例、库、变更说明把 SQL 贴进去点「SQL 检测」有 error 就回去改改完重新检测全绿再点「SQL 提交」。项目经理登录后右上角通知按钮里能看到待办点进去逐条看规则命中情况——重点看那些 warnerror 已经被拦在门外了。最后一级审核通过执行方式才可选立即执行或者定时执行。测试环境反过来发起人和审核人是同一批测试同事SQL 文件从 git 上取取的是开发已经提交进仓库的版本不是聊天窗口里传的那份。这条规矩省了我很多事出问题的时候有 commit 可查而不是靠回忆「当时谁发我的」。工单里我一般要求填全这几个字段缺一个审批就打回字段填写要求为什么必须填工单名称业务模块 动作如「订单表新增渠道字段」事后检索靠它实例 / 库精确到库禁止写「主库」避免执行到错误的实例变更说明一句话说清改什么、为什么改半年后回看只有它有用执行方式立即 / 定时定时填到分钟与发布窗口对齐备份确认DDL 必须勾选已备份DDL 无法自动回滚回滚方案贴反向 DDL 或说明由平台生成出问题时不用现场想定时执行这个功能用得好是省心用不好是玄学。平台记录的时间按服务器时区走如果应用服务器和数据库服务器不在一个时区你设的凌晨两点可能落在业务高峰。我一般要求定时工单的执行时间避开整点留出至少十分钟余量并且必须有人在那个时间点在线——定时不是无人值守只是把执行动作挪到人方便盯的时候。2.3 回滚语句的生成边界DML 能救DDL 只能靠人审核通过后工单页面会出现「查看回滚 SQL」和「下载回滚 SQL」。这个能力对 DML 是真的救命对 DDL 就要打个问号了。DML 的回滚语句是反着构造的INSERT 反向生成按主键定位的 DELETEUPDATE 用变更前的行镜像反写回原值DELETE 用被删行的完整镜像重新 INSERT。前提是能拿到完整行镜像也就是binlog_row_image得是 FULL。如果实例上是 MINIMALUPDATE 只会记被改的列回滚时其他列的值无从还原生成的语句看着能跑跑完数据是错的。DDL 没有这个反向路径。加列、加索引、改字段类型平台生成不了可信的反向语句只能靠人工写-- 变更订单表加一列 ALTER TABLE t_order ADD COLUMN channel VARCHAR(32) NOT NULL DEFAULT COMMENT 来源渠道 AFTER status; -- 回滚删掉这一列 -- 注意 MySQL 8.0 之前 DROP COLUMN 会触发表重建大表要先评估窗口 ALTER TABLE t_order DROP COLUMN channel;加索引的回滚是DROP INDEX改字段类型的回滚需要变更前的原始定义所以在工单里我强制要求贴一份「变更前结构快照」SHOW CREATE TABLE t_order;这条语句的输出粘进工单备注三秒的事出问题时能省三小时。血泪经验是一次改字段类型回滚时没人记得原来是不是NOT NULL、默认值是什么最后只能从备份库翻结构翻了四十分钟。回滚 SQL 下载下来别直接在生产跑。我一般会先在预发或者从库上执行一遍比对行数、比对关键字段的校验和确认干净了再上生产。回滚本身也是一次变更它同样需要审核只是很多时候来不及走流程——这也是为什么事前备份比事后回滚重要。3. 实例管理实战会话、事务、锁与 binlog 的四条排查线审核解决的是「变更之前」实例管理解决的是「出事之后」。这两块在 Archery 里是同一个入口下的两组页面但使用节奏完全不同审核是流程化的、按周走的实例管理是突发的、按分钟走的。下面四条线是我在半夜被叫起来时按顺序看的。3.1 会话排查完整 INFO 勾选与终止会话进程状态页默认只显示command Query的会话也就是正在跑 SQL 的那些。这个默认值挺合理Sleep 的连接一屏几百条看了也没用。但如果要排查连接数暴涨、连接池泄漏右上角的 command 类型要切到All否则你会以为连接数很健康。第二个开关是「完整 INFO」。MySQL 的information_schema.processlist里info字段默认只保留前 100 个字符Archery 页面上不勾这个选项你看到的 SQL 永远是断头的长 SQL 只能看到SELECT ... FROM就没了。排查慢 SQL 时忘了勾盯着半截语句猜表名这种事我干过不止一次。对应的 SQL 我自己也留了一份平台打不开的时候直接连库查-- 找出运行超过 30 秒的非 Sleep 会话按耗时倒序 SELECT id, user, host, db, command, time, state, LEFT(info, 300) AS sql_head -- 只取前 300 字符避免刷屏 FROM information_schema.processlist WHERE command Sleep AND time 30 ORDER BY time DESC;time的单位是秒对Query类型是语句已执行时间对Sleep是空闲时间state是当前状态Sending data、Locked、Waiting for table metadata lock这几个值最能说明问题看到Waiting for table metadata lock基本可以断定有人在做 DDL。定位到具体会话后页面上有「终止会话」按钮背后就是 KILLKILL 12345; -- 断开整个连接当前事务回滚 KILL QUERY 12345; -- 只中断当前语句连接保留这两个的区别值得单独说KILL会回滚事务如果碰上一个大事务已经跑了二十分钟回滚可能比它本身还慢而且回滚期间锁一直不放KILL QUERY只让当前语句报错退出事务还在连接还在客户端如果做了重试可能会立刻再发一遍。我一般先KILL QUERY观察十几秒锁没释放再KILL。3.2 事务与锁的对照trx_mysql_thread_id 怎么用锁信息页给的是「谁在等、谁在挡」。页面上的等待事务 SQL 就是被阻塞的那条语句阻塞方会显示线程 id。这个 id 和进程状态页里的 THREAD ID 是同一个东西拿它去进程状态页右上角搜索选中终止阻塞就解了。想在平台外复现同样的信息5.7 和 8.0 写法不一样。5.7 用information_schema.innodb_lock_waits关联innodb_trx8.0 换成了performance_schema.data_lock_waits和data_locks老 SQL 在 8.0 上直接报字段不存在这也是很多人升级后觉得「锁查不出来了」的原因。-- MySQL 8.0等待中的锁与持锁方的对应关系 SELECT w.REQUESTING_ENGINE_TRANSACTION_ID AS waiting_trx, w.BLOCKING_ENGINE_TRANSACTION_ID AS blocking_trx, r.OBJECT_NAME AS waiting_table, r.INDEX_NAME AS waiting_index, b.THREAD_ID AS blocking_thread, b.OBJECT_NAME AS blocking_table FROM performance_schema.data_lock_waits AS w JOIN performance_schema.data_locks AS r ON r.ENGINE_LOCK_ID w.REQUESTING_ENGINE_LOCK_ID JOIN performance_schema.data_locks AS b ON b.ENGINE_LOCK_ID w.BLOCKING_ENGINE_LOCK_ID;blocking_thread拿到之后回会话页按 THREAD ID 搜索就是那个该被处理的连接。5.7 的等价写法是把data_lock_waits换成innodb_lock_waits关联innodb_trx拿trx_mysql_thread_id字段名不同思路一样。自己搭环境验证一遍最快。开两个会话-- 会话 A BEGIN; UPDATE t_order SET status 1 WHERE id 2; -- 先不提交 -- 会话 B会被阻塞直到 A 提交或回滚 UPDATE t_order SET status 2 WHERE id 2;这时候去锁信息页刷新等待事务显示的就是会话 B 的 UPDATE阻塞方线程 id 指向会话 A。这个实验我在给团队做培训时每次都跑一遍比讲十分钟原理管用——尤其是让大家看到「事务没提交锁就一直挂着」这件事。顺带说一句事务信息页里的trx_mysql_thread_id和进程状态页的 THREAD ID 对应想杀掉某个长事务按这个 id 去进程状态页搜选中终止。但要注意长事务分两种一种是有人在客户端忘了提交杀掉没损失另一种是正在跑大批量更新杀掉要回滚回滚时间可能比原事务还长杀之前先看看它已经改了多少行。3.3 binlog 清理与账号、参数配置的权限边界实例列表里可以清理 binlog操作方式是选中某个文件这个文件之前的所有 binlog 都会被删掉。对应的 SQL 是-- 删除 mysql-bin.000010 之前的所有 binlog不含 000010 PURGE BINARY LOGS TO mysql-bin.000010;这个操作不可逆而且风险点不在主库在从库。如果从库还没拉取到被删的文件主从立刻断恢复起来要么重做从库要么从备份重建。执行前先确认从库进度-- 5.7 / 8.0 通用看从库当前读到主库哪个文件 SHOW REPLICA STATUS\G -- 看 Relay_Master_Log_File 和 Exec_Master_Log_Pos 两个字段 -- 5.7 老版本用 SHOW SLAVE STATUS\G字段名相同只清理Relay_Master_Log_File之前的文件且留出一两个文件的余量别卡着边界删。磁盘告警时我见过有人一口气清到最新文件主从断了业务读写分离的从库全部失效比磁盘满的影响大得多。账号管理页给的是图形化的建号、授权、改密、删号。方便但有两个坑。第一是 host 段页面上默认给%图省事就这么提交了等于允许任意来源连接。我一般要求收窄到网段-- 建号时把 host 收窄不要用 % CREATE USER app_rw10.20.30.% IDENTIFIED BY 强密码; GRANT SELECT, INSERT, UPDATE, DELETE ON db_demo.* TO app_rw10.20.30.%; -- 建完立刻验证实际生效的权限 SHOW GRANTS FOR app_rw10.20.30.%;第二是权限粒度图形界面勾选很方便容易顺手把ALL PRIVILEGES勾上连DROP、GRANT OPTION一起给了。建完必须跑一次SHOW GRANTS核对页面显示的和实际生效的偶尔会因为同名账号不同 host 而看起来对不上。参数配置页能改实例的动态参数并记录修改历史。这里要分清哪些参数改了立刻生效、哪些要重启、哪些会被写进配置文件。SET GLOBAL改的只在当前实例生命周期内有效重启回原值MySQL 8.0 的SET PERSIST会落盘到mysqld-auto.cnf重启后仍生效。而innodb_buffer_pool_size这类静态参数页面上提交了也不会生效得改配置文件重启。平台的修改历史只记录从平台发起的变更DBA 手工在命令行改的看不到排查参数漂移时不能只信这个页面。4. 三个插件落地pt-archiver、binlog2sql、schemasync 的参数与边界三个插件解决三类完全不同的活儿历史数据搬走、误操作数据找回、两套环境结构对齐。共同点是它们都是开源命令行工具Archery 做的只是把参数表单化和把结果可视化所以真正要弄懂的是命令行本身的参数含义。表单里的每个选项背后都对应一个参数理解参数才知道哪个勾不能乱勾。4.1 PT-Archiver 归档流水表分批与限速参数典型场景一张流水表只保留最近三个月更早的数据业务不查了但要留档。手工写DELETE ... WHERE created_at ...是灾难一个大事务锁表几十分钟binlog 暴涨从库延迟拉满。pt-archiver 的做法是分批取、分批删、每批之间歇一下。平台里可以配归档任务也可以由用户提交归档申请命令行等价写法是这样pt-archiver \ --source h10.20.30.11,P3306,uarchiver,p***,Ddb_demo,tt_order \ --dest h10.20.30.12,P3306,uarchiver,p***,Ddb_archive,tt_order \ --where created_at DATE_SUB(NOW(), INTERVAL 3 MONTH) \ --limit 1000 \ --commit-each \ --sleep 1 \ --max-lag 5 \ --check-slave-lag h10.20.30.13,P3306,umonitor,p*** \ --purge \ --statistics参数逐个说清楚这几行是能不能安全跑的分水岭参数含义我的取值习惯--limit每批取多少行5002000大表取小值--commit-each每批单独提交必开否则又变成大事务--sleep每批之间停几秒0.51主库压力大就加--max-lag从库延迟超过多少秒就暂停35--check-slave-lag监控哪台从库的延迟生产必配指向只读从库--purge归档成功后删除源表数据首次跑千万别开--dry-run只打印不执行上线前必跑一遍--where里的时间字段必须有索引否则每一批都是一次全表扫越跑越慢。目标表要先建好结构用CREATE TABLE db_archive.t_order LIKE db_demo.t_order复制别指望工具帮你建表。第一次跑我一般这么走先--dry-run看它打算搬多少行再--no-delete只搬不删跑一天确认目标表数据对得上最后才加--purge正式跑。归档任务配成定时之后记得留监控——任务静默失败是最麻烦的你以为数据在搬实际上停了半个月。4.2 Binlog2SQL 解析与 flashback 的可用范围误删数据之后binlog2sql 是后悔药。它的原理是读 row 格式的 binlog把行变更还原成 SQL 语句加-B参数时反向生成回滚语句。平台里把它做成了可视化选实例、填 binlog 文件名和起止位置点获取就能看到这段区间内的 SQL。想看mysql-bin.000009从 857 到 1183 位置之间发生了什么python binlog2sql.py \ -h10.20.30.11 -P3306 -uarchiver -p*** \ -d db_demo -t t_order \ --start-filemysql-bin.000009 \ --start-pos857 \ --end-pos1183加上-B就是回滚模式平台上是点「flashback」按钮再获取一次python binlog2sql.py \ -h10.20.30.11 -P3306 -uarchiver -p*** \ -d db_demo -t t_order \ --start-filemysql-bin.000009 \ --start-pos857 --end-pos1183 \ -B-d是库-t是表可以只给库不给表--start-pos/--end-pos是 binlog 内的偏移用SHOW BINLOG EVENTS或mysqlbinlog定位。三个前提条件缺一不可binlog_formatROW、binlog_row_imageFULL、账号有SELECT和REPLICATION SLAVE、REPLICATION CLIENT权限。少一个要么解析不出内容要么生成的回滚语句缺列。边界必须说清楚DDL 不能回滚。DROP TABLE、TRUNCATE、ALTER这些在 binlog 里是语句事件不是行事件binlog2sql 生成不了反向操作这类事故只能靠备份恢复。另外回滚 SQL 是按 binlog 顺序倒着执行才成立的如果你只截取中间一段跑跨事务的依赖会断数据可能越修越乱。我的一般流程是定位到误操作前后完整的 binlog 区间生成回滚 SQL先在临时库重放一遍验证确认行数和关键字段对得上再上生产。4.3 SchemaSync 比对表结构只比结构不比数据SchemaSync 解决的是「两套环境的表结构不一致」这个老问题。开发库加了字段忘了同步到测试库或者测试库手工改过一版没回写靠人肉对比SHOW CREATE TABLE一行行看迟早出错。它的逻辑很直接读源库和目标库的 schema比对差异生成两个文件——正向的 patch 和反向的 revert。平台里的示例场景是一套库里有 t1、t2 两张表另一套库只有 t2比对后生成目标库需要补齐的 DDL同时给出对应的回滚 SQL。schemasync \ --output-directory ./schema_diff \ --tag src_vs_dest \ mysql://user:pass10.20.30.11:3306/db_demo \ mysql://user:pass10.20.30.21:3306/db_demo两个位置参数分别是源和目标顺序不能反反了生成的就是「删表」而不是「建表」。--output-directory是产物目录--tag打在文件名上方便区分批次--no-date去掉文件名里的日期。跑完目录里会多出db_demo.patch.xxx.sql和db_demo.revert.xxx.sql两个文件。使用边界有三条。第一只比对表结构不比对数据别指望它同步数据。第二索引名、字符集、排序规则、字段顺序的细微差异都会产生变更语句工具不区分「必须改」和「看着不一样」生成的 patch 必须人工过一遍不然会出现一堆无意义的MODIFY COLUMN。第三它是命令行工具没有事务保护patch 跑一半失败要手动收拾所以执行前先备份结构执行后立刻SHOW CREATE TABLE抽查几张核心表。5. 避坑与排查工单、执行、插件环节的五次翻车记录下面五条都是实际踩过的按「现象 → 原因 → 解决」写。有些坑看起来是平台的问题往下查基本都是用法的问题。5.1 审核与工单环节的三个坑一、SQL 检测全绿执行时报字段不存在。现象很迷惑工单里 SQL 检测通过审批也过了点立即执行数据库返回Unknown column。原因是审核引擎在影子环境里跑影子环境的结构是依据工单提交时的库结构快照构造的如果提交工单到执行之间有人手工改了那个库的表结构快照就过期了。另一个常见原因是 SQL 里写的是别名或者开发库特有的字段审核环境的建表语句和线上有细微差别。解决方式是执行前重新点一次「SQL 检测」让快照刷新DDL 类工单则强制执行一次预发验证不要在工单里省这一步。二、明明写了 WHERE还是被判高危驳回。现象是UPDATE t_order SET status 1 WHERE 11 AND id 5这种语句被拦住。原因是规则判断的不是「有没有 WHERE 这个词」而是过滤条件是否恒真、是否包含主键或索引条件。11 AND id5在逻辑上等价于id5但有些版本的解析器对11这类恒真表达式直接给高危标记。解决方式是写干净的条件别带这种惯性前缀如果确实是规则误判去后台看这条规则的具体 SQL判断是规则的匹配范围太宽调规则而不是绕规则。三、工单卡在「审核通过」状态没人执行。现象是审批人点了通过工单一直挂着第二天发现还没跑。原因通常有两个执行方式选的是定时时间填成了过去的时间点任务入库时已经过期调度器不会补跑或者执行工作进程挂了工单在队列里排队。解决方式是执行窗口前十分钟确认一次工单状态遇到卡住的先去系统里看调度进程是否存活再考虑手工重提。定时执行不是无人值守这点必须在下单时就讲清楚。5.2 执行与插件环节的两个坑四、回滚 SQL 跑完了数据还是不对。现象是误更新之后生成回滚语句执行完发现部分行的其他字段被清空了。原因是实例的binlog_row_image配的是 MINIMALUPDATE 事件只记录被修改的列回滚时生成的反向 UPDATE 只带这几列其余列在构造语句时是空的一执行就把数据覆盖成空值。解决方式是把binlog_row_image统一改成 FULL改完对存量实例做一次巡检已经出问题的放弃 binlog 回滚走全量备份加 binlog 前滚到误操作前一刻或者从从库捞数据比对修复。五、归档任务跑了一晚上主库 IO 打满、从库延迟报警。现象是 pt-archiver 一启动主库磁盘写入直接顶到上限从库延迟从 0 秒涨到几百秒。原因是--limit设成了几万--commit-each没开每批变成一个巨型事务再加上没配--sleep和--max-lag工具完全不看从库状态能跑多快跑多快。解决方式是参数全部重配批大小降到 1000 以内开--commit-each加--sleep 1加--check-slave-lag和--max-lag 5。上线前先--dry-run再--no-delete跑半天观察监控曲线确认平稳了才开--purge。6. 把审核规则养成资产一份规则维护表和三步验证习惯规则配完不是终点。团队写 SQL 的习惯会变业务表结构会变半年前合理的规则现在可能天天误报误报到一定程度大家就开始绕绕的方式一旦形成规则就形同虚设。我一般每季度做一次规则体检看三个数命中次数、误报次数、被绕过次数。命中高且误报低的规则保留并考虑升到 error命中低但一直有零星触发的看看是不是规则 SQL 写得太宽误报高的直接降级回 warn 或者下线。规则级别上季度命中误报处理建表必须有主键error120保留表必须有注释error312保留补充豁免名单列必须有注释warn14815保留 warn暂不升级索引名规范warn97下线改成评审 checklist单表索引数量上限warn30升级为 error第二个习惯是提交前本地预检。平台审核再快也要走一轮能在本地挡掉的明显问题就不该占用审批人的时间。我写了个粗糙的预检脚本用sqlparse把 SQL 切成分句几条正则扫一遍import re import sqlparse # 只做粗筛真正的规范仍以 Archery 后台规则为准 CHECKS [ (re.compile(r^\s*select\s\*, re.I | re.S), SELECT * 请展开具体字段), (re.compile(r^\s*update\s\w\sset\s[^;]*$, re.I | re.S), UPDATE 未见 WHERE确认是否全表更新), (re.compile(r^\s*delete\sfrom\s\w\s*$, re.I | re.S), DELETE 未见 WHERE), (re.compile(r^\s*alter\stable\s\w\sdrop\scolumn, re.I | re.S), DROP COLUMN 不可自动回滚需备份), ] def precheck(sql_text: str): issues [] for stmt in sqlparse.parse(sql_text): raw str(stmt).strip() for pattern, msg in CHECKS: if pattern.search(raw): issues.append((msg, raw[:80].replace(\n, ))) return issues if __name__ __main__: with open(change.sql, encodingutf-8) as f: for msg, head in precheck(f.read()): print(f[预检] {msg} - {head})函数逐条解析语句命中规则就把提示和前 80 个字符打出来跑一遍不到一秒。它挡不住「字段类型选错」「索引顺序不对」这类需要看表结构的问题但能把 SELECT *、忘写 WHERE、随手 DROP COLUMN 这三类送分题拦在提交之前。脚本里的正则只是粗筛别拿它当审核结果真正的一致性检查还得靠平台。最后一个习惯是回滚脚本待命。凡是 DDL 工单执行前必须把反向语句写进工单备注并且在预发环境跑通过一次凡是 DML 工单执行前确认binlog_row_image是 FULL并记下执行前后的 binlog 位置。从那以后我每次提工单前都强制走一遍「本地预检 → 平台 SQL 检测 → 预发执行 → 回滚脚本待命」这四步慢是慢了点但半夜被叫起来的次数确实少了。手册、规则 SQL 模板和归档参数表我按章节顺序整理在了一起照着顺序对照着配一遍就行。希望帮到你。本文还有配套的精品资源点击获取