环境信息
| 机器IP | 组件描述 | 组件版本 | 环境信息 |
|---|---|---|---|
| 10.13.240.254 | pt-archiver | 3.6.0 | 服务机 |
| 10.13.222.3 | Mariadb | 10.6.21 | 数据库 |
安装pt-archiver
# 安装 Percona 官方仓库
sudo yum install https://repo.percona.com/yum/percona-release-latest.noarch.rpm
# 启用 Percona Toolkit 仓库
sudo percona-release enable tools release
# 安装 Percona Toolkit
sudo yum install percona-toolkit
# 查看版本
pt-archiver --version
# 查看帮助
pt-archiver --help
清理数据脚本
#!/bin/bash
# =====================================================
# 分库分表数据清理脚本 - 直接使用你的元数据表
# 数据来源:你提供的表信息
# =====================================================
# 连接信息配置
HOST="10.13.222.3"
PORT="3306"
USER="xxx"
PASSWORD="xxx"
CHARSET="utf8mb4"
# 清理参数配置
WHERE_CONDITION="ctime < '2025-01-01'"
LIMIT=1000
TXN_SIZE=500
SLEEP=1
PROGRESS=100000
# 日志配置
LOG_DIR="/var/log/pt-archiver"
DATE=$(date +%Y%m%d_%H%M%S)
LOG_FILE="${LOG_DIR}/cleanup_${DATE}.log"
SUMMARY_FILE="${LOG_DIR}/summary_${DATE}.txt"
# 创建日志目录
mkdir -p ${LOG_DIR}
echo "开始清理任务 - $(date)" | tee -a ${LOG_FILE}
echo "清理条件: ${WHERE_CONDITION}" | tee -a ${LOG_FILE}
echo "==================================" | tee -a ${LOG_FILE}
# =====================================================
# 直接从你提供的数据生成表列表
# 格式:database_name.table_name_table_number
# =====================================================
# 生成所有表的完整名称(映射关系)
TABLES=(
"database_0.table_0"
"database_1.table_1"
"database_2.table_2"
"database_3.table_3"
)
# 计数器
TOTAL_TABLES=${#TABLES[@]}
SUCCESS_COUNT=0
FAIL_COUNT=0
TOTAL_DELETED=0
CURRENT=0
echo "总共需要处理 ${TOTAL_TABLES} 张表" | tee -a ${LOG_FILE}
echo "==================================" | tee -a ${LOG_FILE}
# 开始时间
START_TIME=$(date +%s)
# 遍历所有表
for TABLE in "${TABLES[@]}"; do
CURRENT=$((CURRENT + 1))
# 拆分数据库名和表名
DB=$(echo $TABLE | cut -d'.' -f1)
TB=$(echo $TABLE | cut -d'.' -f2)
echo "[${CURRENT}/${TOTAL_TABLES}] 处理表: ${DB}.${TB} - $(date)" | tee -a ${LOG_FILE}
# 检查表是否存在
TABLE_EXISTS=$(mysql -h${HOST} -P${PORT} -u${USER} -p${PASSWORD} -N -e "SELECT COUNT(*) FROM information_schema.tables WHERE table_schema='${DB}' AND table_name='${TB}'" 2>/dev/null)
if [ "${TABLE_EXISTS}" -eq 0 ]; then
echo "⚠️ 表 ${DB}.${TB} 不存在,跳过" | tee -a ${LOG_FILE}
continue
fi
# 统计要删除的数据量
COUNT=$(mysql -h${HOST} -P${PORT} -u${USER} -p${PASSWORD} -D${DB} -N -e "SELECT COUNT(*) FROM ${TB} WHERE ${WHERE_CONDITION}" 2>/dev/null)
echo "待删除数据: ${COUNT} 行" | tee -a ${LOG_FILE}
if [ "${COUNT}" -gt 0 ]; then
# 执行清理
pt-archiver \
--source h=${HOST},P=${PORT},u=${USER},p=${PASSWORD},D=${DB},t=${TB} \
--purge \
--where "${WHERE_CONDITION}" \
--limit ${LIMIT} \
--txn-size ${TXN_SIZE} \
--sleep ${SLEEP} \
--progress ${PROGRESS} \
--statistics \
--no-check-charset \
--why-quit \
--charset ${CHARSET} \
>> ${LOG_FILE} 2>&1
# 检查执行结果
if [ $? -eq 0 ]; then
echo "✅ 表 ${DB}.${TB} 清理成功,删除 ${COUNT} 行" | tee -a ${LOG_FILE}
SUCCESS_COUNT=$((SUCCESS_COUNT + 1))
TOTAL_DELETED=$((TOTAL_DELETED + COUNT))
else
echo "❌ 表 ${DB}.${TB} 清理失败" | tee -a ${LOG_FILE}
FAIL_COUNT=$((FAIL_COUNT + 1))
fi
else
echo "表 ${DB}.${TB} 没有需要清理的数据" | tee -a ${LOG_FILE}
SUCCESS_COUNT=$((SUCCESS_COUNT + 1))
fi
# 每张表之间添加短暂间隔
sleep 1
done
# 计算耗时
END_TIME=$(date +%s)
DURATION=$((END_TIME - START_TIME))
# 输出汇总信息
echo "==================================" | tee -a ${LOG_FILE} ${SUMMARY_FILE}
echo "清理完成 - $(date)" | tee -a ${LOG_FILE} ${SUMMARY_FILE}
echo "总处理表数: ${TOTAL_TABLES}" | tee -a ${LOG_FILE} ${SUMMARY_FILE}
echo "成功表数: ${SUCCESS_COUNT}" | tee -a ${LOG_FILE} ${SUMMARY_FILE}
echo "失败表数: ${FAIL_COUNT}" | tee -a ${LOG_FILE} ${SUMMARY_FILE}
echo "总计删除行数: ${TOTAL_DELETED}" | tee -a ${LOG_FILE} ${SUMMARY_FILE}
echo "总耗时: ${DURATION} 秒" | tee -a ${LOG_FILE} ${SUMMARY_FILE}
echo "详细日志请查看: ${LOG_FILE}"
echo "汇总信息请查看: ${SUMMARY_FILE}"
参数解释
1. --source - 数据源连接
bash
--source h=${HOST},P=${PORT},u=${USER},p=${PASSWORD},D=${DB},t=${TB}
h: 数据库主机IP
P: 端口号
u: 用户名
p: 密码
D: 数据库名
t: 表名
2. --purge - 只删除,不归档
bash
--purge
作用: 只删除数据,不同时归档到其他表
不加的话: 默认会尝试将删除的数据插入到 --dest 指定的表
适用场景: 直接清理过期数据,不需要备份
3. --where - 删除条件
bash
--where "ctime < '2025-01-01'"
作用: 指定要删除哪些数据
实际执行:
sql
-- 每次循环实际执行的查询
SELECT * FROM table WHERE ctime < '2025-01-01' ORDER BY id LIMIT ${LIMIT}
⚠️ 重要: ctime字段必须有索引,否则全表扫描
4. --limit - 每批获取的行数
bash
--limit 2000
作用: 每次 SELECT 查询获取多少行
执行流程:
bash
第1批: SELECT ... LIMIT 2000
第2批: SELECT ... WHERE id > 上一批最大id LIMIT 2000
第3批: SELECT ... WHERE id > 第二批最大id LIMIT 2000
...
5. --txn-size - 事务大小
bash
--txn-size 500
作用: 每删除多少行提交一次事务
执行流程:
bash
BEGIN;
DELETE WHERE id = 1; # 第1条
DELETE WHERE id = 2; # 第2条
... # 直到500条
COMMIT; # 提交
为什么小于limit: 即使一批获取2000条,也分成4次事务提交,避免大事务
6. --sleep - 休眠时间
bash
--sleep 1
作用: 每批数据处理完成后,暂停1秒
目的:
- 给主从复制留出同步时间
- 降低磁盘IO压力
- 让数据库有喘息机会
7. --progress - 进度显示
bash
--progress 100000
作用: 每处理10万行输出一次进度
输出示例:
bash
pt-archiver: 100000 rows affected (1.2 sec)
pt-archiver: 200000 rows affected (2.5 sec)
pt-archiver: 300000 rows affected (3.8 sec)
8. --statistics - 统计信息
bash
--statistics
作用: 结束时输出详细统计信息
输出示例:
bash
Started at 2024-01-15T10:00:00, ended at 2024-01-15T10:30:00
Source: D=test,t=users
SELECT 50000 rows
DELETE 50000 rows
Rows affected per second: 27.78
...
9. --no-check-charset - 跳过字符集检查
bash
--no-check-charset
作用: 不检查源和目标的字符集是否一致
10. --why-quit - 退出原因
bash
--why-quit
作用: 程序退出时显示原因
输出示例:
bash
Exiting because: 批处理完成 (正常结束)
Exiting because: 被用户中断 (Ctrl+C)
Exiting because: 连接超时 (异常)
11. --charset - 连接字符集
bash
--charset utf8mb4
作用: 指定数据库连接的字符集
执行案例
bash
开始清理任务 - Mon Mar 9 06:34:29 UTC 2026
清理条件: ctime < '2025-01-01'
==================================
总共需要处理 64 张表
==================================
[1/64] 处理表: database_0.table_0 - Mon Mar 9 06:34:29 UTC 2026
待删除数据: 488496 行
✅ 表 bill_intl_0.fp_stat_alt_0 清理成功,删除 488496 行
[2/64] 处理表: database_1.table_1 - Mon Mar 9 06:44:50 UTC 2026
待删除数据: 296442 行
结语
通过此次 pt-archiver 实践,我们成功实现了对 MariaDB 历史数据的高效、安全归档与清理。该工具以低负载、可控批量的方式,有效缓解了表膨胀问题,提升了数据库性能,并为后续数据生命周期管理提供了可复用的自动化方案。建议将其纳入定期运维任务,并持续监控执行效率与集群状态,以保障业务稳定运行。