简介:本资源是一份面向数据库管理员、后端开发及运维工程师的《Archery使用手册》实战指南,聚焦SQL审核、性能优化与MySQL实例精细化管理三大核心场景。手册系统覆盖SQL语法与规范审核(含高危语句自动驳回、钉钉通知)、慢SQL分析与优化建议、binlog清理、会话/事务/锁监控、账号权限配置,以及PTArchiver、Binlog2SQL、SchemaSync等关键插件的可视化操作流程。资源为单个Word文档(.doc),大小1.2MB,内容结构清晰,含功能详解、审核工单全流程示例(研发→项目经理→测试多角色协作)、SQL优化前后对比、锁等待模拟排障、工具参数配置截图等实操细节。目前已有1474人学习下载,适合希望快速掌握Archery平台部署后落地应用、提升SQL质量与数据库稳定性的中高级技术人员。
1. Archery 是什么:一个能跑在生产环境里的 SQL 审核平台,不是玩具
Archery 不是又一个“本地调试用的 SQL 格式化小工具”,它是国内一线 DBA 团队真正在 MySQL/Oracle/PostgreSQL 生产环境中长期扛住日均 5000+ 工单、审核通过率超 92% 的 SQL 安全网关。它把 DBA 最头疼的三件事——开发提的 DML 语句有没有 WHERE、ALTER TABLE 是否加了 ONLINE、DROP 表有没有走审批流程——全部收进 Web 界面 + 自动化规则引擎 + 钉钉/企业微信通知闭环里。你不需要写 Python 脚本去解析慢日志,也不用靠人工盯 binlog 去回滚误操作;Archery 把 PT-archiver 的归档能力、Binlog2SQL 的闪回逻辑、MySQL 8.0 的角色权限体系,全拧成一套可审计、可回溯、可灰度上线的落地管线。适合中小团队没有专职 DBA 但又不敢放任开发直连生产库的场景,也适合已有 DBA 团队想把经验沉淀为规则、把重复劳动交给机器的进阶需求。它不解决“怎么装 MySQL”,但能让你装完 MySQL 后,第一行上线 SQL 就被拦下来检查索引缺失。
2. 搭建 Archery:从源码编译到服务启动的最小可行路径
Archery 官方推荐 Docker 部署,但真实生产环境里,Docker Compose 的网络隔离、日志落盘、配置热更新常翻车;我们一线更倾向源码部署——可控、可 debug、升级时能看清每行 patch 干了什么。以下是在 CentOS 7.9 + MySQL 8.0.33 + Python 3.9.16 环境下验证过的最小闭环路径,全程无 Docker、无 Kubernetes,纯 Linux 服务化管理。
2.1 环境准备:系统依赖与 Python 环境隔离
Archery 本质是 Django 应用,但重度依赖 MySQL 客户端库、psutil(查进程)、pymysql(连库)、sqlparse(解析 SQL)等 C 扩展模块。CentOS 默认的 python3-pip 版本太老,直接 pip install 会卡在 cryptography 编译;必须先装好编译链和 OpenSSL 开发头文件:
# 安装系统级依赖(CentOS 7) sudo yum install -y gcc gcc-c++ make openssl-devel libffi-devel mysql-devel \ python39 python39-devel python39-pip # 创建独立虚拟环境(严禁用系统 Python 直接 pip) python3.9 -m venv /opt/archery/venv source /opt/archery/venv/bin/activate # 升级 pip 到 23.3.1(低于 23.0 会因 setuptools 68+ 报错) pip install --upgrade pip==23.3.1提示:
mysql-devel是关键。如果漏装,后续pip install PyMySQL虽能成功,但archery manage.py db init会报ModuleNotFoundError: No module named '_mysql'——因为 PyMySQL 是纯 Python 实现,但 Archery 内部部分模块(如 binlog 解析桥接)仍调用 MySQLdb 兼容层,而该层需mysql_config可执行文件,它由mysql-devel提供。
2.2 获取源码与初始化数据库结构
Archery 项目已迁移到 GitHub 组织archerydms下,主仓库为archery。注意:不要用旧版hhyo/Archery(已归档),新版自 4.0 起强制要求 MySQL 5.7+,且默认启用sql_mode=STRICT_TRANS_TABLES,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,否则迁移脚本会失败。
# 下载最新稳定版(截至 2024 年 6 月为 v4.2.0) cd /opt/archery git clone https://github.com/archerydms/archery.git --branch v4.2.0 --depth 1 cd archery # 初始化数据库(假设已建好名为 archery 的库,字符集 utf8mb4,排序规则 utf8mb4_unicode_ci) # 注意:MySQL 用户需有 CREATE、ALTER、INDEX、REFERENCES 权限,不能仅给 USAGE mysql -u root -p -e "CREATE DATABASE IF NOT EXISTS archery CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci;" mysql -u root -p -e "CREATE USER 'archery'@'localhost' IDENTIFIED BY 'StrongPass123!';" mysql -u root -p -e "GRANT ALL PRIVILEGES ON archery.* TO 'archery'@'localhost'; FLUSH PRIVILEGES;" # 安装 Python 依赖(跳过前端构建,后端服务无需 nodejs) pip install -r requirements.txt --no-cache-dir # 修改配置文件:复制模板并填入数据库连接 cp archery/settings.py.example archery/settings.py sed -i "s/'HOST': '127.0.0.1'/'HOST': '127.0.0.1'/g" archery/settings.py sed -i "s/'PORT': '3306'/'PORT': '3306'/g" archery/settings.py sed -i "s/'USER': 'root'/'USER': 'archery'/g" archery/settings.py sed -i "s/'PASSWORD': ''/'PASSWORD': 'StrongPass123!'/g" archery/settings.py sed -i "s/'NAME': 'archery'/'NAME': 'archery'/g" archery/settings.py2.3 迁移数据表并启动服务
Archery 使用 Django ORM 管理 schema,但它的migrate命令不是简单跑django.db.migrations,而是封装了archery manage.py db init——这个命令会自动创建用户表、审核工单表、实例配置表,并插入初始管理员账号(admin/admin):
# 执行数据库迁移(含初始数据) python manage.py db init # 创建超级管理员(可选,init 已建 admin,但建议再确认) python manage.py createsuperuser --username dba --email dba@company.com # 启动开发服务器(仅用于验证,生产环境必须用 gunicorn) python manage.py runserver 0.0.0.0:8000此时访问http://<your-server-ip>:8000,输入admin/admin即可登录。但注意:runserver是 Django 自带的单线程开发服务器,绝对不可用于生产。下一节将切换为 gunicorn + nginx 的标准组合。
3. 生产就绪:gunicorn + nginx + systemd 三位一体服务化
开发模式下runserver无法处理并发请求,且无进程守护、无日志轮转、无 graceful shutdown。生产环境必须用 gunicorn 管理 worker 进程,nginx 做反向代理和静态资源托管,systemd 确保开机自启与异常重启。
3.1 配置 gunicorn:控制并发与内存安全
Archery 对内存较敏感,尤其开启 SQL 审核规则扫描时,单个 worker 可能占用 300MB+。worker 数量不能简单按 CPU 核数设,需结合--max-requests和--max-requests-jitter防止内存泄漏累积:
# 创建 gunicorn 配置文件 /opt/archery/gunicorn.conf.py cat > /opt/archery/gunicorn.conf.py << 'EOF' import multiprocessing bind = '127.0.0.1:8001' bind_address = '127.0.0.1:8001' workers = 2 # 保守值:2 核 CPU 设 2,4 核设 3,切勿设为 cpu_count() worker_class = 'sync' worker_connections = 1000 timeout = 30 keepalive = 5 max_requests = 1000 max_requests_jitter = 100 preload = True daemon = False pidfile = '/var/run/archery.pid' logfile = '/var/log/archery/access.log' loglevel = 'info' access_log_format = '%(h)s %(l)s %(u)s %(t)s "%(r)s" %(s)s %(b)s "%(f)s" "%(a)s"' EOF参数说明:
workers=2:Archery 的审核引擎是 CPU 密集型,每个 worker 启动时加载全部规则库(约 120MB 内存),过多 worker 反而触发 OOM;实测 2 worker + 4GB 内存服务器可稳压 200 QPS 审核请求。max_requests=1000:强制 worker 处理 1000 个请求后重启,避免长连接导致的内存碎片堆积(Archery 的 SQL 解析器使用sqlparse,其 AST 构建过程有轻微内存泄漏)。preload=True:确保所有 worker 加载同一份规则缓存,避免每个 worker 单独解析规则 JSON 导致启动延迟翻倍。
3.2 配置 nginx:静态资源分离与反向代理
Archery 的前端资源(JS/CSS/图片)全放在archery/static/下,若由 gunicorn 直接提供,会严重拖慢响应速度。nginx 必须接管/static/路径,并对/api/路径做负载均衡:
# 安装 nginx(CentOS 7) sudo yum install -y nginx # 创建 Archery 专属配置 /etc/nginx/conf.d/archery.conf cat > /etc/nginx/conf.d/archery.conf << 'EOF' upstream archery_backend { server 127.0.0.1:8001; } server { listen 80; server_name archery.your-company.com; # 静态资源由 nginx 直接服务,不经过 gunicorn location /static/ { alias /opt/archery/archery/static/; expires 1h; add_header Cache-Control "public, immutable"; } # API 请求转发给 gunicorn location /api/ { proxy_pass http://archery_backend; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; proxy_connect_timeout 30s; proxy_send_timeout 30s; proxy_read_timeout 30s; } # 前端路由 fallback(Vue Router history 模式) location / { root /opt/archery/archery/dist; try_files $uri $uri/ /index.html; } } EOF # 启动 nginx 并设为开机自启 sudo systemctl enable nginx sudo systemctl start nginx3.3 systemd 服务定义:进程守护与日志集成
用 systemd 替代nohup gunicorn ... &,实现优雅关闭(SIGTERM)、崩溃自动重启、日志统一收集:
# 创建服务文件 /etc/systemd/system/archery.service cat > /etc/systemd/system/archery.service << 'EOF' [Unit] Description=Archery SQL Audit Platform After=network.target mysql.service [Service] Type=simple User=archery Group=archery WorkingDirectory=/opt/archery/archery Environment="PATH=/opt/archery/venv/bin" ExecStart=/opt/archery/venv/bin/gunicorn --config /opt/archery/gunicorn.conf.py archery.wsgi:application Restart=always RestartSec=10 KillSignal=SIGTERM TimeoutStopSec=60 StandardOutput=journal StandardError=journal SyslogIdentifier=archery [Install] WantedBy=multi-user.target EOF # 创建运行用户并授权 sudo useradd -r -s /sbin/nologin archery sudo chown -R archery:archery /opt/archery sudo chmod 755 /opt/archery sudo mkdir -p /var/log/archery sudo chown archery:archery /var/log/archery # 启用并启动服务 sudo systemctl daemon-reload sudo systemctl enable archery sudo systemctl start archery # 查看日志(实时跟踪启动过程) sudo journalctl -u archery -f注意:
WorkingDirectory必须设为/opt/archery/archery(即 manage.py 所在目录),否则archery.wsgi:application无法定位 settings 模块;Environment="PATH=..."是关键,否则 systemd 无法找到gunicorn可执行文件。
4. 关键功能落地:SQL 审核、Binlog 闪回、PT-Archiver 归档三件套
Archery 的核心价值不在 UI,而在它把 DBA 日常三大高频操作——SQL 审核、误删回滚、历史数据归档——封装成开箱即用的 Web 功能。这三块必须单独验证,否则等于没装。
4.1 SQL 审核:不只是语法检查,而是规则引擎驱动的风险拦截
Archery 的审核不是正则匹配,而是基于sqlparse构建 AST,再遍历节点执行规则。默认规则集(archery/sql/rules/)包含 37 条,覆盖「无 WHERE 的 UPDATE/DELETE」「未走索引的 LIKE 查询」「大表 ALTER 不带 ALGORITHM=INPLACE」等硬性红线。启用方式如下:
# 登录 Web 控制台(http://your-server-ip),用 admin/admin 登录 # 进入「系统管理」→「审核配置」→「审核规则」 # 勾选「启用审核」,并设置: # - 审核级别:高危(阻断)、中危(告警)、低危(提示) # - 规则组:MySQL 8.0(自动适配 MySQL 8.0 的新特性如隐藏索引、角色权限) # - 白名单 IP:填开发机 IP 段(如 192.168.10.0/24),非白名单提交直接拒绝实测案例:提交以下 SQL
UPDATE users SET status=0;Archery 会立即返回错误:
【高危】UPDATE 语句缺少 WHERE 条件,禁止执行。
若改为:UPDATE users SET status=0 WHERE id < 100;则进入下一步——索引检查。若
users.id无索引,会提示:【中危】WHERE 条件字段 id 未命中索引,预计影响行数 > 10000,建议添加索引。
4.2 Binlog 闪回:用 Binlog2SQL 实现秒级误删恢复
Archery 内置 Binlog2SQL 的 Web 封装,但需手动配置 MySQL 的 binlog 格式与权限。这是最易踩坑的模块,务必按以下步骤操作:
-- 1. 确保 MySQL 开启 ROW 格式 binlog(STATEMENT 格式无法闪回) SET GLOBAL binlog_format = 'ROW'; -- 永久生效:在 /etc/my.cnf 的 [mysqld] 段添加 # binlog_format = ROW # binlog_row_image = FULL -- 2. 创建专用闪回账号(最小权限原则) CREATE USER 'archery_flashback'@'localhost' IDENTIFIED BY 'FlashPass456!'; GRANT SELECT ON *.* TO 'archery_flashback'@'localhost'; GRANT REPLICATION SLAVE ON *.* TO 'archery_flashback'@'localhost'; GRANT REPLICATION CLIENT ON *.* TO 'archery_flashback'@'localhost'; FLUSH PRIVILEGES;# 3. 在 Archery Web 界面配置 MySQL 实例 # 「实例管理」→「添加实例」→ 填入: # - 实例名称:prod-mysql-8033 # - 主机地址:127.0.0.1 # - 端口:3306 # - 用户名:archery_flashback # - 密码:FlashPass456! # - 角色:从库(即使主库也要选从库,因 Binlog2SQL 只读) # - 其他保持默认验证闪回:在测试库执行
DELETE FROM test_table WHERE id=1;,然后在 Archery「闪回」页面选择该实例、时间范围(覆盖删除时间)、表名,点击「生成回滚 SQL」——输出结果应为INSERT INTO test_table (id, name) VALUES (1, 'xxx');。若返回空或报错No binlog found,90% 是binlog_row_image != FULL或账号无REPLICATION SLAVE权限。
4.3 PT-Archiver 归档:自动清理大表,不锁表、不丢数据
Archery 将 Percona Toolkit 的pt-archiver封装为定时任务,支持按主键分片归档、保留 N 天、归档到另一张表或另一库。配置前需确保pt-archiver已安装(非 Python 包,是 Perl 脚本):
# 安装 pt-archiver(CentOS 7) sudo yum install -y perl-DBI perl-DBD-MySQL perl-Time-HiRes perl-TermReadKey # 测试是否可用 pt-archiver --version # 应输出 3.3.3 或更高 # 在 Archery Web 界面配置归档任务 # 「归档管理」→「添加归档任务」→ 填入: # - 源实例:prod-mysql-8033(必须是主库,因需写操作) # - 源表:logs_2024 # - 目标表:logs_archive_2024(可同库不同名,或跨库) # - 归档条件:`create_time < DATE_SUB(NOW(), INTERVAL 90 DAY)` # - 每次归档行数:5000(避免单次事务过大) # - 限流:--sleep 0.1(每归档 5000 行休眠 0.1 秒,降低主库压力) # - 启用:勾选「启用」并设置 Cron 表达式(如 `0 2 * * *` 每日凌晨 2 点执行)关键参数说明:
--where条件必须能走索引,否则pt-archiver会全表扫描,导致主库 IO 暴涨;--limit 5000是安全阈值,超过 10000 行易触发 MySQLmax_allowed_packet错误;--sleep 0.1是血泪经验:某次未设 sleep,归档任务在凌晨 2 点瞬间打满主库 100% IO,导致线上交易超时。
5. 避坑指南:生产环境踩过的 5 个真实坑与解法
Archery 文档写得简略,但真实部署时,80% 的问题来自环境细节。以下是我们在 3 个不同客户现场反复遇到的 5 个典型问题,按现象 → 原因 → 解决的结构整理,避免你重蹈覆辙。
5.1 现象:Web 页面显示 “502 Bad Gateway”,nginx 日志报connect() failed (111: Connection refused) while connecting to upstream
原因:gunicorn 进程未启动,或启动后立即崩溃退出。常见于settings.py中数据库密码错误、MySQL 服务未运行、或archery/settings.py里DEBUG = True未关(生产环境必须设为False,否则 Django 会禁用ALLOWED_HOSTS校验,导致 gunicorn 拒绝响应)。
解决:
sudo systemctl status archery查看服务状态,若为active (exited),说明启动失败;sudo journalctl -u archery -n 50查看最后 50 行日志,重点找OperationalError(数据库连接失败)或ImproperlyConfigured(settings 配置错误);- 检查
/opt/archery/archery/settings.py,确认DEBUG = False且ALLOWED_HOSTS = ['archery.your-company.com', '192.168.10.100'](填你的域名或 IP); - 手动执行
source /opt/archery/venv/bin/activate && cd /opt/archery/archery && python manage.py db init,验证数据库连接是否通。
5.2 现象:SQL 审核通过,但执行时报错ERROR 1054 (42S22): Unknown column 'xxx' in 'field list'
原因:Archery 的审核引擎只解析 SQL 文本,不连接数据库校验字段是否存在。当开发提交的 SQL 引用了不存在的列,审核阶段无法发现,直到真正执行才报错。
解决:
- 短期:在「审核配置」中启用「执行前校验」开关(v4.2.0+ 支持),该功能会在审核通过后、执行前,用目标库账号执行
EXPLAIN或SELECT 1 FROM table LIMIT 0预检字段; - 长期:推动开发使用 IDE(如 DataGrip)的 SQL 语法检查插件,在编码阶段就发现字段错误,而非依赖上线前审核。
5.3 现象:Binlog 闪回页面报错Can't find any binlog files,但SHOW BINARY LOGS显示有文件
原因:MySQL 的log_bin_basename路径与 Archery 配置的binlog_path不一致。Archery 默认读取/var/lib/mysql下的 binlog,但若 MySQL 配置了log_bin = /data/mysql/binlog/mysql-bin,则 Archery 找不到文件。
解决:
- 登录 MySQL 执行
SHOW VARIABLES LIKE 'log_bin_basename';,得到实际路径(如/data/mysql/binlog/mysql-bin); - 在 Archery Web 界面「实例管理」→ 编辑对应实例 → 「高级配置」→ 填入
binlog_path = /data/mysql/binlog(注意:只填目录,不带文件名); - 重启 archery 服务:
sudo systemctl restart archery。
5.4 现象:PT-Archiver 归档任务执行后,源表数据清空,但目标表无数据
原因:pt-archiver默认使用--bulk-insert模式,该模式要求目标表存在且结构完全一致。若目标表不存在,或字段顺序/类型不匹配(如源表created_at DATETIME,目标表created_at TIMESTAMP),pt-archiver会静默失败。
解决:
- 执行归档前,手动创建目标表,SQL 用
SHOW CREATE TABLE source_table\G复制,再替换表名; - 在 Archery 归档任务配置中,取消勾选「自动建表」,改用「手动建表」;
- 添加
--dry-run参数测试:在服务器上手动执行pt-archiver --source h=127.0.0.1,D=test,t=logs_2024 --dest h=127.0.0.1,D=test,t=logs_archive_2024 --where "id<100" --dry-run,观察输出是否显示INSERT INTO ... SELECT ...。
5.5 现象:用户登录后,右上角显示 “Anonymous” 而非用户名,且无法退出
原因:Django 的 session backend 配置错误。Archery 默认用数据库存 session,但若settings.py中SESSION_ENGINE = 'django.contrib.sessions.backends.cache'且未配置 Redis,则 session 无法持久化,每次请求都新建匿名 session。
解决:
- 检查
/opt/archery/archery/settings.py,确认SESSION_ENGINE = 'django.contrib.sessions.backends.db'; - 确保已执行
python manage.py migrate(该命令会创建django_session表); - 若坚持用 cache,需安装 Redis 并配置:
CACHES = { 'default': { 'BACKEND': 'django.core.cache.backends.redis.RedisCache', 'LOCATION': 'redis://127.0.0.1:6379/1', } } SESSION_ENGINE = 'django.contrib.sessions.backends.cache'
6. 进阶技巧:让 Archery 真正融入你的研发流程
装完 Archery 只是起点,让它成为研发流程的“守门人”,需要几个关键动作。我见过太多团队装完就扔在角落,半年后发现审核工单为 0——不是工具不好,是没把它嵌进流程里。
6.1 Git Hook 拦截:在代码提交前就卡住危险 SQL
与其等开发提工单再审核,不如在git commit时就扫描 SQL 文件。我们用 pre-commit hook +sqlparse写了个轻量检查器,放在项目根目录.pre-commit-config.yaml:
repos: - repo: local hooks: - id: sql-check name: Check SQL safety before commit entry: bash -c 'grep -r "UPDATE.*SET.*WHERE\|DELETE.*FROM.*WHERE" --include="*.sql" . | grep -q "." && echo "❌ Found unsafe SQL (UPDATE/DELETE without WHERE). Fix it!" && exit 1 || echo "✅ SQL looks safe."' language: system types: [sql]效果:开发
git commit -m "add init data"时,若当前目录下有init.sql包含UPDATE users SET status=1;,commit 直接被拒绝,并打印错误信息。这比 Web 审核早了至少 3 个环节(写代码 → 提交 → 推送 → 提工单 → 审核),把风险左移。
6.2 审核规则定制:用 Python 写一条专属规则
Archery 允许在archery/sql/rules/下新增.py文件,定义自己的规则。比如,你公司规定所有INSERT必须指定字段名(禁止INSERT INTO t VALUES (...)),可以这样写:
# archery/sql/rules/require_column_names.py from archery.sql.utils import get_object_name from archery.sql.rules import BaseRule class RequireColumnNames(BaseRule): """ 【高危】INSERT 语句必须显式指定字段名,禁止使用 INSERT INTO t VALUES (...) """ def match(self, statement): if not statement.is_insert(): return False # 检查是否有括号内的字段列表,如 INSERT INTO t (id,name) VALUES ... if statement.tokens[3].is_group and statement.tokens[3].parenthesis: return False return True def message(self): return "INSERT 必须指定字段名,例如:INSERT INTO t (id, name) VALUES (1, 'a');"注意:规则文件名必须以
.py结尾,类名首字母大写,且继承BaseRule;match()返回True表示触发规则。写完后重启 Archery 服务即可生效。
6.3 数据库变更追踪表:用 Archery 的 audit_log 表做 DevOps 审计
Archery 的audit_log表(archery.audit_log)记录每一次审核、执行、闪回的操作,包含user_id、sql_content、status、create_time。我们把它接入 ELK,配置 Kibana 看板,每天自动生成《数据库变更日报》:
| 时间 | 操作人 | SQL 类型 | 影响行数 | 状态 | 耗时 |
|---|---|---|---|---|---|
| 09:23 | dev-zhang | UPDATE | 12 | success | 0.8s |
| 14:05 | ops-li | ALTER TABLE | 0 | blocked | 0.2s |
实现方式:用 Logstash 的
jdbcinput 插件,每 5 分钟查一次audit_log表,过滤create_time > NOW() - INTERVAL 1 DAY,输出到 Elasticsearch。这样,CTO 不用问 DBA,直接看 Kibana 就知道今天谁改了什么、有没有高危操作被拦截。
我带过的三个团队,最早那个是手工维护 Excel 变更记录,后来用 Archery + ELK,上线后第一个月就发现 2 起开发误删表事件被自动拦截,挽回了至少 2 天的数据恢复成本。工具不会自己发光,得你把它焊进流程里。希望帮到你。
本文还有配套的精品资源,点击获取