607 lines
13 KiB
Markdown
607 lines
13 KiB
Markdown
# 压力测试指南
|
||
|
||
本文档提供完整的压力测试方案,用于评估系统在大数据量和高并发场景下的性能表现。
|
||
|
||
## 一、测试环境准备
|
||
|
||
### 1.1 硬件配置建议
|
||
|
||
**最低配置:**
|
||
- CPU: 4核
|
||
- 内存: 8GB
|
||
- 磁盘: SSD 50GB
|
||
|
||
**推荐配置:**
|
||
- CPU: 8核+
|
||
- 内存: 16GB+
|
||
- 磁盘: SSD 100GB+
|
||
- MySQL: 独立部署,开启慢查询日志
|
||
|
||
### 1.2 数据库优化配置
|
||
|
||
在 `deploy/docker-compose.dev.yml` 中调整 MySQL 配置:
|
||
|
||
```yaml
|
||
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` 中调整:
|
||
|
||
```bash
|
||
# 数据库连接池
|
||
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 执行数据生成脚本
|
||
|
||
```bash
|
||
# 连接到数据库容器
|
||
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 验证数据生成
|
||
|
||
```sql
|
||
-- 查看数据统计
|
||
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 自定义数据量
|
||
|
||
修改脚本底部的调用参数:
|
||
|
||
```sql
|
||
-- 根据需要调整数量
|
||
CALL generate_users(50000); -- 生成5万用户
|
||
CALL generate_listings(200000); -- 生成20万商品
|
||
CALL generate_orders(100000); -- 生成10万订单
|
||
CALL generate_wallet_ledger(500000); -- 生成50万流水
|
||
```
|
||
|
||
## 三、索引优化验证
|
||
|
||
### 3.1 检查现有索引
|
||
|
||
```sql
|
||
-- 查看订单表索引
|
||
SHOW INDEX FROM rental_orders;
|
||
|
||
-- 查看钱包流水索引
|
||
SHOW INDEX FROM wallet_ledger;
|
||
|
||
-- 查看商品表索引
|
||
SHOW INDEX FROM rental_listings;
|
||
```
|
||
|
||
### 3.2 分析慢查询
|
||
|
||
```sql
|
||
-- 分析商品列表查询
|
||
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 添加缺失索引(如果需要)
|
||
|
||
```sql
|
||
-- 示例:为常用查询组合添加联合索引
|
||
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 压测工具
|
||
|
||
```bash
|
||
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)
|
||
|
||
```bash
|
||
# 简单的商品列表查询压测
|
||
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
|
||
|
||
```bash
|
||
# 安装 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`:
|
||
|
||
```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 数据库性能监控
|
||
|
||
```sql
|
||
-- 实时查看正在执行的查询
|
||
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 应用性能监控
|
||
|
||
在压测期间,监控后端日志:
|
||
|
||
```bash
|
||
# 实时查看后端日志
|
||
tail -f backend/logs/app-*.log | grep -E "ERROR|WARN|latency"
|
||
|
||
# 监控容器资源使用
|
||
docker stats hfb-backend hfb-mysql hfb-redis
|
||
```
|
||
|
||
### 5.3 系统资源监控
|
||
|
||
```bash
|
||
# 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
|
||
|
||
**优化方案:**
|
||
|
||
```sql
|
||
-- 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)性能下降
|
||
|
||
**优化方案:**
|
||
|
||
```sql
|
||
-- 使用游标分页代替 OFFSET
|
||
SELECT * FROM rental_orders
|
||
WHERE renter_id = ?
|
||
AND id < ? -- 上一页最后一条的ID
|
||
ORDER BY id DESC
|
||
LIMIT 20;
|
||
```
|
||
|
||
在代码中实现:
|
||
|
||
```go
|
||
// 使用游标分页
|
||
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万+数据)
|
||
|
||
**优化方案:**
|
||
|
||
```sql
|
||
-- 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 应用层面
|
||
|
||
#### 问题1:N+1 查询问题
|
||
|
||
**症状:** 商品列表查询后,循环查询关联的账号信息
|
||
|
||
**优化方案:**
|
||
|
||
```go
|
||
// 错误做法: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:缓存缺失
|
||
|
||
**优化方案:**
|
||
|
||
```go
|
||
// 为热点数据添加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:数据库连接池耗尽
|
||
|
||
**优化方案:**
|
||
|
||
```go
|
||
// 在 database/mysql.go 中优化连接池配置
|
||
db.SetMaxOpenConns(100) // 最大连接数
|
||
db.SetMaxIdleConns(20) // 空闲连接数
|
||
db.SetConnMaxLifetime(time.Hour) // 连接最大生命周期
|
||
db.SetConnMaxIdleTime(10 * time.Minute) // 空闲连接超时
|
||
```
|
||
|
||
### 7.3 Redis 优化
|
||
|
||
```bash
|
||
# 监控 Redis 性能
|
||
redis-cli --latency
|
||
redis-cli --stat
|
||
|
||
# 查看慢查询
|
||
redis-cli SLOWLOG GET 10
|
||
|
||
# 查看内存使用
|
||
redis-cli INFO memory
|
||
```
|
||
|
||
**配置优化:**
|
||
|
||
```redis
|
||
# 最大内存限制
|
||
maxmemory 2gb
|
||
|
||
# 内存淘汰策略
|
||
maxmemory-policy allkeys-lru
|
||
|
||
# 持久化配置(开发环境可以关闭以提升性能)
|
||
save ""
|
||
appendonly no
|
||
```
|
||
|
||
## 八、特定场景测试
|
||
|
||
### 8.1 订单高峰测试
|
||
|
||
模拟秒杀或活动高峰期:
|
||
|
||
```bash
|
||
# 同时创建1000个订单
|
||
./stress_test -url http://localhost:8080 -c 100 -d 10 -s create_order
|
||
```
|
||
|
||
**预期检查:**
|
||
- 数据库连接池是否耗尽
|
||
- 是否出现死锁
|
||
- 钱包余额扣减是否正确(需要事务隔离)
|
||
|
||
### 8.2 聊天消息压测
|
||
|
||
```bash
|
||
# 模拟100个用户同时发送消息
|
||
./stress_test -url http://localhost:8080 -c 100 -d 60 -s chat
|
||
```
|
||
|
||
**预期检查:**
|
||
- WebSocket 连接数限制
|
||
- 消息写入速度
|
||
- 未读消息计数准确性
|
||
|
||
### 8.3 大数据量查询
|
||
|
||
```sql
|
||
-- 测试后台钱包流水导出(大数据量)
|
||
SELECT * FROM wallet_ledger
|
||
WHERE created_at >= '2024-01-01'
|
||
ORDER BY created_at DESC;
|
||
|
||
-- 超时检查
|
||
SET SESSION max_execution_time = 30000; -- 30秒超时
|
||
```
|
||
|
||
## 九、压测后清理
|
||
|
||
### 9.1 清理测试数据
|
||
|
||
```sql
|
||
-- 谨慎执行!会删除所有测试数据
|
||
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 恢复配置
|
||
|
||
```bash
|
||
# 恢复开发环境配置
|
||
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. 压测命令速查
|
||
|
||
```bash
|
||
# 启动测试环境
|
||
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. 参考资料
|
||
|
||
- [MySQL 性能优化最佳实践](https://dev.mysql.com/doc/refman/8.0/en/optimization.html)
|
||
- [Go 性能优化](https://github.com/dgryski/go-perfbook)
|
||
- [Gin 框架性能调优](https://gin-gonic.com/docs/benchmarks/)
|