增加测试 修复错误
This commit is contained in:
@@ -0,0 +1,606 @@
|
||||
# 压力测试指南
|
||||
|
||||
本文档提供完整的压力测试方案,用于评估系统在大数据量和高并发场景下的性能表现。
|
||||
|
||||
## 一、测试环境准备
|
||||
|
||||
### 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/)
|
||||
Reference in New Issue
Block a user