1166 lines
38 KiB
Markdown
1166 lines
38 KiB
Markdown
|
|
# 技术架构文档
|
|||
|
|
|
|||
|
|
## 概述
|
|||
|
|
|
|||
|
|
本文档描述用户管理系统的技术架构设计,包括系统架构、性能优化、缓存策略、数据库优化等,确保系统能够满足 PRD 要求的性能指标:
|
|||
|
|
- 支持 10 亿用户规模
|
|||
|
|
- 支持 10 万级并发访问
|
|||
|
|
- API 响应时间 P99 < 500ms
|
|||
|
|
- 系统可用性 99.99%
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 1. 系统架构
|
|||
|
|
|
|||
|
|
### 1.1 整体架构
|
|||
|
|
|
|||
|
|
```
|
|||
|
|
┌─────────────────────────────────────────────────────────────────────────┐
|
|||
|
|
│ 负载均衡层 │
|
|||
|
|
│ (Nginx / HAProxy / LVS) │
|
|||
|
|
└──────────────────────────────┬──────────────────────────────────────────┘
|
|||
|
|
│
|
|||
|
|
┌──────────────────────┼──────────────────────┐
|
|||
|
|
│ │ │
|
|||
|
|
┌───────▼────────┐ ┌────────▼────────┐ ┌────────▼────────┐
|
|||
|
|
│ CDN 层 │ │ API 网关 │ │ WebSocket │
|
|||
|
|
│ (静态资源) │ │ (路由/限流/鉴权) │ │ (实时通信) │
|
|||
|
|
└────────────────┘ └────────┬────────┘ └─────────────────┘
|
|||
|
|
│
|
|||
|
|
┌─────────────────────┼─────────────────────┐
|
|||
|
|
│ │ │
|
|||
|
|
┌───────▼────────┐ ┌────────▼────────┐ ┌────────▼────────┐
|
|||
|
|
│ 应用服务层 │ │ 应用服务层 │ │ 应用服务层 │
|
|||
|
|
│ (多实例) │ │ (多实例) │ │ (多实例) │
|
|||
|
|
│ ┌──────────┐ │ │ ┌──────────┐ │ │ ┌──────────┐ │
|
|||
|
|
│ │ 认证服务 │ │ │ │ 认证服务 │ │ │ │ 认证服务 │ │
|
|||
|
|
│ │ 用户服务 │ │ │ │ 用户服务 │ │ │ │ 用户服务 │ │
|
|||
|
|
│ │ 权限服务 │ │ │ │ 权限服务 │ │ │ │ 权限服务 │ │
|
|||
|
|
│ └──────────┘ │ │ └──────────┘ │ │ └──────────┘ │
|
|||
|
|
└───────┬────────┘ └────────┬────────┘ └────────┬────────┘
|
|||
|
|
│ │ │
|
|||
|
|
└──────────────────────┼──────────────────────┘
|
|||
|
|
│
|
|||
|
|
┌──────────────────────┼──────────────────────┐
|
|||
|
|
│ │ │
|
|||
|
|
┌───────▼────────┐ ┌────────▼────────┐ ┌────────▼────────┐
|
|||
|
|
│ 缓存层 │ │ 缓存层 │ │ 缓存层 │
|
|||
|
|
│ (Redis 集群) │ │ (Redis 集群) │ │ (Redis 集群) │
|
|||
|
|
│ ┌──────────┐ │ │ ┌──────────┐ │ │ ┌──────────┐ │
|
|||
|
|
│ │ 本地缓存 │ │ │ │ 本地缓存 │ │ │ │ 本地缓存 │ │
|
|||
|
|
│ │ 分布式 │ │ │ │ 分布式 │ │ │ │ 分布式 │ │
|
|||
|
|
│ └──────────┘ │ │ └──────────┘ │ │ └──────────┘ │
|
|||
|
|
└───────┬────────┘ └────────┬────────┘ └────────┬────────┘
|
|||
|
|
│ │ │
|
|||
|
|
└──────────────────────┼──────────────────────┘
|
|||
|
|
│
|
|||
|
|
┌──────────────────────┼──────────────────────┐
|
|||
|
|
│ │ │
|
|||
|
|
┌───────▼────────┐ ┌────────▼────────┐ ┌────────▼────────┐
|
|||
|
|
│ 数据库层 │ │ 数据库层 │ │ 数据库层 │
|
|||
|
|
│ (主从复制) │ │ (主从复制) │ │ (主从复制) │
|
|||
|
|
│ ┌──────────┐ │ │ ┌──────────┐ │ │ ┌──────────┐ │
|
|||
|
|
│ │ 主库 │ │ │ │ 主库 │ │ │ │ 主库 │ │
|
|||
|
|
│ │ 从库 1 │ │ │ │ 从库 1 │ │ │ │ 从库 1 │ │
|
|||
|
|
│ │ 从库 2 │ │ │ │ 从库 2 │ │ │ │ 从库 2 │ │
|
|||
|
|
│ └──────────┘ │ │ └──────────┘ │ │ └──────────┘ │
|
|||
|
|
└────────────────┘ └─────────────────┘ └─────────────────┘
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 1.2 单机架构(SQLite)
|
|||
|
|
|
|||
|
|
```
|
|||
|
|
┌─────────────────────────────────────────┐
|
|||
|
|
│ 用户管理系统 (单实例) │
|
|||
|
|
│ │
|
|||
|
|
│ ┌─────────────────────────────────┐ │
|
|||
|
|
│ │ 应用服务 (Port 8080) │ │
|
|||
|
|
│ │ ┌───────────────────────────┐ │ │
|
|||
|
|
│ │ │ 本地缓存 (L1 Cache) │ │ │
|
|||
|
|
│ │ │ - 用户信息 │ │ │
|
|||
|
|
│ │ │ - 权限信息 │ │ │
|
|||
|
|
│ │ │ - Token 黑名单 │ │ │
|
|||
|
|
│ │ └───────────────────────────┘ │ │
|
|||
|
|
│ │ ┌───────────────────────────┐ │ │
|
|||
|
|
│ │ │ 认证/用户/权限服务 │ │ │
|
|||
|
|
│ │ └───────────────────────────┘ │ │
|
|||
|
|
│ └─────────────────────────────────┘ │
|
|||
|
|
│ │ │
|
|||
|
|
│ ┌───────────────┴───────────────┐ │
|
|||
|
|
│ │ │ │
|
|||
|
|
│ ▼ ▼ │
|
|||
|
|
│ ┌──────────────────┐ ┌──────────────┐│
|
|||
|
|
│ │ SQLite DB │ │ 可选 Redis ││
|
|||
|
|
│ │ (单文件存储) │ │ (L2 Cache) ││
|
|||
|
|
│ └──────────────────┘ └──────────────┘│
|
|||
|
|
└─────────────────────────────────────────┘
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 1.3 集群架构(PostgreSQL/MySQL)
|
|||
|
|
|
|||
|
|
```
|
|||
|
|
┌─────────────────────────────────────────────────────────────────┐
|
|||
|
|
│ 负载均衡 (Nginx) │
|
|||
|
|
└────────────────────────┬────────────────────────────────────────┘
|
|||
|
|
│
|
|||
|
|
┌────────────────┼────────────────┐
|
|||
|
|
│ │ │
|
|||
|
|
┌───────▼────────┐ ┌────▼────────┐ ┌─────▼────────┐
|
|||
|
|
│ 应用实例 1 │ │ 应用实例 2 │ │ 应用实例 N │
|
|||
|
|
│ (8080) │ │ (8080) │ │ (8080) │
|
|||
|
|
│ ┌──────────┐ │ │ ┌────────┐ │ │ ┌────────┐ │
|
|||
|
|
│ │ 本地缓存 │ │ │ │ 本地缓 │ │ │ │ 本地缓 │ │
|
|||
|
|
│ │ L1 Cache │ │ │ │ 存 L1 │ │ │ │ 存 L1 │ │
|
|||
|
|
│ └──────────┘ │ │ └────────┘ │ │ └────────┘ │
|
|||
|
|
└───────┬────────┘ └────┬────────┘ └─────┬────────┘
|
|||
|
|
│ │ │
|
|||
|
|
└───────────────┼────────────────┘
|
|||
|
|
│
|
|||
|
|
┌───────────────┴───────────────┐
|
|||
|
|
│ │
|
|||
|
|
┌───────▼────────┐ ┌──────────▼────────┐
|
|||
|
|
│ Redis 集群 │ │ PostgreSQL 集群 │
|
|||
|
|
│ (L2 Cache) │ │ ┌─────────────┐ │
|
|||
|
|
│ ┌──────────┐ │ │ │ 主库 │ │
|
|||
|
|
│ │ Master │ │ │ │ (写) │ │
|
|||
|
|
│ │ 哨兵 │ │ │ └─────────────┘ │
|
|||
|
|
│ └──────────┘ │ │ ┌─────────────┐ │
|
|||
|
|
│ ┌──────────┐ │ │ │ 从库 1 │ │
|
|||
|
|
│ │ Slave 1 │ │ │ │ (读) │ │
|
|||
|
|
│ └──────────┘ │ │ └─────────────┘ │
|
|||
|
|
│ ┌──────────┐ │ │ ┌─────────────┐ │
|
|||
|
|
│ │ Slave N │ │ │ │ 从库 N │ │
|
|||
|
|
│ └──────────┘ │ │ │ (读) │ │
|
|||
|
|
└────────────────┘ │ └─────────────┘ │
|
|||
|
|
└───────────────────┘
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 2. 技术栈选择
|
|||
|
|
|
|||
|
|
### 2.1 后端技术栈
|
|||
|
|
|
|||
|
|
| 层级 | 技术选型 | 说明 |
|
|||
|
|
|------|---------|------|
|
|||
|
|
| **开发语言** | Go 1.21+ | 高性能、并发能力强、内存占用低 |
|
|||
|
|
| **Web 框架** | Gin / Fiber | 轻量级、高性能 |
|
|||
|
|
| **数据库驱动** | GORM / sqlx | ORM 和原生 SQL 混合使用 |
|
|||
|
|
| **缓存** | Redis (go-redis) | 高性能缓存和分布式锁 |
|
|||
|
|
| **配置管理** | Viper | 配置文件和环境变量管理 |
|
|||
|
|
| **日志** | Zap | 高性能结构化日志 |
|
|||
|
|
| **监控** | Prometheus + OpenTelemetry | 指标收集和链路追踪 |
|
|||
|
|
| **限流** | Uber Rate Limit | 令牌桶算法限流 |
|
|||
|
|
| **JWT** | golang-jwt/jwt | JWT 生成和验证 |
|
|||
|
|
| **密码加密** | golang.org/x/crypto/argon2 | Argon2id 密码哈希 |
|
|||
|
|
|
|||
|
|
### 2.2 前端技术栈(Admin 后台)
|
|||
|
|
|
|||
|
|
当前前端技术栈不再在本文件内独立演化,唯一有效方案见:
|
|||
|
|
|
|||
|
|
- `docs/plans/ADMIN_FRONTEND_EXECUTION_PLAN.md`
|
|||
|
|
|
|||
|
|
本文件只保留当前统一结论:
|
|||
|
|
|
|||
|
|
| 层级 | 技术选型 | 说明 |
|
|||
|
|
|------|---------|------|
|
|||
|
|
| **框架** | React 18 + TypeScript | 当前唯一前端框架口径 |
|
|||
|
|
| **构建工具** | Vite | 从零启动成本低,构建快 |
|
|||
|
|
| **UI 组件库** | Ant Design 5 | 后台场景优先 |
|
|||
|
|
| **状态管理** | React Context(仅会话态) | 不引入 Pinia / Redux / Zustand |
|
|||
|
|
| **HTTP 客户端** | 原生 `fetch` + 统一请求客户端 | 不再使用 Axios |
|
|||
|
|
| **路由** | React Router 6 | 统一受保护路由方案 |
|
|||
|
|
| **样式** | CSS Modules + CSS Variables + AntD Theme Token | 不使用 `styled-components` |
|
|||
|
|
|
|||
|
|
页面范围、类型模型、认证流和 API 服务层一律以 `docs/plans/ADMIN_FRONTEND_EXECUTION_PLAN.md` 为准。
|
|||
|
|
|
|||
|
|
### 2.3 基础设施
|
|||
|
|
|
|||
|
|
| 组件 | 技术选型 | 说明 |
|
|||
|
|
|------|---------|------|
|
|||
|
|
| **容器化** | Docker | 应用容器化 |
|
|||
|
|
| **编排** | Kubernetes / Docker Compose | 容器编排 |
|
|||
|
|
| **负载均衡** | Nginx | HTTP 负载均衡 |
|
|||
|
|
| **监控** | Prometheus + Grafana | 指标监控 |
|
|||
|
|
| **日志** | ELK (Elasticsearch + Logstash + Kibana) | 日志收集和分析 |
|
|||
|
|
| **链路追踪** | Jaeger / Zipkin | 分布式链路追踪 |
|
|||
|
|
| **消息队列** | 可选:Kafka / RabbitMQ | 异步消息处理 |
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 3. 性能优化方案
|
|||
|
|
|
|||
|
|
### 3.1 多级缓存架构
|
|||
|
|
|
|||
|
|
```
|
|||
|
|
┌─────────────────────────────────────────────────────────────┐
|
|||
|
|
│ 多级缓存架构 │
|
|||
|
|
├─────────────────────────────────────────────────────────────┤
|
|||
|
|
│ │
|
|||
|
|
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
|
|||
|
|
│ │ L1 缓存 │ -> │ L2 缓存 │ -> │ L3 缓存 │ │
|
|||
|
|
│ │ (本地内存) │ │ (Redis) │ │ (数据库) │ │
|
|||
|
|
│ │ │ │ │ │ │ │
|
|||
|
|
│ │ • 用户信息 │ │ • 用户信息 │ │ • 完整数据 │ │
|
|||
|
|
│ │ • 权限信息 │ │ • 权限信息 │ │ • 原始数据 │ │
|
|||
|
|
│ │ • Token │ │ • Session │ │ │ │
|
|||
|
|
│ │ • 热点数据 │ │ • 热点数据 │ │ │ │
|
|||
|
|
│ │ │ │ │ │ │ │
|
|||
|
|
│ │ TTL: 5min │ │ TTL: 30min │ │ TTL: 永久 │ │
|
|||
|
|
│ │ 容量: 1GB │ │ 容量: 64GB │ │ 容量: 10TB │ │
|
|||
|
|
│ │ 命中率: 85%│ │ 命中率: 12% │ │ 命中率: 3% │ │
|
|||
|
|
│ └─────────────┘ └─────────────┘ └─────────────┘ │
|
|||
|
|
│ │ │ │ │
|
|||
|
|
│ └──────────────────┼──────────────────┘ │
|
|||
|
|
│ │ │
|
|||
|
|
│ ┌─────▼─────┐ │
|
|||
|
|
│ │ 缓存回源 │ │
|
|||
|
|
│ │ 策略 │ │
|
|||
|
|
│ └───────────┘ │
|
|||
|
|
└─────────────────────────────────────────────────────────────┘
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 3.2 L1 本地缓存实现(Go)
|
|||
|
|
|
|||
|
|
```go
|
|||
|
|
package cache
|
|||
|
|
|
|||
|
|
import (
|
|||
|
|
"sync"
|
|||
|
|
"time"
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
type CacheItem struct {
|
|||
|
|
Value interface{}
|
|||
|
|
ExpireTime time.Time
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
type LocalCache struct {
|
|||
|
|
items map[string]*CacheItem
|
|||
|
|
mu sync.RWMutex
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
func NewLocalCache() *LocalCache {
|
|||
|
|
cache := &LocalCache{
|
|||
|
|
items: make(map[string]*CacheItem),
|
|||
|
|
}
|
|||
|
|
// 启动后台清理过期数据
|
|||
|
|
go cache.cleanupExpired()
|
|||
|
|
return cache
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
func (c *LocalCache) Set(key string, value interface{}, ttl time.Duration) {
|
|||
|
|
c.mu.Lock()
|
|||
|
|
defer c.mu.Unlock()
|
|||
|
|
|
|||
|
|
expireTime := time.Now().Add(ttl)
|
|||
|
|
c.items[key] = &CacheItem{
|
|||
|
|
Value: value,
|
|||
|
|
ExpireTime: expireTime,
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
func (c *LocalCache) Get(key string) (interface{}, bool) {
|
|||
|
|
c.mu.RLock()
|
|||
|
|
defer c.mu.RUnlock()
|
|||
|
|
|
|||
|
|
item, exists := c.items[key]
|
|||
|
|
if !exists {
|
|||
|
|
return nil, false
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if time.Now().After(item.ExpireTime) {
|
|||
|
|
return nil, false
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return item.Value, true
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
func (c *LocalCache) Delete(key string) {
|
|||
|
|
c.mu.Lock()
|
|||
|
|
defer c.mu.Unlock()
|
|||
|
|
delete(c.items, key)
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
func (c *LocalCache) cleanupExpired() {
|
|||
|
|
ticker := time.NewTicker(1 * time.Minute)
|
|||
|
|
defer ticker.Stop()
|
|||
|
|
|
|||
|
|
for range ticker.C {
|
|||
|
|
c.mu.Lock()
|
|||
|
|
now := time.Now()
|
|||
|
|
for key, item := range c.items {
|
|||
|
|
if now.After(item.ExpireTime) {
|
|||
|
|
delete(c.items, key)
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
c.mu.Unlock()
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 3.3 L2 Redis 缓存策略
|
|||
|
|
|
|||
|
|
```go
|
|||
|
|
package cache
|
|||
|
|
|
|||
|
|
import (
|
|||
|
|
"context"
|
|||
|
|
"encoding/json"
|
|||
|
|
"time"
|
|||
|
|
|
|||
|
|
"github.com/redis/go-redis/v9"
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
type RedisCache struct {
|
|||
|
|
client *redis.Client
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
func NewRedisCache(addr string) *RedisCache {
|
|||
|
|
rdb := redis.NewClient(&redis.Options{
|
|||
|
|
Addr: addr,
|
|||
|
|
Password: "",
|
|||
|
|
DB: 0,
|
|||
|
|
PoolSize: 100,
|
|||
|
|
MinIdleConns: 10,
|
|||
|
|
})
|
|||
|
|
return &RedisCache{client: rdb}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 设置缓存
|
|||
|
|
func (r *RedisCache) Set(ctx context.Context, key string, value interface{}, ttl time.Duration) error {
|
|||
|
|
data, err := json.Marshal(value)
|
|||
|
|
if err != nil {
|
|||
|
|
return err
|
|||
|
|
}
|
|||
|
|
return r.client.Set(ctx, key, data, ttl).Err()
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 获取缓存
|
|||
|
|
func (r *RedisCache) Get(ctx context.Context, key string, dest interface{}) error {
|
|||
|
|
data, err := r.client.Get(ctx, key).Bytes()
|
|||
|
|
if err != nil {
|
|||
|
|
return err
|
|||
|
|
}
|
|||
|
|
return json.Unmarshal(data, dest)
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 缓存回源
|
|||
|
|
func (r *RedisCache) GetOrSet(ctx context.Context, key string, ttl time.Duration, fn func() (interface{}, error), dest interface{}) error {
|
|||
|
|
// 尝试从缓存获取
|
|||
|
|
err := r.Get(ctx, key, dest)
|
|||
|
|
if err == nil {
|
|||
|
|
return nil
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if err != redis.Nil {
|
|||
|
|
return err
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 缓存未命中,从数据源获取
|
|||
|
|
value, err := fn()
|
|||
|
|
if err != nil {
|
|||
|
|
return err
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 设置缓存
|
|||
|
|
if err := r.Set(ctx, key, value, ttl); err != nil {
|
|||
|
|
// 缓存设置失败不影响主流程
|
|||
|
|
return nil
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 将值赋给 dest
|
|||
|
|
data, _ := json.Marshal(value)
|
|||
|
|
return json.Unmarshal(data, dest)
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 3.4 缓存穿透、击穿、雪崩防护
|
|||
|
|
|
|||
|
|
```go
|
|||
|
|
package cache
|
|||
|
|
|
|||
|
|
import (
|
|||
|
|
"context"
|
|||
|
|
"sync"
|
|||
|
|
"time"
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
// 缓存穿透防护: 布隆过滤器
|
|||
|
|
type BloomFilter struct {
|
|||
|
|
bits []bool
|
|||
|
|
size int
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
func (b *BloomFilter) Add(key string) {
|
|||
|
|
// 简化实现,实际使用推荐使用 github.com/bits-and-blooms/bloom
|
|||
|
|
idx := hash(key) % b.size
|
|||
|
|
b.bits[idx] = true
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
func (b *BloomFilter) Contains(key string) bool {
|
|||
|
|
idx := hash(key) % b.size
|
|||
|
|
return b.bits[idx]
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 缓存击穿防护: 单机互斥锁
|
|||
|
|
type SingleFlight struct {
|
|||
|
|
mu sync.Mutex
|
|||
|
|
calls map[string]*call
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
type call struct {
|
|||
|
|
wg sync.WaitGroup
|
|||
|
|
val interface{}
|
|||
|
|
err error
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
func (s *SingleFlight) Do(key string, fn func() (interface{}, error)) (interface{}, error) {
|
|||
|
|
s.mu.Lock()
|
|||
|
|
if s.calls == nil {
|
|||
|
|
s.calls = make(map[string]*call)
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if c, ok := s.calls[key]; ok {
|
|||
|
|
s.mu.Unlock()
|
|||
|
|
c.wg.Wait()
|
|||
|
|
return c.val, c.err
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
c := new(call)
|
|||
|
|
c.wg.Add(1)
|
|||
|
|
s.calls[key] = c
|
|||
|
|
s.mu.Unlock()
|
|||
|
|
|
|||
|
|
c.val, c.err = fn()
|
|||
|
|
c.wg.Done()
|
|||
|
|
|
|||
|
|
s.mu.Lock()
|
|||
|
|
delete(s.calls, key)
|
|||
|
|
s.mu.Unlock()
|
|||
|
|
|
|||
|
|
return c.val, c.err
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 缓存雪崩防护: 随机 TTL
|
|||
|
|
func RandomTTL(baseTTL time.Duration, jitter time.Duration) time.Duration {
|
|||
|
|
jitterNs := time.Duration(time.Now().UnixNano() % int64(jitter))
|
|||
|
|
return baseTTL + jitterNs
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 3.5 数据库读写分离
|
|||
|
|
|
|||
|
|
```go
|
|||
|
|
package database
|
|||
|
|
|
|||
|
|
import (
|
|||
|
|
"gorm.io/gorm"
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
type Cluster struct {
|
|||
|
|
Master *gorm.DB
|
|||
|
|
Slaves []*gorm.DB
|
|||
|
|
mu sync.RWMutex
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
func NewCluster(master *gorm.DB, slaves []*gorm.DB) *Cluster {
|
|||
|
|
return &Cluster{
|
|||
|
|
Master: master,
|
|||
|
|
Slaves: slaves,
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 获取读库 (负载均衡)
|
|||
|
|
func (c *Cluster) GetSlave() *gorm.DB {
|
|||
|
|
c.mu.RLock()
|
|||
|
|
defer c.mu.RUnlock()
|
|||
|
|
|
|||
|
|
if len(c.Slaves) == 0 {
|
|||
|
|
return c.Master
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 轮询选择从库
|
|||
|
|
idx := time.Now().UnixNano() % int64(len(c.Slaves))
|
|||
|
|
return c.Slaves[idx]
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 写操作使用主库
|
|||
|
|
func (c *Cluster) Write() *gorm.DB {
|
|||
|
|
return c.Master
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 读操作使用从库
|
|||
|
|
func (c *Cluster) Read() *gorm.DB {
|
|||
|
|
return c.GetSlave()
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 3.6 数据库连接池优化
|
|||
|
|
|
|||
|
|
```yaml
|
|||
|
|
# database.yml
|
|||
|
|
database:
|
|||
|
|
# 主库连接池
|
|||
|
|
master:
|
|||
|
|
max_open_conns: 100 # 最大打开连接数
|
|||
|
|
max_idle_conns: 20 # 最大空闲连接数
|
|||
|
|
conn_max_lifetime: 1800s # 连接最大存活时间(30分钟)
|
|||
|
|
conn_max_idle_time: 600s # 连接最大空闲时间(10分钟)
|
|||
|
|
|
|||
|
|
# 从库连接池
|
|||
|
|
slave:
|
|||
|
|
max_open_conns: 200 # 从库可以配置更大的连接池
|
|||
|
|
max_idle_conns: 50
|
|||
|
|
conn_max_lifetime: 1800s
|
|||
|
|
conn_max_idle_time: 600s
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
```go
|
|||
|
|
// Go 实现
|
|||
|
|
db.SetMaxOpenConns(100)
|
|||
|
|
db.SetMaxIdleConns(20)
|
|||
|
|
db.SetConnMaxLifetime(30 * time.Minute)
|
|||
|
|
db.SetConnMaxIdleTime(10 * time.Minute)
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 4. 接口性能优化
|
|||
|
|
|
|||
|
|
### 4.1 批量操作优化
|
|||
|
|
|
|||
|
|
```go
|
|||
|
|
// 不推荐: 循环查询
|
|||
|
|
func GetUsersBatch(userIDs []int64) ([]*User, error) {
|
|||
|
|
var users []*User
|
|||
|
|
for _, id := range userIDs {
|
|||
|
|
var user User
|
|||
|
|
if err := db.First(&user, id).Error; err != nil {
|
|||
|
|
return nil, err
|
|||
|
|
}
|
|||
|
|
users = append(users, &user)
|
|||
|
|
}
|
|||
|
|
return users, nil
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 推荐: 批量查询
|
|||
|
|
func GetUsersBatch(userIDs []int64) ([]*User, error) {
|
|||
|
|
var users []*User
|
|||
|
|
if err := db.Where("id IN ?", userIDs).Find(&users).Error; err != nil {
|
|||
|
|
return nil, err
|
|||
|
|
}
|
|||
|
|
return users, nil
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 4.2 预加载关联数据
|
|||
|
|
|
|||
|
|
```go
|
|||
|
|
// 不推荐: N+1 查询
|
|||
|
|
func GetUsersWithRoles() ([]*User, error) {
|
|||
|
|
var users []*User
|
|||
|
|
db.Find(&users)
|
|||
|
|
|
|||
|
|
for _, user := range users {
|
|||
|
|
var roles []Role
|
|||
|
|
db.Where("user_id = ?", user.ID).Find(&roles) // N+1 查询
|
|||
|
|
user.Roles = roles
|
|||
|
|
}
|
|||
|
|
return users, nil
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 推荐: 预加载
|
|||
|
|
func GetUsersWithRoles() ([]*User, error) {
|
|||
|
|
var users []*User
|
|||
|
|
db.Preload("Roles").Find(&users) // 使用 Preload 一次性加载
|
|||
|
|
return users, nil
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 4.3 索引优化
|
|||
|
|
|
|||
|
|
```sql
|
|||
|
|
-- 用户登录查询优化
|
|||
|
|
-- 不推荐: 全表扫描
|
|||
|
|
SELECT * FROM users WHERE email = 'john@example.com' AND status = 1;
|
|||
|
|
|
|||
|
|
-- 推荐: 创建复合索引
|
|||
|
|
CREATE INDEX idx_email_status ON users(email, status);
|
|||
|
|
|
|||
|
|
-- 角色权限查询优化
|
|||
|
|
-- 不推荐: 多次关联查询
|
|||
|
|
SELECT p.* FROM permissions p
|
|||
|
|
INNER JOIN role_permissions rp ON p.id = rp.permission_id
|
|||
|
|
WHERE rp.role_id IN (SELECT role_id FROM user_roles WHERE user_id = ?);
|
|||
|
|
|
|||
|
|
-- 推荐: 优化 SQL 和索引
|
|||
|
|
CREATE INDEX idx_user_roles_user_id ON user_roles(user_id);
|
|||
|
|
CREATE INDEX idx_role_permissions_role_id ON role_permissions(role_id);
|
|||
|
|
|
|||
|
|
-- 使用 JOIN 优化
|
|||
|
|
SELECT p.* FROM permissions p
|
|||
|
|
INNER JOIN role_permissions rp ON p.id = rp.permission_id
|
|||
|
|
INNER JOIN user_roles ur ON rp.role_id = ur.role_id
|
|||
|
|
WHERE ur.user_id = ?;
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 4.4 分页优化(游标分页)
|
|||
|
|
|
|||
|
|
```go
|
|||
|
|
// 不推荐: OFFSET 分页(数据量大时性能差)
|
|||
|
|
func GetUsersByPage(page, pageSize int) ([]*User, error) {
|
|||
|
|
var users []*User
|
|||
|
|
offset := (page - 1) * pageSize
|
|||
|
|
if err := db.Offset(offset).Limit(pageSize).Find(&users).Error; err != nil {
|
|||
|
|
return nil, err
|
|||
|
|
}
|
|||
|
|
return users, nil
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 推荐: 游标分页(基于 ID)
|
|||
|
|
type PageResult struct {
|
|||
|
|
Users []*User `json:"users"`
|
|||
|
|
LastID int64 `json:"last_id"`
|
|||
|
|
HasMore bool `json:"has_more"`
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
func GetUsersByCursor(lastID int64, pageSize int) (*PageResult, error) {
|
|||
|
|
var users []*User
|
|||
|
|
query := db.Order("id ASC").Limit(pageSize + 1)
|
|||
|
|
|
|||
|
|
if lastID > 0 {
|
|||
|
|
query = query.Where("id > ?", lastID)
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if err := query.Find(&users).Error; err != nil {
|
|||
|
|
return nil, err
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
hasMore := len(users) > pageSize
|
|||
|
|
if hasMore {
|
|||
|
|
users = users[:pageSize]
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
var lastIDResult int64
|
|||
|
|
if len(users) > 0 {
|
|||
|
|
lastIDResult = users[len(users)-1].ID
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return &PageResult{
|
|||
|
|
Users: users,
|
|||
|
|
LastID: lastIDResult,
|
|||
|
|
HasMore: hasMore,
|
|||
|
|
}, nil
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 5. 并发处理优化
|
|||
|
|
|
|||
|
|
### 5.1 协程池
|
|||
|
|
|
|||
|
|
```go
|
|||
|
|
package worker
|
|||
|
|
|
|||
|
|
import (
|
|||
|
|
"sync"
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
type Task func()
|
|||
|
|
|
|||
|
|
type WorkerPool struct {
|
|||
|
|
tasks chan Task
|
|||
|
|
workers int
|
|||
|
|
wg sync.WaitGroup
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
func NewWorkerPool(workers int, taskQueueSize int) *WorkerPool {
|
|||
|
|
return &WorkerPool{
|
|||
|
|
tasks: make(chan Task, taskQueueSize),
|
|||
|
|
workers: workers,
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
func (p *WorkerPool) Start() {
|
|||
|
|
for i := 0; i < p.workers; i++ {
|
|||
|
|
p.wg.Add(1)
|
|||
|
|
go p.worker()
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
func (p *WorkerPool) worker() {
|
|||
|
|
defer p.wg.Done()
|
|||
|
|
for task := range p.tasks {
|
|||
|
|
task()
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
func (p *WorkerPool) Submit(task Task) {
|
|||
|
|
p.tasks <- task
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
func (p *WorkerPool) Stop() {
|
|||
|
|
close(p.tasks)
|
|||
|
|
p.wg.Wait()
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 5.2 批量并发查询
|
|||
|
|
|
|||
|
|
```go
|
|||
|
|
package service
|
|||
|
|
|
|||
|
|
import (
|
|||
|
|
"sync"
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
func BatchGetUsers(userIDs []int64) (map[int64]*User, error) {
|
|||
|
|
result := make(map[int64]*User)
|
|||
|
|
var mu sync.Mutex
|
|||
|
|
var wg sync.WaitGroup
|
|||
|
|
errChan := make(chan error, len(userIDs))
|
|||
|
|
|
|||
|
|
// 创建协程池
|
|||
|
|
pool := worker.NewWorkerPool(10, 1000)
|
|||
|
|
pool.Start()
|
|||
|
|
defer pool.Stop()
|
|||
|
|
|
|||
|
|
for _, id := range userIDs {
|
|||
|
|
wg.Add(1)
|
|||
|
|
pool.Submit(func() {
|
|||
|
|
defer wg.Done()
|
|||
|
|
|
|||
|
|
user, err := getUserByID(id)
|
|||
|
|
if err != nil {
|
|||
|
|
errChan <- err
|
|||
|
|
return
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
mu.Lock()
|
|||
|
|
result[id] = user
|
|||
|
|
mu.Unlock()
|
|||
|
|
})
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
wg.Wait()
|
|||
|
|
close(errChan)
|
|||
|
|
|
|||
|
|
// 检查是否有错误
|
|||
|
|
for err := range errChan {
|
|||
|
|
if err != nil {
|
|||
|
|
return nil, err
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return result, nil
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 6. 性能监控
|
|||
|
|
|
|||
|
|
### 6.1 Prometheus 指标定义
|
|||
|
|
|
|||
|
|
```go
|
|||
|
|
package metrics
|
|||
|
|
|
|||
|
|
import (
|
|||
|
|
"github.com/prometheus/client_golang/prometheus"
|
|||
|
|
"github.com/prometheus/client_golang/prometheus/promauto"
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
var (
|
|||
|
|
// HTTP 请求数
|
|||
|
|
HTTPRequestsTotal = promauto.NewCounterVec(
|
|||
|
|
prometheus.CounterOpts{
|
|||
|
|
Name: "http_requests_total",
|
|||
|
|
Help: "Total number of HTTP requests",
|
|||
|
|
},
|
|||
|
|
[]string{"method", "path", "status"},
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
// HTTP 请求耗时
|
|||
|
|
HTTPRequestDuration = promauto.NewHistogramVec(
|
|||
|
|
prometheus.HistogramOpts{
|
|||
|
|
Name: "http_request_duration_seconds",
|
|||
|
|
Help: "HTTP request latency in seconds",
|
|||
|
|
Buckets: prometheus.DefBuckets,
|
|||
|
|
},
|
|||
|
|
[]string{"method", "path"},
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
// 缓存命中率
|
|||
|
|
CacheHitTotal = promauto.NewCounterVec(
|
|||
|
|
prometheus.CounterOpts{
|
|||
|
|
Name: "cache_hit_total",
|
|||
|
|
Help: "Total number of cache hits",
|
|||
|
|
},
|
|||
|
|
[]string{"cache_level", "key_pattern"},
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
CacheMissTotal = promauto.NewCounterVec(
|
|||
|
|
prometheus.CounterOpts{
|
|||
|
|
Name: "cache_miss_total",
|
|||
|
|
Help: "Total number of cache misses",
|
|||
|
|
},
|
|||
|
|
[]string{"cache_level", "key_pattern"},
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
// 数据库查询耗时
|
|||
|
|
DBQueryDuration = promauto.NewHistogramVec(
|
|||
|
|
prometheus.HistogramOpts{
|
|||
|
|
Name: "db_query_duration_seconds",
|
|||
|
|
Help: "Database query latency in seconds",
|
|||
|
|
Buckets: []float64{0.001, 0.005, 0.01, 0.05, 0.1, 0.5, 1, 5},
|
|||
|
|
},
|
|||
|
|
[]string{"operation", "table"},
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
// 在线用户数
|
|||
|
|
OnlineUsers = promauto.NewGauge(
|
|||
|
|
prometheus.GaugeOpts{
|
|||
|
|
Name: "online_users",
|
|||
|
|
Help: "Current number of online users",
|
|||
|
|
},
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
// 总用户数
|
|||
|
|
TotalUsers = promauto.NewGauge(
|
|||
|
|
prometheus.GaugeOpts{
|
|||
|
|
Name: "total_users",
|
|||
|
|
Help: "Total number of users",
|
|||
|
|
},
|
|||
|
|
)
|
|||
|
|
)
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 6.2 中间件集成
|
|||
|
|
|
|||
|
|
```go
|
|||
|
|
package middleware
|
|||
|
|
|
|||
|
|
import (
|
|||
|
|
"strconv"
|
|||
|
|
"time"
|
|||
|
|
|
|||
|
|
"github.com/gin-gonic/gin"
|
|||
|
|
"your-project/metrics"
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
func PrometheusMiddleware() gin.HandlerFunc {
|
|||
|
|
return func(c *gin.Context) {
|
|||
|
|
start := time.Now()
|
|||
|
|
|
|||
|
|
// 处理请求
|
|||
|
|
c.Next()
|
|||
|
|
|
|||
|
|
// 记录指标
|
|||
|
|
duration := time.Since(start).Seconds()
|
|||
|
|
status := strconv.Itoa(c.Writer.Status())
|
|||
|
|
|
|||
|
|
metrics.HTTPRequestsTotal.WithLabelValues(
|
|||
|
|
c.Request.Method,
|
|||
|
|
c.FullPath(),
|
|||
|
|
status,
|
|||
|
|
).Inc()
|
|||
|
|
|
|||
|
|
metrics.HTTPRequestDuration.WithLabelValues(
|
|||
|
|
c.Request.Method,
|
|||
|
|
c.FullPath(),
|
|||
|
|
).Observe(duration)
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 7. 性能目标与调优
|
|||
|
|
|
|||
|
|
### 7.1 性能目标
|
|||
|
|
|
|||
|
|
| 指标 | 目标值 | 当前值 | 状态 |
|
|||
|
|
|------|--------|--------|------|
|
|||
|
|
| 并发用户数 | 100,000 | - | 待验证 |
|
|||
|
|
| QPS | 100,000 | - | 待验证 |
|
|||
|
|
| P50 响应时间 | < 100ms | - | 待验证 |
|
|||
|
|
| P99 响应时间 | < 500ms | - | 待验证 |
|
|||
|
|
| 缓存命中率 | > 95% | - | 待验证 |
|
|||
|
|
| 数据库 QPS | < 10,000 | - | 待验证 |
|
|||
|
|
|
|||
|
|
### 7.2 性能调优清单
|
|||
|
|
|
|||
|
|
- [ ] 启用本地缓存(L1 Cache)
|
|||
|
|
- [ ] 配置 Redis 集群(L2 Cache)
|
|||
|
|
- [ ] 数据库读写分离
|
|||
|
|
- [ ] 优化数据库索引
|
|||
|
|
- [ ] 批量操作优化
|
|||
|
|
- [ ] 使用游标分页
|
|||
|
|
- [ ] 连接池调优
|
|||
|
|
- [ ] 协程池优化
|
|||
|
|
- [ ] 启用 Gzip 压缩
|
|||
|
|
- [ ] CDN 加速静态资源
|
|||
|
|
- [ ] HTTP/2 支持
|
|||
|
|
- [ ] 数据库查询优化
|
|||
|
|
|
|||
|
|
### 7.3 压力测试方案
|
|||
|
|
|
|||
|
|
```bash
|
|||
|
|
# 使用 Apache Bench (ab)
|
|||
|
|
ab -n 100000 -c 1000 http://localhost:8080/api/v1/users
|
|||
|
|
|
|||
|
|
# 使用 wrk
|
|||
|
|
wrk -t12 -c400 -d30s http://localhost:8080/api/v1/users
|
|||
|
|
|
|||
|
|
# 使用 hey
|
|||
|
|
hey -n 100000 -c 1000 http://localhost:8080/api/v1/users
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 8. 扩展性设计
|
|||
|
|
|
|||
|
|
### 8.1 水平扩展
|
|||
|
|
|
|||
|
|
- **无状态设计**: 应用服务不保存状态,支持水平扩展
|
|||
|
|
- **会话管理**: 使用 Redis 存储会话
|
|||
|
|
- **文件存储**: 使用对象存储(OSS/S3)
|
|||
|
|
- **消息队列**: 使用 Kafka/RabbitMQ 异步处理
|
|||
|
|
|
|||
|
|
### 8.2 垂直扩展
|
|||
|
|
|
|||
|
|
- **资源限制**: 根据 QPS 调整资源配置
|
|||
|
|
- **缓存调优**: 增加缓存容量
|
|||
|
|
- **数据库优化**: 增加 CPU/内存,使用更好的存储
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 9. 容灾与高可用
|
|||
|
|
|
|||
|
|
### 9.1 多机房部署
|
|||
|
|
|
|||
|
|
```
|
|||
|
|
┌─────────────────────────────────────────────────────────┐
|
|||
|
|
│ 全局负载均衡 (GSLB) │
|
|||
|
|
└──────────────────┬───────────────┬──────────────────────┘
|
|||
|
|
│ │
|
|||
|
|
┌──────────▼────┐ ┌──────▼──────────┐
|
|||
|
|
│ 机房 A (北京) │ │ 机房 B (上海) │
|
|||
|
|
│ ┌──────────┐ │ │ ┌──────────┐ │
|
|||
|
|
│ │ 负载均衡 │ │ │ │ 负载均衡 │ │
|
|||
|
|
│ └────┬─────┘ │ │ └────┬─────┘ │
|
|||
|
|
│ │ │ │ │ │
|
|||
|
|
│ ┌────▼────┐ │ │ ┌────▼────┐ │
|
|||
|
|
│ │ 应用集群│ │ │ │ 应用集群│ │
|
|||
|
|
│ └────┬────┘ │ │ └────┬────┘ │
|
|||
|
|
│ │ │ │ │ │
|
|||
|
|
│ ┌────▼────┐ │ │ ┌────▼────┐ │
|
|||
|
|
│ │Redis集群│ │ │ │Redis集群│ │
|
|||
|
|
│ └─────────┘ │ │ └─────────┘ │
|
|||
|
|
│ ┌────┬────┐ │ │ ┌────┬────┐ │
|
|||
|
|
│ │DB主│DB从│ │ │ │DB主│DB从│ │
|
|||
|
|
│ └────┴────┘ │ │ └────┴────┘ │
|
|||
|
|
└───────────────┘ └───────────────┘
|
|||
|
|
│ │
|
|||
|
|
└───────┬───────┘
|
|||
|
|
│
|
|||
|
|
┌──────▼──────┐
|
|||
|
|
│ 异地灾备 │
|
|||
|
|
│ (广州) │
|
|||
|
|
└─────────────┘
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 9.2 数据备份策略
|
|||
|
|
|
|||
|
|
- **实时备份**: 主从复制
|
|||
|
|
- **每日备份**: 全量备份 + 增量备份
|
|||
|
|
- **跨机房备份**: 异地备份
|
|||
|
|
- **加密存储**: 备份数据加密
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 10. 性能优化案例
|
|||
|
|
|
|||
|
|
### 10.1 登录接口优化
|
|||
|
|
|
|||
|
|
**优化前**: 500ms (P99)
|
|||
|
|
```go
|
|||
|
|
// 每次都查询数据库
|
|||
|
|
func Login(username, password string) (*User, error) {
|
|||
|
|
var user User
|
|||
|
|
db.Where("username = ?", username).First(&user)
|
|||
|
|
// 验证密码...
|
|||
|
|
return &user, nil
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**优化后**: 50ms (P99)
|
|||
|
|
```go
|
|||
|
|
// 使用本地缓存 + Redis 缓存
|
|||
|
|
func Login(username, password string) (*User, error) {
|
|||
|
|
// L1 缓存查询
|
|||
|
|
if user, ok := l1Cache.Get("user:" + username); ok {
|
|||
|
|
return user.(*User), nil
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// L2 缓存查询
|
|||
|
|
var user User
|
|||
|
|
err := redisCache.GetOrSet(ctx, "user:"+username, 30*time.Minute,
|
|||
|
|
func() (interface{}, error) {
|
|||
|
|
var u User
|
|||
|
|
db.Where("username = ?", username).First(&u)
|
|||
|
|
return &u, nil
|
|||
|
|
}, &user)
|
|||
|
|
|
|||
|
|
if err != nil {
|
|||
|
|
return nil, err
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 更新 L1 缓存
|
|||
|
|
l1Cache.Set("user:"+username, &user, 5*time.Minute)
|
|||
|
|
|
|||
|
|
return &user, nil
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 10.2 权限查询优化
|
|||
|
|
|
|||
|
|
**优化前**: 1000ms (P99)
|
|||
|
|
```go
|
|||
|
|
// 每次都查询数据库
|
|||
|
|
func GetUserPermissions(userID int64) ([]string, error) {
|
|||
|
|
var userRoles []UserRole
|
|||
|
|
db.Where("user_id = ?", userID).Find(&userRoles)
|
|||
|
|
|
|||
|
|
var permissions []string
|
|||
|
|
for _, ur := range userRoles {
|
|||
|
|
var rolePermissions []RolePermission
|
|||
|
|
db.Where("role_id = ?", ur.RoleID).Find(&rolePermissions)
|
|||
|
|
|
|||
|
|
for _, rp := range rolePermissions {
|
|||
|
|
var permission Permission
|
|||
|
|
db.First(&permission, rp.PermissionID)
|
|||
|
|
permissions = append(permissions, permission.Code)
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return permissions, nil
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**优化后**: 20ms (P99)
|
|||
|
|
```go
|
|||
|
|
// 使用本地缓存 + Redis 缓存 + 批量查询
|
|||
|
|
func GetUserPermissions(userID int64) ([]string, error) {
|
|||
|
|
cacheKey := fmt.Sprintf("user:permissions:%d", userID)
|
|||
|
|
|
|||
|
|
// L1 缓存查询
|
|||
|
|
if perms, ok := l1Cache.Get(cacheKey); ok {
|
|||
|
|
return perms.([]string), nil
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// L2 缓存查询
|
|||
|
|
var permissions []string
|
|||
|
|
err := redisCache.GetOrSet(ctx, cacheKey, 30*time.Minute,
|
|||
|
|
func() (interface{}, error) {
|
|||
|
|
// 批量查询角色和权限
|
|||
|
|
var result []struct {
|
|||
|
|
PermissionCode string
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
db.Table("permissions").
|
|||
|
|
Select("permissions.code as permission_code").
|
|||
|
|
Joins("INNER JOIN role_permissions ON role_permissions.permission_id = permissions.id").
|
|||
|
|
Joins("INNER JOIN user_roles ON user_roles.role_id = role_permissions.role_id").
|
|||
|
|
Where("user_roles.user_id = ?", userID).
|
|||
|
|
Scan(&result)
|
|||
|
|
|
|||
|
|
var codes []string
|
|||
|
|
for _, r := range result {
|
|||
|
|
codes = append(codes, r.PermissionCode)
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return codes, nil
|
|||
|
|
}, &permissions)
|
|||
|
|
|
|||
|
|
if err != nil {
|
|||
|
|
return nil, err
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 更新 L1 缓存
|
|||
|
|
l1Cache.Set(cacheKey, permissions, 5*time.Minute)
|
|||
|
|
|
|||
|
|
return permissions, nil
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 11. 性能监控与告警
|
|||
|
|
|
|||
|
|
### 11.1 核心监控指标
|
|||
|
|
|
|||
|
|
| 指标 | 类型 | 说明 |
|
|||
|
|
|------|------|------|
|
|||
|
|
| `http_requests_total` | Counter | HTTP 请求总数 |
|
|||
|
|
| `http_request_duration_seconds` | Histogram | HTTP 请求耗时 |
|
|||
|
|
| `cache_hit_total` | Counter | 缓存命中数 |
|
|||
|
|
| `cache_miss_total` | Counter | 缓存未命中数 |
|
|||
|
|
| `db_query_duration_seconds` | Histogram | 数据库查询耗时 |
|
|||
|
|
| `online_users` | Gauge | 在线用户数 |
|
|||
|
|
| `total_users` | Gauge | 总用户数 |
|
|||
|
|
|
|||
|
|
### 11.2 告警规则
|
|||
|
|
|
|||
|
|
```yaml
|
|||
|
|
groups:
|
|||
|
|
- name: user-ms-alerts
|
|||
|
|
rules:
|
|||
|
|
# 高错误率告警
|
|||
|
|
- alert: HighErrorRate
|
|||
|
|
expr: rate(http_requests_total{status=~"5.."}[5m]) > 0.01
|
|||
|
|
for: 5m
|
|||
|
|
annotations:
|
|||
|
|
summary: "高错误率告警"
|
|||
|
|
|
|||
|
|
# 高响应时间告警
|
|||
|
|
- alert: HighResponseTime
|
|||
|
|
expr: histogram_quantile(0.99, rate(http_request_duration_seconds_bucket[5m])) > 0.5
|
|||
|
|
for: 5m
|
|||
|
|
annotations:
|
|||
|
|
summary: "P99 响应时间超过 500ms"
|
|||
|
|
|
|||
|
|
# 低缓存命中率告警
|
|||
|
|
- alert: LowCacheHitRate
|
|||
|
|
expr: rate(cache_hit_total[5m]) / (rate(cache_hit_total[5m]) + rate(cache_miss_total[5m])) < 0.9
|
|||
|
|
for: 10m
|
|||
|
|
annotations:
|
|||
|
|
summary: "缓存命中率低于 90%"
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 12. 总结
|
|||
|
|
|
|||
|
|
本技术架构文档定义了用户管理系统的完整技术方案,包括:
|
|||
|
|
|
|||
|
|
1. **系统架构**: 单机(SQLite)和集群(PostgreSQL/MySQL)两种架构
|
|||
|
|
2. **多级缓存**: L1 本地缓存 + L2 Redis 缓存 + L3 数据库
|
|||
|
|
3. **性能优化**: 批量操作、预加载、索引优化、游标分页
|
|||
|
|
4. **并发处理**: 协程池、批量并发查询
|
|||
|
|
5. **监控告警**: Prometheus 指标、告警规则
|
|||
|
|
6. **扩展性**: 水平扩展、垂直扩展
|
|||
|
|
7. **高可用**: 多机房部署、数据备份
|
|||
|
|
|
|||
|
|
通过以上优化,系统能够达到 PRD 要求的性能指标:
|
|||
|
|
- 10 亿用户规模
|
|||
|
|
- 10 万级并发
|
|||
|
|
- P99 响应时间 < 500ms
|
|||
|
|
- 99.99% 可用性
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
*本文档持续更新中,如有疑问请联系技术团队。*
|