Files
hfb_sys/docs/stress-test-guide.md
T
2026-06-05 22:01:14 +08:00

13 KiB
Raw Blame History

压力测试指南

本文档提供完整的压力测试方案,用于评估系统在大数据量和高并发场景下的性能表现。

一、测试环境准备

1.1 硬件配置建议

最低配置:

  • CPU: 4核
  • 内存: 8GB
  • 磁盘: SSD 50GB

推荐配置:

  • CPU: 8核+
  • 内存: 16GB+
  • 磁盘: SSD 100GB+
  • MySQL: 独立部署,开启慢查询日志

1.2 数据库优化配置

deploy/docker-compose.dev.yml 中调整 MySQL 配置:

services:
  mysql:
    environment:
      - MYSQL_ROOT_PASSWORD=secret
    command:
      - --max_connections=500
      - --innodb_buffer_pool_size=2G
      - --innodb_log_file_size=512M
      - --slow_query_log=1
      - --slow_query_log_file=/var/log/mysql/slow.log
      - --long_query_time=0.5

1.3 后端配置优化

backend/.env 中调整:

# 数据库连接池
DB_MAX_OPEN_CONNS=100
DB_MAX_IDLE_CONNS=20
DB_CONN_MAX_LIFETIME=3600

# Redis配置
REDIS_POOL_SIZE=50

# 日志级别(压测时降低日志输出)
LOG_LEVEL=warn

# Gin模式
APP_ENV=production

二、生成测试数据

2.1 执行数据生成脚本

# 连接到数据库容器
docker exec -i hfb-mysql mysql -uhfb -psecret hfb_sys < scripts/load_test_data.sql

2.2 数据规模说明

该脚本会生成:

  • 10,000 个用户(90%已实名)
  • 50,000 个租号商品
  • 30,000 个订单(包含各种状态)
  • 100,000 条钱包流水
  • 50,000 条聊天消息

2.3 验证数据生成

-- 查看数据统计
SELECT '用户数' as item, COUNT(*) as count FROM users
UNION ALL
SELECT '商品数', COUNT(*) FROM rental_listings
UNION ALL
SELECT '订单数', COUNT(*) FROM rental_orders
UNION ALL
SELECT '钱包流水', COUNT(*) FROM wallet_ledger;

2.4 自定义数据量

修改脚本底部的调用参数:

-- 根据需要调整数量
CALL generate_users(50000);      -- 生成5万用户
CALL generate_listings(200000);  -- 生成20万商品
CALL generate_orders(100000);    -- 生成10万订单
CALL generate_wallet_ledger(500000); -- 生成50万流水

三、索引优化验证

3.1 检查现有索引

-- 查看订单表索引
SHOW INDEX FROM rental_orders;

-- 查看钱包流水索引
SHOW INDEX FROM wallet_ledger;

-- 查看商品表索引
SHOW INDEX FROM rental_listings;

3.2 分析慢查询

-- 分析商品列表查询
EXPLAIN SELECT * FROM rental_listings 
WHERE status = 'active' 
  AND review_status = 'approved' 
ORDER BY published_at DESC 
LIMIT 20;

-- 分析用户订单查询
EXPLAIN SELECT * FROM rental_orders 
WHERE renter_id = 1234 
  AND status = 'active'
ORDER BY created_at DESC;

-- 分析钱包流水查询
EXPLAIN SELECT * FROM wallet_ledger 
WHERE user_id = 1234 
  AND biz_type = 'rent_payment'
ORDER BY created_at DESC 
LIMIT 50;

3.3 添加缺失索引(如果需要)

-- 示例:为常用查询组合添加联合索引
ALTER TABLE rental_orders 
ADD INDEX idx_status_handoff_created (status, handoff_status, created_at);

-- 为管理后台查询优化
ALTER TABLE wallet_ledger 
ADD INDEX idx_created_biz_type (created_at, biz_type);

四、压力测试执行

4.1 使用 Go 压测工具

cd scripts

# 编译压测工具
go build -o stress_test stress_test.go

# 场景1: 商品列表查询(高频读)
./stress_test -url http://localhost:8080 -c 50 -d 60 -s list_listings

# 场景2: 订单创建(写操作)
./stress_test -url http://localhost:8080 -c 20 -d 60 -s create_order

# 场景3: 钱包流水查询
./stress_test -url http://localhost:8080 -c 30 -d 60 -s wallet

# 场景4: 混合场景(模拟真实流量)
./stress_test -url http://localhost:8080 -c 100 -d 300 -s mixed

4.2 使用 Apache Bench (ab)

# 简单的商品列表查询压测
ab -n 10000 -c 100 http://localhost:8080/api/listings?page=1&page_size=20

# 健康检查接口压测
ab -n 50000 -c 200 http://localhost:8080/health

4.3 使用 wrk

# 安装 wrk
brew install wrk  # macOS
# sudo apt install wrk  # Ubuntu

# 基础压测
wrk -t4 -c100 -d60s http://localhost:8080/api/listings

# 使用脚本进行复杂场景测试
wrk -t4 -c100 -d60s -s scripts/wrk_scenario.lua http://localhost:8080

创建 scripts/wrk_scenario.lua

-- 模拟不同的查询参数
counter = 0

request = function()
   counter = counter + 1
   page = (counter % 10) + 1
   path = "/api/listings?page=" .. page .. "&page_size=20"
   return wrk.format("GET", path)
end

五、性能监控

5.1 数据库性能监控

-- 实时查看正在执行的查询
SHOW FULL PROCESSLIST;

-- 查看慢查询日志
docker exec hfb-mysql tail -f /var/log/mysql/slow.log

-- 查看表锁情况
SHOW OPEN TABLES WHERE In_use > 0;

-- 查看InnoDB状态
SHOW ENGINE INNODB STATUS;

-- 查看连接数
SHOW STATUS LIKE 'Threads_connected';
SHOW STATUS LIKE 'Max_used_connections';

5.2 应用性能监控

在压测期间,监控后端日志:

# 实时查看后端日志
tail -f backend/logs/app-*.log | grep -E "ERROR|WARN|latency"

# 监控容器资源使用
docker stats hfb-backend hfb-mysql hfb-redis

5.3 系统资源监控

# CPU和内存使用
top -p $(pgrep -f "go run")

# 网络连接数
netstat -an | grep :8080 | wc -l

# 查看打开的文件描述符
lsof -p $(pgrep -f "go run") | wc -l

六、性能指标基准

6.1 响应时间目标

接口类型 P50 P95 P99
商品列表查询 < 50ms < 100ms < 200ms
订单详情查询 < 30ms < 80ms < 150ms
钱包流水查询 < 40ms < 100ms < 200ms
创建订单 < 100ms < 300ms < 500ms
支付处理 < 200ms < 500ms < 1000ms

6.2 吞吐量目标

  • 读操作:单机 QPS > 1000
  • 写操作:单机 QPS > 200
  • 混合场景:单机 QPS > 500

6.3 数据库查询目标

  • 简单查询< 10ms
  • 联表查询< 50ms
  • 复杂聚合< 100ms

七、常见性能瓶颈与优化

7.1 数据库层面

问题1:商品列表查询慢

症状: SELECT * FROM rental_listings WHERE status = 'active' 耗时超过 100ms

优化方案:

-- 1. 添加覆盖索引
ALTER TABLE rental_listings 
ADD INDEX idx_status_review_published_cover (
    status, review_status, published_at, id, price, deposit_amount
);

-- 2. 避免 SELECT *,只查询需要的字段
SELECT id, owner_id, price, deposit_amount, published_at 
FROM rental_listings 
WHERE status = 'active' AND review_status = 'approved'
ORDER BY published_at DESC 
LIMIT 20;

问题2:用户订单分页查询慢

症状: 大偏移量分页(page > 100)性能下降

优化方案:

-- 使用游标分页代替 OFFSET
SELECT * FROM rental_orders 
WHERE renter_id = ? 
  AND id < ?  -- 上一页最后一条的ID
ORDER BY id DESC 
LIMIT 20;

在代码中实现:

// 使用游标分页
func (r *Repository) ListOrdersCursor(userID, lastID uint64, limit int) ([]Order, error) {
    query := `SELECT * FROM rental_orders 
              WHERE renter_id = ? AND id < ?
              ORDER BY id DESC LIMIT ?`
    
    if lastID == 0 {
        lastID = ^uint64(0) // Max uint64
    }
    
    // ...
}

问题3:钱包流水查询慢(10万+数据)

优化方案:

-- 1. 确保有复合索引
ALTER TABLE wallet_ledger 
ADD INDEX idx_user_created_desc (user_id, created_at DESC);

-- 2. 分区表(适用于超大数据量)
ALTER TABLE wallet_ledger 
PARTITION BY RANGE (YEAR(created_at)) (
    PARTITION p2024 VALUES LESS THAN (2025),
    PARTITION p2025 VALUES LESS THAN (2026),
    PARTITION p2026 VALUES LESS THAN (2027),
    PARTITION p_future VALUES LESS THAN MAXVALUE
);

-- 3. 归档历史数据
CREATE TABLE wallet_ledger_archive LIKE wallet_ledger;
INSERT INTO wallet_ledger_archive 
SELECT * FROM wallet_ledger 
WHERE created_at < DATE_SUB(NOW(), INTERVAL 6 MONTH);

7.2 应用层面

问题1N+1 查询问题

症状: 商品列表查询后,循环查询关联的账号信息

优化方案:

// 错误做法:N+1查询
for _, listing := range listings {
    account, _ := repo.GetAccount(listing.AccountID)
    listing.Account = account
}

// 正确做法:预加载
func (r *Repository) ListWithAccounts(filter ListingFilter) ([]Listing, error) {
    query := `
        SELECT 
            rl.*,
            ga.title as account_title,
            ga.rank_level,
            ga.server_region
        FROM rental_listings rl
        LEFT JOIN game_accounts ga ON rl.account_id = ga.id
        WHERE rl.status = ?
        ORDER BY rl.published_at DESC
        LIMIT ?
    `
    // ...
}

问题2:缓存缺失

优化方案:

// 为热点数据添加Redis缓存
func (s *Service) GetListing(id uint64) (*Listing, error) {
    cacheKey := fmt.Sprintf("listing:%d", id)
    
    // 1. 尝试从缓存读取
    if cached, err := s.redis.Get(ctx, cacheKey).Bytes(); err == nil {
        var listing Listing
        json.Unmarshal(cached, &listing)
        return &listing, nil
    }
    
    // 2. 缓存未命中,从数据库读取
    listing, err := s.repo.GetByID(id)
    if err != nil {
        return nil, err
    }
    
    // 3. 写入缓存
    data, _ := json.Marshal(listing)
    s.redis.Set(ctx, cacheKey, data, 10*time.Minute)
    
    return listing, nil
}

问题3:数据库连接池耗尽

优化方案:

// 在 database/mysql.go 中优化连接池配置
db.SetMaxOpenConns(100)           // 最大连接数
db.SetMaxIdleConns(20)            // 空闲连接数
db.SetConnMaxLifetime(time.Hour)  // 连接最大生命周期
db.SetConnMaxIdleTime(10 * time.Minute) // 空闲连接超时

7.3 Redis 优化

# 监控 Redis 性能
redis-cli --latency
redis-cli --stat

# 查看慢查询
redis-cli SLOWLOG GET 10

# 查看内存使用
redis-cli INFO memory

配置优化:

# 最大内存限制
maxmemory 2gb

# 内存淘汰策略
maxmemory-policy allkeys-lru

# 持久化配置(开发环境可以关闭以提升性能)
save ""
appendonly no

八、特定场景测试

8.1 订单高峰测试

模拟秒杀或活动高峰期:

# 同时创建1000个订单
./stress_test -url http://localhost:8080 -c 100 -d 10 -s create_order

预期检查:

  • 数据库连接池是否耗尽
  • 是否出现死锁
  • 钱包余额扣减是否正确(需要事务隔离)

8.2 聊天消息压测

# 模拟100个用户同时发送消息
./stress_test -url http://localhost:8080 -c 100 -d 60 -s chat

预期检查:

  • WebSocket 连接数限制
  • 消息写入速度
  • 未读消息计数准确性

8.3 大数据量查询

-- 测试后台钱包流水导出(大数据量)
SELECT * FROM wallet_ledger 
WHERE created_at >= '2024-01-01'
ORDER BY created_at DESC;

-- 超时检查
SET SESSION max_execution_time = 30000; -- 30秒超时

九、压测后清理

9.1 清理测试数据

-- 谨慎执行!会删除所有测试数据
DELETE FROM wallet_ledger WHERE id > 100;
DELETE FROM rental_orders WHERE id > 100;
DELETE FROM rental_listings WHERE id > 100;
DELETE FROM game_accounts WHERE id > 100;
DELETE FROM users WHERE id > 1000;

-- 重置自增ID
ALTER TABLE users AUTO_INCREMENT = 1001;
ALTER TABLE rental_orders AUTO_INCREMENT = 101;

9.2 恢复配置

# 恢复开发环境配置
cd backend
cp .env.example .env

# 重启服务
./scripts/dev.sh

十、持续监控建议

10.1 生产环境监控

推荐集成:

  • APM: New Relic / Datadog
  • 日志: ELK Stack / Grafana Loki
  • 监控: Prometheus + Grafana
  • 告警: PagerDuty / 企业微信

10.2 关键指标

应用层:

  • API 响应时间(P50/P95/P99
  • QPS / TPS
  • 错误率
  • 慢查询数量

数据库层:

  • 连接数
  • 慢查询数
  • 锁等待时间
  • InnoDB 缓存命中率

系统层:

  • CPU 使用率
  • 内存使用率
  • 磁盘 IO
  • 网络带宽

十一、性能优化 Checklist

  • 数据库索引覆盖所有常用查询
  • 消除 N+1 查询问题
  • 热点数据使用 Redis 缓存
  • 数据库连接池配置合理
  • 分页查询使用游标而非 OFFSET
  • 大数据量表考虑分区
  • 历史数据定期归档
  • 慢查询日志监控告警
  • 数据库读写分离(如适用)
  • CDN 加速静态资源

附录

A. 压测命令速查

# 启动测试环境
docker-compose -f deploy/docker-compose.dev.yml up -d
cd backend && go run ./cmd/api

# 生成测试数据
docker exec -i hfb-mysql mysql -uhfb -psecret hfb_sys < scripts/load_test_data.sql

# 执行压测
cd scripts
go build -o stress_test stress_test.go
./stress_test -url http://localhost:8080 -c 100 -d 60 -s mixed

# 监控性能
docker stats
docker exec hfb-mysql mysqladmin -uhfb -psecret processlist

B. 参考资料