强曰为道
与天地相似,故不违。知周乎万物,而道济天下,故不过。旁行而不流,乐天知命,故不忧.
文档目录

Sysbench 完全指南 / 第十二章:最佳实践

第十二章:最佳实践

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%+

扩展阅读