第十二章:最佳实践
第十二章:最佳实践
12.1 测试方法论
12.1.1 性能测试的基本原则
| 原则 | 说明 |
|---|
| 可重复性 | 相同条件下多次测试,结果应一致 |
| 可对比性 | 只改变一个变量,其他条件保持一致 |
| 真实性 | 测试场景应尽量接近真实业务 |
| 渐进性 | 从低负载逐步增加,观察性能变化趋势 |
| 完整性 | 测试应包括准备、预热、测试、清理四个阶段 |
12.1.2 标准测试流程
┌─────────────────────────────────────────────────────────────┐
│ 性能测试标准流程 │
└─────────────────────────────────────────────────────────────┘
┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐
│ 1. 规划 │────→│ 2. 准备 │────→│ 3. 预热 │────→│ 4. 测试 │
│ │ │ │ │ │ │ │
│ • 明确目标│ │ • 环境配置│ │ • 短时间 │ │ • 正式运行│
│ • 选择测试│ │ • 数据准备│ │ • 预热缓存│ │ • 收集数据│
│ • 确定指标│ │ • 工具安装│ │ • 检查配置│ │ • 多次运行│
└──────────┘ └──────────┘ └──────────┘ └──────────┘
│
▼
┌──────────┐ ┌──────────┐ ┌──────────────────────────────┐
│ 7. 归档 │←────│ 6. 报告 │←────│ 5. 分析 │
│ │ │ │ │ │
│ • 保存结果│ │ • 总结结论│ │ • 汇总数据 │
│ • 记录配置│ │ • 提供建议│ │ • 对比基线 │
│ • 版本控制│ │ • 分享报告│ │ • 识别瓶颈 │
└──────────┘ └──────────┘ └──────────────────────────────┘
12.1.3 测试规划清单
在开始测试前,确保回答以下问题:
| 问题 | 示例回答 |
|---|
| 测试的目的是什么? | 验证 MySQL 8.0 升级后的性能变化 |
| 测试什么指标? | TPS、P95 延迟、QPS |
| 测试什么场景? | oltp_read_write,16 线程,16 张表 |
| 测试环境是什么? | ECS 8C16G,MySQL 8.0,SSD 云盘 |
| 什么是基线? | 升级前的 MySQL 5.7 测试结果 |
| 多长时间? | 300 秒,运行 3 次取平均 |
| 如何判断成功/失败? | TPS 不低于基线的 95% |
12.2 环境准备
12.2.1 系统层面准备
#!/bin/bash
# env_prepare.sh - 测试环境准备
echo "=== 系统信息收集 ==="
echo "操作系统: $(cat /etc/os-release | grep PRETTY_NAME | cut -d= -f2)"
echo "内核版本: $(uname -r)"
echo "CPU 型号: $(lscpu | grep 'Model name' | cut -d: -f2 | xargs)"
echo "CPU 核心: $(nproc)"
echo "内存大小: $(free -h | grep Mem | awk '{print $2}')"
echo "磁盘类型: $(lsblk -d -o NAME,ROTA,SIZE,MODEL | head -5)"
echo ""
echo "=== 关闭不必要的服务 ==="
# 避免测试期间干扰
sudo systemctl stop cron 2>/dev/null || true
sudo systemctl stop unattended-upgrades 2>/dev/null || true
sudo systemctl stop snapd 2>/dev/null || true
echo "Done"
echo ""
echo "=== 清空文件缓存 ==="
sudo sh -c 'echo 3 > /proc/sys/vm/drop_caches'
echo "Done"
echo ""
echo "=== 设置 CPU 调度器为 performance ==="
sudo cpupower frequency-set -g performance 2>/dev/null || echo "cpupower not available"
echo ""
echo "=== 禁用 Transparent HugePages(推荐数据库测试时禁用) ==="
echo never | sudo tee /sys/kernel/mm/transparent_hugepage/enabled > /dev/null 2>&1 || echo "THP not available"
echo ""
echo "=== 检查并设置文件描述符限制 ==="
echo "当前限制: $(ulimit -n)"
ulimit -n 65535
echo "新限制: $(ulimit -n)"
echo ""
echo "=== 检查 Swap ==="
echo "Swap: $(free -h | grep Swap | awk '{print $2}')"
echo "Swappiness: $(cat /proc/sys/vm/swappiness)"
echo ""
echo "=== 网络信息 ==="
ip -4 addr show | grep inet | head -3
echo ""
echo "=== 环境准备完成 ==="
12.2.2 MySQL 测试环境准备
#!/bin/bash
# mysql_env_prepare.sh - MySQL 测试环境准备
MYSQL_HOST="127.0.0.1"
MYSQL_USER="root"
MYSQL_PASS="secret"
echo "=== MySQL 版本 ==="
mysql -h $MYSQL_HOST -u $MYSQL_USER -p$MYSQL_PASS -e "SELECT VERSION();"
echo ""
echo "=== 关键参数 ==="
mysql -h $MYSQL_HOST -u $MYSQL_USER -p$MYSQL_PASS -e "
SHOW VARIABLES WHERE Variable_name IN (
'innodb_buffer_pool_size',
'innodb_buffer_pool_instances',
'innodb_flush_log_at_trx_commit',
'innodb_io_capacity',
'innodb_io_capacity_max',
'innodb_log_buffer_size',
'innodb_redo_log_capacity',
'max_connections',
'table_open_cache',
'table_open_cache_instances',
'thread_cache_size',
'sort_buffer_size',
'join_buffer_size',
'read_buffer_size',
'innodb_flush_method',
'sync_binlog'
);
"
echo ""
echo "=== Buffer Pool 命中率 ==="
mysql -h $MYSQL_HOST -u $MYSQL_USER -p$MYSQL_PASS -e "
SHOW STATUS LIKE 'Innodb_buffer_pool_read%';
"
echo ""
echo "=== 当前连接数 ==="
mysql -h $MYSQL_HOST -u $MYSQL_USER -p$MYSQL_PASS -e "
SHOW STATUS LIKE 'Threads_connected';
SHOW STATUS LIKE 'Max_used_connections';
"
12.2.3 PostgreSQL 测试环境准备
#!/bin/bash
# pgsql_env_prepare.sh
PG_HOST="127.0.0.1"
PG_USER="postgres"
echo "=== PostgreSQL 版本 ==="
psql -h $PG_HOST -U $PG_USER -c "SELECT version();"
echo ""
echo "=== 关键参数 ==="
psql -h $PG_HOST -U $PG_USER -c "
SELECT name, setting, unit
FROM pg_settings
WHERE name IN (
'shared_buffers', 'effective_cache_size', 'work_mem',
'maintenance_work_mem', 'max_connections', 'wal_buffers',
'checkpoint_completion_target', 'max_wal_size', 'random_page_cost',
'effective_io_concurrency', 'max_parallel_workers_per_gather'
) ORDER BY name;
"
echo ""
echo "=== Buffer Cache 命中率 ==="
psql -h $PG_HOST -U $PG_USER -c "
SELECT
sum(blks_hit) * 100.0 / NULLIF(sum(blks_hit) + sum(blks_read), 0) AS hit_ratio
FROM pg_stat_database
WHERE datname = 'sbtest';
"
12.3 预热策略
12.3.1 为什么要预热
| 缓存类型 | 冷启动表现 | 预热后表现 |
|---|
| OS 文件缓存 | 频繁磁盘 I/O | 内存命中,I/O 减少 |
| InnoDB Buffer Pool | 全磁盘读取 | 缓存命中,性能提升 |
| PostgreSQL Shared Buffers | 磁盘读取 | 缓存命中 |
| CPU 缓存 | 缓存未命中 | 缓存命中 |
| 连接池 | 连接建立开销 | 连接复用 |
12.3.2 预热脚本
#!/bin/bash
# warmup.sh - 预热脚本
DURATION=${1:-60} # 默认 60 秒
echo "=== 开始预热(${DURATION}秒) ==="
# CPU 预热
echo ">>> CPU 预热..."
sysbench cpu --threads=$(nproc) --time=10 run > /dev/null 2>&1
# 内存预热
echo ">>> 内存预热..."
sysbench memory --memory-block-size=1M --threads=4 --time=10 run > /dev/null 2>&1
# 数据库预热(OLTP 测试)
if [ -n "$MYSQL_HOST" ]; then
echo ">>> MySQL 预热..."
sysbench oltp_read_write \
--mysql-host=$MYSQL_HOST --mysql-user=$MYSQL_USER --mysql-password=$MYSQL_PASS \
--tables=${TABLES:-16} --table-size=${TABLE_SIZE:-1000000} \
--threads=16 --time=$DURATION \
run > /dev/null 2>&1
fi
if [ -n "$PG_HOST" ]; then
echo ">>> PostgreSQL 预热..."
sysbench oltp_read_write \
--db-driver=pgsql --pgsql-host=$PG_HOST --pgsql-user=$PG_USER --pgsql-password=$PG_PASS \
--tables=${TABLES:-16} --table-size=${TABLE_SIZE:-1000000} \
--threads=16 --time=$DURATION \
run > /dev/null 2>&1
fi
echo "=== 预热完成 ==="
12.3.3 验证预热效果
-- MySQL: 检查 Buffer Pool 命中率
SHOW STATUS LIKE 'Innodb_buffer_pool_read%';
-- 命中率 = 1 - (Innodb_buffer_pool_reads / Innodb_buffer_pool_read_requests)
-- 理想值 > 99%
-- PostgreSQL: 检查 Buffer Cache 命中率
SELECT
sum(blks_hit) * 100.0 / NULLIF(sum(blks_hit) + sum(blks_read), 0) AS hit_ratio
FROM pg_stat_database WHERE datname = 'sbtest';
-- 理想值 > 99%
12.4 结果收集与分析
12.4.1 自动化结果收集
#!/bin/bash
# collect_results.sh - 自动化结果收集
RESULT_DIR="./benchmark_$(date +%Y%m%d_%H%M%S)"
mkdir -p "$RESULT_DIR"
echo "=== 结果目录: $RESULT_DIR ==="
# 记录系统信息
cat > "$RESULT_DIR/system_info.txt" << EOF
日期: $(date)
主机名: $(hostname)
内核: $(uname -r)
CPU: $(lscpu | grep 'Model name' | cut -d: -f2 | xargs)
核心数: $(nproc)
内存: $(free -h | grep Mem | awk '{print $2}')
磁盘: $(lsblk -d -o NAME,SIZE,ROTA,MODEL | grep -v NAME)
EOF
# 记录数据库信息
if command -v mysql &> /dev/null; then
mysql -h 127.0.0.1 -u root -psecret -e "SELECT VERSION();" > "$RESULT_DIR/mysql_info.txt" 2>/dev/null
fi
# 运行测试并收集 JSON 结果
run_test() {
local test_name=$1
shift
echo ">>> 运行: $test_name"
sysbench "$@" --json="$RESULT_DIR/${test_name}.json" run
# 提取关键指标
local tps=$(jq '.transactions.per_second' "$RESULT_DIR/${test_name}.json")
local avg_latency=$(jq '.latency.avg' "$RESULT_DIR/${test_name}.json")
local p95=$(jq '.latency.percentile_95' "$RESULT_DIR/${test_name}.json")
echo " TPS: $tps, Avg Latency: ${avg_latency}ms, P95: ${p95}ms"
}
# 运行各种测试
run_test "cpu_t1" cpu --threads=1 --time=60
run_test "cpu_t8" cpu --threads=8 --time=60
run_test "memory_t4" memory --memory-block-size=1M --threads=4 --time=60
# 数据库测试
if [ -n "$MYSQL_HOST" ]; then
run_test "oltp_rw_t8" oltp_read_write \
--mysql-host=$MYSQL_HOST --mysql-user=$MYSQL_USER --mysql-password=$MYSQL_PASS \
--tables=16 --table-size=1000000 --threads=8 --time=300
run_test "oltp_rw_t16" oltp_read_write \
--mysql-host=$MYSQL_HOST --mysql-user=$MYSQL_USER --mysql-password=$MYSQL_PASS \
--tables=16 --table-size=1000000 --threads=16 --time=300
fi
# 生成摘要
echo ""
echo "=== 测试摘要 ==="
echo "结果保存在: $RESULT_DIR"
ls -la "$RESULT_DIR"/*.json
12.4.2 结果解析脚本
#!/bin/bash
# parse_results.sh - 解析 JSON 结果
RESULT_DIR=$1
if [ -z "$RESULT_DIR" ]; then
echo "Usage: $0 <result_dir>"
exit 1
fi
echo "========================================="
echo " Sysbench 测试结果摘要"
echo "========================================="
echo ""
printf "%-20s %10s %10s %10s %10s\n" "测试项" "TPS" "QPS" "Avg(ms)" "P95(ms)"
printf "%-20s %10s %10s %10s %10s\n" "-----" "---" "---" "-------" "-------"
for json_file in "$RESULT_DIR"/*.json; do
[ -f "$json_file" ] || continue
test_name=$(basename "$json_file" .json)
tps=$(jq '.transactions.per_second // 0' "$json_file" 2>/dev/null || echo "N/A")
qps=$(jq '.queries.per_second // 0' "$json_file" 2>/dev/null || echo "N/A")
avg=$(jq '.latency.avg // 0' "$json_file" 2>/dev/null || echo "N/A")
p95=$(jq '.latency.percentile_95 // 0' "$json_file" 2>/dev/null || echo "N/A")
printf "%-20s %10.2f %10.2f %10.2f %10.2f\n" "$test_name" "$tps" "$qps" "$avg" "$p95"
done
echo ""
echo "========================================="
12.5 结果解读
12.5.1 关键指标解读指南
| 指标 | 好 | 中等 | 差 | 说明 |
|---|
| TPS | 越高越好 | 取决于场景 | 低于基线 10% | 每秒事务数 |
| P95 延迟 | < 10ms | 10-50ms | > 100ms | 95% 请求的响应时间 |
| P99 延迟 | < 50ms | 50-200ms | > 500ms | 99% 请求的响应时间 |
| 平均延迟 | < 5ms | 5-20ms | > 50ms | 所有请求的平均响应时间 |
| 最大延迟 | < 100ms | 100-500ms | > 1000ms | 单个请求的最长响应时间 |
| 标准差 | < 平均值 10% | 平均值 10-50% | > 平均值 50% | 性能波动程度 |
12.5.2 性能瓶颈识别
┌─────────────────────────────────────────────────────────────┐
│ 性能瓶颈诊断流程 │
└─────────────────────────────────────────────────────────────┘
TPS 低 + CPU 高
├── CPU 密集型瓶颈
│ ├── 优化 SQL 查询
│ ├── 增加索引
│ └── 升级 CPU
└── 检查: top, mpstat
TPS 低 + 磁盘 I/O 高
├── I/O 密集型瓶颈
│ ├── 增加 Buffer Pool
│ ├── 优化查询(减少 I/O)
│ ├── 升级存储(SSD)
│ └── 增加内存(减少磁盘访问)
└── 检查: iostat, iotop
TPS 低 + 延迟高
├── 锁/等待瓶颈
│ ├── 检查锁等待
│ ├── 优化事务大小
│ ├── 减少锁冲突
│ └── 检查网络延迟
└── 检查: SHOW PROCESSLIST, SHOW ENGINE INNODB STATUS
TPS 低 + 资源空闲
├── 并发/连接瓶颈
│ ├── 增加 max_connections
│ ├── 使用连接池
│ ├── 调整 thread_cache_size
│ └── 检查应用端瓶颈
└── 检查: SHOW STATUS LIKE 'Threads%'
12.5.3 MySQL 性能诊断
-- 查看当前运行的查询
SELECT * FROM information_schema.processlist
WHERE command != 'Sleep' AND time > 1
ORDER BY time DESC;
-- 查看 InnoDB 状态
SHOW ENGINE INNODB STATUS\G
-- 查看锁等待
SELECT * FROM information_schema.innodb_lock_waits;
-- 查看索引使用情况
SELECT * FROM sys.schema_unused_indexes;
SELECT * FROM sys.schema_redundant_indexes;
-- 查看表统计
SELECT table_name, table_rows, data_length, index_length
FROM information_schema.tables
WHERE table_schema = 'sbtest'
ORDER BY data_length DESC;
12.6 测试报告生成
12.6.1 Markdown 报告模板
#!/bin/bash
# generate_report.sh - 生成 Markdown 测试报告
RESULT_DIR=$1
REPORT_FILE="$RESULT_DIR/report.md"
cat > "$REPORT_FILE" << 'HEADER'
# Sysbench 性能测试报告
## 测试环境
| 项目 | 详情 |
|------|------|
HEADER
echo "| 测试日期 | $(date '+%Y-%m-%d %H:%M:%S') |" >> "$REPORT_FILE"
echo "| 操作系统 | $(cat /etc/os-release | grep PRETTY_NAME | cut -d= -f2 | tr -d '"') |" >> "$REPORT_FILE"
echo "| 内核版本 | $(uname -r) |" >> "$REPORT_FILE"
echo "| CPU | $(lscpu | grep 'Model name' | cut -d: -f2 | xargs) |" >> "$REPORT_FILE"
echo "| CPU 核心 | $(nproc) |" >> "$REPORT_FILE"
echo "| 内存 | $(free -h | grep Mem | awk '{print $2}') |" >> "$REPORT_FILE"
echo "| Sysbench 版本 | $(sysbench --version 2>&1 | head -1) |" >> "$REPORT_FILE"
cat >> "$REPORT_FILE" << 'TABLE_HEADER'
## 测试结果
| 测试项 | TPS | QPS | 平均延迟(ms) | P95延迟(ms) | P99延迟(ms) |
|--------|-----|-----|-------------|------------|------------|
TABLE_HEADER
for json_file in "$RESULT_DIR"/*.json; do
[ -f "$json_file" ] || continue
test_name=$(basename "$json_file" .json)
tps=$(jq '.transactions.per_second // 0' "$json_file")
qps=$(jq '.queries.per_second // 0' "$json_file")
avg=$(jq '.latency.avg // 0' "$json_file")
p95=$(jq '.latency.percentile_95 // 0' "$json_file")
p99=$(jq '.latency.percentile_99 // 0' "$json_file")
echo "| $test_name | $tps | $qps | $avg | $p95 | $p99 |" >> "$REPORT_FILE"
done
cat >> "$REPORT_FILE" << 'FOOTER'
## 结论
1. (在此填写测试结论)
2. (在此填写性能建议)
3. (在此填写后续优化方向)
## 附录
### 测试命令
```bash
# CPU 测试
sysbench cpu --threads=8 --time=60 run
# OLTP 测试
sysbench oltp_read_write --tables=16 --table-size=1000000 --threads=16 --time=300 run
注意事项
- 测试期间无其他负载
- 数据库缓存已预热
- 每项测试运行 3 次取平均值
FOOTER
echo ""
echo “报告已生成: $REPORT_FILE”
### 12.6.2 HTML 报告生成
```bash
#!/bin/bash
# generate_html_report.sh
RESULT_DIR=$1
HTML_FILE="$RESULT_DIR/report.html"
cat > "$HTML_FILE" << 'EOF'
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Sysbench 性能测试报告</title>
<style>
body { font-family: Arial, sans-serif; margin: 20px; }
table { border-collapse: collapse; width: 100%; margin: 20px 0; }
th, td { border: 1px solid #ddd; padding: 8px; text-align: left; }
th { background-color: #4CAF50; color: white; }
tr:nth-child(even) { background-color: #f2f2f2; }
.good { color: green; font-weight: bold; }
.warning { color: orange; font-weight: bold; }
.bad { color: red; font-weight: bold; }
h1 { color: #333; }
h2 { color: #666; border-bottom: 2px solid #4CAF50; padding-bottom: 5px; }
</style>
</head>
<body>
<h1>Sysbench 性能测试报告</h1>
<h2>测试环境</h2>
<table>
<tr><th>项目</th><th>详情</th></tr>
<tr><td>测试日期</td><td>DATE_PLACEHOLDER</td></tr>
<tr><td>操作系统</td><td>OS_PLACEHOLDER</td></tr>
<tr><td>CPU</td><td>CPU_PLACEHOLDER</td></tr>
<tr><td>内存</td><td>MEM_PLACEHOLDER</td></tr>
</table>
<h2>测试结果</h2>
<table>
<tr>
<th>测试项</th>
<th>TPS</th>
<th>QPS</th>
<th>平均延迟(ms)</th>
<th>P95延迟(ms)</th>
<th>P99延迟(ms)</th>
</tr>
RESULT_ROWS
</table>
<h2>结论</h2>
<ul>
<li>(在此填写测试结论)</li>
</ul>
</body>
</html>
EOF
# 替换占位符
sed -i "s|DATE_PLACEHOLDER|$(date '+%Y-%m-%d %H:%M:%S')|" "$HTML_FILE"
sed -i "s|OS_PLACEHOLDER|$(cat /etc/os-release | grep PRETTY_NAME | cut -d= -f2 | tr -d '\"')|" "$HTML_FILE"
sed -i "s|CPU_PLACEHOLDER|$(lscpu | grep 'Model name' | cut -d: -f2 | xargs)|" "$HTML_FILE"
sed -i "s|MEM_PLACEHOLDER|$(free -h | grep Mem | awk '{print $2}')|" "$HTML_FILE"
# 生成结果行
ROWS=""
for json_file in "$RESULT_DIR"/*.json; do
[ -f "$json_file" ] || continue
test_name=$(basename "$json_file" .json)
tps=$(jq '.transactions.per_second // 0' "$json_file")
qps=$(jq '.queries.per_second // 0' "$json_file")
avg=$(jq '.latency.avg // 0' "$json_file")
p95=$(jq '.latency.percentile_95 // 0' "$json_file")
p99=$(jq '.latency.percentile_99 // 0' "$json_file")
ROWS="$ROWS<tr><td>$test_name</td><td>$tps</td><td>$qps</td><td>$avg</td><td>$p95</td><td>$p99</td></tr>\n"
done
sed -i "s|RESULT_ROWS|$ROWS|" "$HTML_FILE"
echo "HTML 报告已生成: $HTML_FILE"
12.7 常见陷阱与避坑指南
12.7.1 测试环境陷阱
| 陷阱 | 表现 | 解决方案 |
|---|
| 测试数据量太小 | Buffer Pool 100% 命中,结果偏高 | 数据集 > Buffer Pool |
| 测试时间太短 | 结果波动大 | 至少 5 分钟 |
| 只运行一次 | 偶然因素影响 | 至少运行 3 次 |
| 未预热 | 第一次测试结果偏低 | 先短时间预热 |
| 有其他负载 | 结果不稳定 | 关闭无关服务 |
| 使用 root 用户 | 安全风险 | 创建专用测试用户 |
| 未清理缓存 | 文件 I/O 测试不准 | 使用 O_DIRECT 或 drop_caches |
12.7.2 结果解读陷阱
| 陷阱 | 表现 | 正确做法 |
|---|
| 只看 TPS | 忽略延迟问题 | 同时关注 P95/P99 延迟 |
| 跨版本对比 | 不同 Sysbench 版本不可比 | 使用相同版本 |
| 忽略标准差 | 平均值掩盖了波动 | 关注 stddev |
| 绝对化数字 | 不同硬件结果不同 | 关注相对变化 |
| 忽略业务差异 | 标准测试 ≠ 真实业务 | 结合业务场景测试 |
12.7.3 数据库测试陷阱
| 陷阱 | 表现 | 解决方案 |
|---|
| prepare 后不预热 | 前几次测试结果偏低 | prepare 后先预热 |
| 多测试不重新 prepare | 数据分布变化 | 重要测试前重新 prepare |
| 忽略 autovacuum (PG) | 测试期间 VACUUM 干扰 | 监控或禁用 |
| 忽略 binlog (MySQL) | 写入测试受影响 | 评估是否需要 binlog |
| 连接数不足 | 报错或性能低 | 检查 max_connections |
| 隔离级别不同 | PG 和 MySQL 默认不同 | 统一隔离级别 |
12.8 性能基线管理
12.8.1 建立性能基线
#!/bin/bash
# establish_baseline.sh - 建立性能基线
BASELINE_DIR="./baseline/$(date +%Y%m%d)"
mkdir -p "$BASELINE_DIR"
echo "=== 建立性能基线 ==="
# 记录环境信息
cat > "$BASELINE_DIR/env.txt" << EOF
日期: $(date)
主机: $(hostname)
内核: $(uname -r)
CPU: $(lscpu | grep 'Model name' | cut -d: -f2 | xargs)
内存: $(free -h | grep Mem | awk '{print $2}')
EOF
# 运行基线测试
for i in 1 2 3; do
echo "--- 第 $i 次运行 ---"
sysbench cpu --threads=$(nproc) --time=60 \
--json="$BASELINE_DIR/cpu_run${i}.json" run
sysbench memory --memory-block-size=1M --threads=$(nproc) --time=60 \
--json="$BASELINE_DIR/mem_run${i}.json" run
done
# 计算平均值
echo ""
echo "=== 基线结果(3 次平均) ==="
cpu_avg=$(jq -s 'map(.cpu_speed.events_per_second) | add / length' "$BASELINE_DIR"/cpu_run*.json)
echo "CPU events/sec: $cpu_avg"
mem_avg=$(jq -s 'map(.total_number_of_events) | add / length' "$BASELINE_DIR"/mem_run*.json)
echo "Memory operations: $mem_avg"
# 保存基线
echo "$cpu_avg" > "$BASELINE_DIR/cpu_baseline.txt"
echo "$mem_avg" > "$BASELINE_DIR/mem_baseline.txt"
12.8.2 基线对比
#!/bin/bash
# compare_with_baseline.sh - 与基线对比
BASELINE_DIR=$1
CURRENT_RESULT=$2
THRESHOLD=${3:-5} # 默认阈值 5%
# 对比 CPU
cpu_baseline=$(cat "$BASELINE_DIR/cpu_baseline.txt")
cpu_current=$(jq '.cpu_speed.events_per_second' "$CURRENT_RESULT/cpu_run1.json")
cpu_diff=$(echo "scale=2; ($cpu_current - $cpu_baseline) / $cpu_baseline * 100" | bc)
echo "=== CPU 性能对比 ==="
echo "基线: $cpu_baseline"
echo "当前: $cpu_current"
echo "变化: ${cpu_diff}%"
if (( $(echo "$cpu_diff < -$THRESHOLD" | bc -l) )); then
echo "⚠️ 警告: CPU 性能下降超过 ${THRESHOLD}%!"
fi
12.9 持续性能监控
12.9.1 定期性能测试脚本
#!/bin/bash
# periodic_benchmark.sh - 定期性能测试
LOG_DIR="./benchmark_logs"
mkdir -p "$LOG_DIR"
LOG_FILE="$LOG_DIR/benchmark_$(date +%Y%m%d_%H%M%S).log"
{
echo "=== 性能测试开始: $(date) ==="
sysbench cpu --threads=$(nproc) --time=60 run 2>&1
echo ""
sysbench memory --memory-block-size=1M --threads=$(nproc) --time=60 run 2>&1
echo ""
if [ -n "$MYSQL_HOST" ]; then
sysbench oltp_read_write \
--mysql-host=$MYSQL_HOST --mysql-user=$MYSQL_USER --mysql-password=$MYSQL_PASS \
--tables=16 --table-size=1000000 --threads=16 --time=300 run 2>&1
fi
echo "=== 性能测试结束: $(date) ==="
} | tee "$LOG_FILE"
12.9.2 cron 定时任务
# 编辑 crontab
# crontab -e
# 每天凌晨 3 点运行性能测试
0 3 * * * /opt/scripts/periodic_benchmark.sh >> /var/log/benchmark.log 2>&1
# 每周一生成报告
0 4 * * 1 /opt/scripts/generate_report.sh /opt/results >> /var/log/benchmark_report.log 2>&1
12.10 总结与建议
12.10.1 性能测试检查清单
| 序号 | 检查项 | 状态 |
|---|
| 1 | 明确测试目标和指标 | ☐ |
| 2 | 记录测试环境信息 | ☐ |
| 3 | 关闭不必要的服务和进程 | ☐ |
| 4 | 清空文件缓存(文件 I/O 测试) | ☐ |
| 5 | 设置 CPU 调度器为 performance | ☐ |
| 6 | 创建测试数据库和用户 | ☐ |
| 7 | 准备足够的测试数据 | ☐ |
| 8 | 执行预热 | ☐ |
| 9 | 运行测试至少 3 次 | ☐ |
| 10 | 收集 JSON/CSV 结果 | ☐ |
| 11 | 检查结果一致性(标准差) | ☐ |
| 12 | 生成测试报告 | ☐ |
| 13 | 清理测试数据 | ☐ |
| 14 | 归档结果和配置 | ☐ |
12.10.2 性能优化路线图
┌─────────────────────────────────────────────────────────────┐
│ 性能优化路线图 │
└─────────────────────────────────────────────────────────────┘
Level 1: 快速优化(低成本,高收益)
├── 调整 innodb_buffer_pool_size
├── 添加缺失索引
├── 优化慢查询 SQL
└── 预期提升: 50-200%
Level 2: 中等优化(中等成本,中等收益)
├── 升级存储到 SSD
├── 增加内存
├── 调整 Redo Log 大小
└── 预期提升: 30-100%
Level 3: 深度优化(高成本,较高收益)
├── 架构优化(读写分离、分库分表)
├── 缓存层引入(Redis/Memcached)
├── 硬件升级(CPU/内存/存储)
└── 预期提升: 100-500%
Level 4: 架构重构(极高成本,极高收益)
├── 数据库迁移(MySQL → 分布式数据库)
├── 微服务化改造
├── 全链路优化
└── 预期提升: 500-1000%+
扩展阅读