
前言:
"
过早优化是万恶之源,但过晚优化可能让你失去用户"---这是一篇帮助 你我
更好的做牛马,做更好的牛马的文档---刚刚开始,慢慢来
📋 目录
- [🎯 文档说明](#🎯 文档说明)
- [📊 性能优化全景图](#📊 性能优化全景图)
-
💾 [第一章:数据库性能优化\](#第一章数据库性能优化)- `点击跳转相应文档`](https://blog.csdn.net/bojinyuan00/article/details/156270988) * [1.1 SQL 执行分析与优化](#1.1 SQL 执行分析与优化) * [1.2 索引优化的艺术](#1.2 索引优化的艺术) * [1.3 查询优化技巧](#1.3 查询优化技巧) * [1.4 连接池优化](#1.4 连接池优化) * [1.5 读写分离与分库分表](#1.5 读写分离与分库分表)
- [2.1 缓存设计原则](#2.1 缓存设计原则)
- [2.2 多级缓存架构](#2.2 多级缓存架构)
- [2.3 缓存三大问题及解决方案](#2.3 缓存三大问题及解决方案)
- [2.4 缓存更新策略](#2.4 缓存更新策略)
- [2.5 Redis 性能优化](#2.5 Redis 性能优化)
- [🎨 第三章:代码层面性能优化](#🎨 第三章:代码层面性能优化)
点击跳转相应文档- [3.1 内存管理与优化](#3.1 内存管理与优化)
- [3.2 并发编程最佳实践](#3.2 并发编程最佳实践)
- [3.3 字符串处理优化](#3.3 字符串处理优化)
- [3.4 数据结构选择](#3.4 数据结构选择)
- [3.5 对象复用与内存池](#3.5 对象复用与内存池)
- [🔄 第四章:异步处理与消息队列](#🔄 第四章:异步处理与消息队列)
点击跳转相应文档- [4.1 异步编程模式](#4.1 异步编程模式)
- [4.2 消息队列选型](#4.2 消息队列选型)
- [4.3 任务队列设计](#4.3 任务队列设计)
- [4.4 异步回调机制](#4.4 异步回调机制)
- [🌐 第五章:网络 I/O 优化](#🌐 第五章:网络 I/O 优化)
点击跳转相应文档- [5.1 HTTP 性能优化](#5.1 HTTP 性能优化)
- [5.2 gRPC 高性能实践](#5.2 gRPC 高性能实践)
- [5.3 WebSocket 优化](#5.3 WebSocket 优化)
- [5.4 连接复用与池化](#5.4 连接复用与池化)
- [📈 第六章:监控、分析与调优](#📈 第六章:监控、分析与调优)
点击跳转相应文档- [6.1 性能监控体系](#6.1 性能监控体系)
- [6.2 pprof 深度使用](#6.2 pprof 深度使用)
- [6.3 链路追踪](#6.3 链路追踪)
- [6.4 日志优化](#6.4 日志优化)
- [🏗️ 第七章:架构层面优化](#🏗️ 第七章:架构层面优化)
点击跳转相应文档- [7.1 服务治理](#7.1 服务治理)
- [7.2 限流与熔断](#7.2 限流与熔断)
- [7.3 负载均衡策略](#7.3 负载均衡策略)
- [7.4 CDN 与边缘计算](#7.4 CDN 与边缘计算)
- [💡 第八章:高级优化技巧](#💡 第八章:高级优化技巧)
点击跳转相应文档- [8.1 CPU 缓存友好的代码](#8.1 CPU 缓存友好的代码)
- [8.2 减少 GC 压力](#8.2 减少 GC 压力)
- [8.3 编译优化](#8.3 编译优化)
- [8.4 性能测试与压测](#8.4 性能测试与压测)
- [📝 第九章:实战案例分析](#📝 第九章:实战案例分析)
点击跳转相应文档 - [✅ 第十章:性能优化 Checklist](#✅ 第十章:性能优化 Checklist)
点击跳转相应文档
🎯 文档说明
为什么需要这份手册?
在微服务盛行的今天,后端接口性能直接影响用户体验和系统稳定性。一个响应时间从 3 秒优化到 300 毫秒的接口,不仅能让用户体验提升 10 倍,还能节省大量服务器成本。
本手册的特色
- ✅ 实战导向:每个优化点都配有真实代码示例
- ✅ 场景明确:清晰说明每种优化的适用场景
- ✅ 对比鲜明:用 ❌ 和 ✅ 直观展示好坏实践
- ✅ 深入浅出:用生动的比喻解释复杂概念
- ✅ 可操作性强:提供完整的代码和配置示例
如何使用本手册
- 快速诊断:遇到性能问题时,查找对应章节
- 系统学习:按章节顺序学习性能优化知识体系
- 代码审查:用 Checklist 检查现有项目
- 方案设计:参考架构章节设计高性能系统
性能优化的黄金法则
💡 80/20 原则:80% 的性能问题通常来自 20% 的代码
💡 测量先行:没有测量就没有优化,先用数据说话
💡 渐进式优化:先优化瓶颈,再优化细节
📊 性能优化全景图
┌─────────────────────────────────────────────────────────────────┐
│ 性能优化层次模型 │
├─────────────────────────────────────────────────────────────────┤
│ ┌───────────────────────────────────────────────────────────┐ │
│ │ 架构层 🏗️ │ 服务拆分 • 负载均衡 • 限流熔断 • CDN │ │
│ └───────────────────────────────────────────────────────────┘ │
│ ┌───────────────────────────────────────────────────────────┐ │
│ │ 存储层 💾 │ 数据库优化 • 缓存策略 • 读写分离 │ │
│ └───────────────────────────────────────────────────────────┘ │
│ ┌───────────────────────────────────────────────────────────┐ │
│ │ 应用层 ⚡ │ 代码优化 • 并发控制 • 异步处理 │ │
│ └───────────────────────────────────────────────────────────┘ │
│ ┌───────────────────────────────────────────────────────────┐ │
│ │ 网络层 🌐 │ 协议优化 • 连接池 • 序列化优化 │ │
│ └───────────────────────────────────────────────────────────┘ │
│ ┌───────────────────────────────────────────────────────────┐ │
│ │ 监控层 📈 │ 性能监控 • 链路追踪 • 日志分析 │ │
│ └───────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────┘
💾 第一章:数据库性能优化
"数据库就像冰山,90% 的性能问题都藏在水面下"
数据库往往是后端性能的第一瓶颈。据统计,超过 70% 的慢接口问题源于数据库查询。本章将带你深入了解数据库优化的方方面面。
1.1 SQL 执行分析与优化
📌 慢查询日志:性能问题的侦察兵
适用场景:生产环境持续监控,定位性能瓶颈
go
// 配置 GORM 慢查询日志
import (
"gorm.io/gorm"
"gorm.io/gorm/logger"
"log"
"os"
"time"
)
func InitDB() *gorm.DB {
// 自定义日志配置
newLogger := logger.New(
log.New(os.Stdout, "\r\n", log.LstdFlags),
logger.Config{
SlowThreshold: 200 * time.Millisecond, // 慢查询阈值:200ms
LogLevel: logger.Warn, // 只记录警告级别
IgnoreRecordNotFoundError: true, // 忽略 ErrRecordNotFound
Colorful: true, // 彩色输出
},
)
db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{
Logger: newLogger,
})
if err != nil {
panic("failed to connect database")
}
return db
}
// 输出示例:
// [2.156s] [rows:1523] SELECT * FROM orders WHERE user_id = 12345
// ⚠️ 这条查询太慢了,需要优化!
📌 EXPLAIN:SQL 的体检报告
适用场景:开发阶段分析查询计划,优化 SQL 语句
go
// 在 Go 中使用 EXPLAIN 分析查询
func AnalyzeQuery(db *gorm.DB) {
var result []map[string]interface{}
// 使用 EXPLAIN 分析查询
db.Raw(`
EXPLAIN
SELECT u.name, o.order_no, o.amount
FROM users u
LEFT JOIN orders o ON u.id = o.user_id
WHERE u.email = ?
AND o.created_at > ?
`, "test@example.com", time.Now().AddDate(0, -1, 0)).Scan(&result)
// 打印分析结果
for _, row := range result {
fmt.Printf("Type: %v, Rows: %v, Extra: %v\n",
row["type"], row["rows"], row["Extra"])
}
}
EXPLAIN 关键字段解读:
| 字段 | 好的值 | 坏的值 | 说明 |
|---|---|---|---|
| type | const, ref, range | ALL, index | ALL 意味着全表扫描,性能最差 |
| rows | 越小越好 | 数万以上 | 预计扫描的行数 |
| Extra | Using index | Using filesort, Using temporary | filesort 表示需要额外排序 |
| key | 有索引名 | NULL | NULL 表示没有使用索引 |
📌 N+1 查询问题:性能杀手 No.1
问题场景:查询用户列表,然后循环查询每个用户的订单
go
// ❌ 糟糕的做法:N+1 查询问题
func GetUsersWithOrdersBad(db *gorm.DB) []UserWithOrders {
var users []User
db.Find(&users) // 1 次查询
var result []UserWithOrders
for _, user := range users { // N 次查询
var orders []Order
db.Where("user_id = ?", user.ID).Find(&orders) // 每个用户都查一次!
result = append(result, UserWithOrders{
User: user,
Orders: orders,
})
}
return result
}
// 如果有 100 个用户,就会执行 101 次查询!😱
// ✅ 优秀的做法:使用 JOIN 或 Preload
func GetUsersWithOrdersGood(db *gorm.DB) []User {
var users []User
// 方法一:使用 Preload(推荐)
db.Preload("Orders").Find(&users) // 只需 2 次查询!
return users
}
// ✅ 方法二:使用 Joins(更高效)
func GetUsersWithOrdersJoin(db *gorm.DB) []UserWithOrders {
var results []UserWithOrders
db.Table("users").
Select("users.*, orders.*").
Joins("LEFT JOIN orders ON users.id = orders.user_id").
Scan(&results) // 只需 1 次查询!
return results
}
// 性能对比:
// ❌ N+1 查询:101 次数据库请求,耗时约 1000ms
// ✅ Preload: 2 次数据库请求,耗时约 50ms
// ✅ JOIN: 1 次数据库请求,耗时约 30ms
📌 只查询需要的字段
适用场景:列表查询、API 响应字段较少时
go
// ❌ 查询所有字段(包括大字段)
func GetUsersBad(db *gorm.DB) []User {
var users []User
db.Find(&users) // SELECT * FROM users
// 即使不需要,也会查询 avatar(大图)、description(长文本)等字段
return users
}
// ✅ 只查询需要的字段
func GetUsersGood(db *gorm.DB) []UserSimple {
var users []UserSimple
db.Model(&User{}).
Select("id, name, email, created_at"). // 只查询必要字段
Find(&users)
return users
}
// ✅ 使用 DTO 而不是完整实体
type UserDTO struct {
ID int64 `json:"id"`
Name string `json:"name"`
Email string `json:"email"`
CreatedAt time.Time `json:"created_at"`
// 不包含 Avatar、Description 等大字段
}
// 性能提升:
// 假设 users 表有 20 个字段,包含大字段
// ❌ SELECT *:每行数据 5KB,100 行 = 500KB
// ✅ SELECT 4 字段:每行数据 0.5KB,100 行 = 50KB
// 网络传输时间减少 90%!
📌 批量操作优化
适用场景:批量插入、更新、删除操作
go
// ❌ 逐条插入:慢如蜗牛
func BatchInsertBad(db *gorm.DB, orders []Order) error {
for _, order := range orders {
if err := db.Create(&order).Error; err != nil {
return err
}
}
return nil
}
// 1000 条数据需要 1000 次网络往返,耗时约 10 秒
// ✅ 批量插入:快如闪电
func BatchInsertGood(db *gorm.DB, orders []Order) error {
// 每批 100 条
return db.CreateInBatches(orders, 100).Error
}
// 1000 条数据只需 10 次网络往返,耗时约 200ms
// ✅ 使用事务批量插入(更快)
func BatchInsertBest(db *gorm.DB, orders []Order) error {
return db.Transaction(func(tx *gorm.DB) error {
return tx.CreateInBatches(orders, 100).Error
})
}
// 性能对比:
// ❌ 逐条插入: 10,000 ms
// ✅ 批量插入: 200 ms (50倍提升)
// ✅ 事务批量: 150 ms (66倍提升)
1.2 索引优化的艺术
"索引就像书的目录,没有它你只能一页页翻"
📌 索引的基本原理
go
// 索引前后的性能对比示例
// 假设 users 表有 100 万条数据
// ❌ 没有索引:全表扫描
// SELECT * FROM users WHERE email = 'test@example.com'
// 扫描行数:1,000,000 行
// 查询时间:5000ms
// ✅ 添加索引后
// CREATE INDEX idx_email ON users(email)
// 扫描行数:1 行
// 查询时间:5ms
📌 索引类型选择
go
// 1. 单列索引:最常见
type User struct {
ID int64 `gorm:"primaryKey"`
Email string `gorm:"index"` // 单列索引
Name string
}
// 2. 复合索引:多列组合
type Order struct {
ID int64 `gorm:"primaryKey"`
UserID int64 `gorm:"index:idx_user_status"` // 复合索引第一列
Status int `gorm:"index:idx_user_status"` // 复合索引第二列
CreatedAt time.Time `gorm:"index:idx_user_status"` // 复合索引第三列
}
// 复合索引的左前缀原则:
// ✅ 可以使用索引:WHERE user_id = 1
// ✅ 可以使用索引:WHERE user_id = 1 AND status = 2
// ✅ 可以使用索引:WHERE user_id = 1 AND status = 2 AND created_at > '2024-01-01'
// ❌ 不能使用索引:WHERE status = 2 (没有最左列)
// ❌ 不能使用索引:WHERE created_at > '2024-01-01' (跳过了前面的列)
// 3. 唯一索引:保证唯一性
type User struct {
ID int64 `gorm:"primaryKey"`
Email string `gorm:"uniqueIndex"` // 唯一索引
}
// 4. 覆盖索引:索引包含所有需要的字段
type User struct {
ID int64 `gorm:"primaryKey"`
Email string `gorm:"index:idx_email_name"` // 复合索引
Name string `gorm:"index:idx_email_name"` // 同一个索引
}
// 查询时只使用索引列,不需要回表
// SELECT email, name FROM users WHERE email = 'test@example.com'
// 直接从索引获取数据,更快!
📌 索引失效的常见场景
go
// ❌ 场景1:在索引列上使用函数
db.Where("DATE(created_at) = ?", "2024-01-01").Find(&orders)
// 索引失效,全表扫描
// ✅ 改为范围查询
startDate := time.Date(2024, 1, 1, 0, 0, 0, 0, time.UTC)
endDate := startDate.AddDate(0, 0, 1)
db.Where("created_at >= ? AND created_at < ?", startDate, endDate).Find(&orders)
// ❌ 场景2:隐式类型转换
// id 是 int64 类型,但传入字符串
db.Where("id = ?", "12345").Find(&user) // 索引可能失效
// ✅ 使用正确的类型
db.Where("id = ?", int64(12345)).Find(&user)
// ❌ 场景3:前缀模糊查询
db.Where("name LIKE ?", "%张三%").Find(&users) // 索引失效
// ✅ 只用后缀模糊查询(如果业务允许)
db.Where("name LIKE ?", "张三%").Find(&users)
// ✅ 或使用全文索引
// ALTER TABLE users ADD FULLTEXT INDEX idx_name_fulltext(name);
db.Where("MATCH(name) AGAINST(?)", "张三").Find(&users)
// ❌ 场景4:使用 OR 连接不同列
db.Where("email = ? OR phone = ?", email, phone).Find(&users) // 索引失效
// ✅ 改用 UNION
var users []User
db.Where("email = ?", email).Find(&users)
var phoneUsers []User
db.Where("phone = ?", phone).Find(&phoneUsers)
users = append(users, phoneUsers...)
// ❌ 场景5:不等于操作
db.Where("status != ?", 1).Find(&orders) // 可能不走索引
// ✅ 使用 IN 指定具体值
db.Where("status IN ?", []int{0, 2, 3, 4}).Find(&orders)
📌 索引优化实战案例
go
// 案例:电商订单查询优化
// 需求:查询某用户最近30天的已完成订单,按时间倒序
// ❌ 糟糕的索引设计
type Order struct {
ID int64
UserID int64 `gorm:"index"` // 单独的索引
Status int `gorm:"index"` // 单独的索引
CreatedAt time.Time `gorm:"index"` // 单独的索引
Amount float64
}
// 查询会使用其中一个索引,但不是最优的
// ✅ 优秀的索引设计
type Order struct {
ID int64
UserID int64 `gorm:"index:idx_user_time_status"`
CreatedAt time.Time `gorm:"index:idx_user_time_status"`
Status int `gorm:"index:idx_user_time_status"`
Amount float64
}
// 复合索引完美匹配查询条件
func GetUserRecentOrders(db *gorm.DB, userID int64) []Order {
var orders []Order
thirtyDaysAgo := time.Now().AddDate(0, 0, -30)
db.Where("user_id = ? AND created_at > ? AND status = ?",
userID, thirtyDaysAgo, StatusCompleted).
Order("created_at DESC").
Find(&orders)
return orders
}
// 性能提升:
// ❌ 单独索引:扫描 50,000 行,耗时 500ms
// ✅ 复合索引:扫描 50 行,耗时 5ms
// 提升 100 倍!
1.3 查询优化技巧
📌 分页优化:深度分页的噩梦
go
// ❌ 传统分页:OFFSET 越大越慢
func GetUsersPaginationBad(db *gorm.DB, page, pageSize int) []User {
var users []User
offset := (page - 1) * pageSize
db.Offset(offset).Limit(pageSize).Find(&users)
// SELECT * FROM users LIMIT 20 OFFSET 100000
// 需要先扫描前 100,000 条,然后丢弃,再取 20 条
// 当 OFFSET 很大时,性能急剧下降
return users
}
// 第 1 页:耗时 10ms
// 第 100 页:耗时 50ms
// 第 5000 页:耗时 2000ms 😱
// ✅ 游标分页:性能稳定
func GetUsersPaginationGood(db *gorm.DB, lastID int64, pageSize int) []User {
var users []User
query := db.Limit(pageSize).Order("id ASC")
if lastID > 0 {
query = query.Where("id > ?", lastID)
}
query.Find(&users)
// SELECT * FROM users WHERE id > 100000 LIMIT 20
// 直接从指定位置开始,性能稳定
return users
}
// 所有页面:耗时都在 10ms 左右 ✨
// ✅ 游标分页的完整实现
type PageResult struct {
Data []User `json:"data"`
LastID int64 `json:"last_id"` // 下一页的游标
HasMore bool `json:"has_more"` // 是否还有更多数据
}
func GetUsersWithCursor(db *gorm.DB, lastID int64, pageSize int) PageResult {
var users []User
// 多查一条判断是否还有数据
query := db.Limit(pageSize + 1).Order("id ASC")
if lastID > 0 {
query = query.Where("id > ?", lastID)
}
query.Find(&users)
hasMore := len(users) > pageSize
if hasMore {
users = users[:pageSize] // 去掉多查的那一条
}
var newLastID int64
if len(users) > 0 {
newLastID = users[len(users)-1].ID
}
return PageResult{
Data: users,
LastID: newLastID,
HasMore: hasMore,
}
}
// 前端调用示例:
// 第一页:GET /users?page_size=20
// 第二页:GET /users?last_id=123&page_size=20
// 第三页:GET /users?last_id=456&page_size=20
📌 子查询优化
go
// ❌ 使用子查询(可能很慢)
func GetActiveUsersWithOrdersBad(db *gorm.DB) []User {
var users []User
db.Raw(`
SELECT * FROM users
WHERE id IN (
SELECT DISTINCT user_id FROM orders
WHERE created_at > ?
)
`, time.Now().AddDate(0, -1, 0)).Scan(&users)
return users
}
// ✅ 使用 JOIN(通常更快)
func GetActiveUsersWithOrdersGood(db *gorm.DB) []User {
var users []User
db.Table("users u").
Select("DISTINCT u.*").
Joins("INNER JOIN orders o ON u.id = o.user_id").
Where("o.created_at > ?", time.Now().AddDate(0, -1, 0)).
Scan(&users)
return users
}
// 性能对比:
// ❌ 子查询:可能执行两次全表扫描
// ✅ JOIN:只需一次扫描 + 索引查找
📌 COUNT 查询优化
go
// ❌ SELECT COUNT(*) 在大表上很慢
func GetTotalUserCountBad(db *gorm.DB) int64 {
var count int64
db.Model(&User{}).Count(&count)
// 在千万级别的表上可能需要几秒钟
return count
}
// ✅ 方案1:使用缓存
func GetTotalUserCountGood(db *gorm.DB, cache *redis.Client) int64 {
cacheKey := "user:total_count"
// 先从缓存获取
if val, err := cache.Get(context.Background(), cacheKey).Int64(); err == nil {
return val
}
// 缓存未命中,查询数据库
var count int64
db.Model(&User{}).Count(&count)
// 写入缓存,1小时过期
cache.Set(context.Background(), cacheKey, count, time.Hour)
return count
}
// ✅ 方案2:使用近似值(适用于不需要精确计数的场景)
func GetApproximateCount(db *gorm.DB) int64 {
var result struct {
Count int64
}
db.Raw(`
SELECT table_rows as count
FROM information_schema.tables
WHERE table_schema = DATABASE()
AND table_name = 'users'
`).Scan(&result)
return result.Count
}
// 几乎瞬间返回,但是近似值
// ✅ 方案3:异步更新计数
// 使用单独的计数表,通过触发器或异步任务更新
type Statistics struct {
ID int64
TotalUsers int64
UpdatedAt time.Time
}
func GetCachedCount(db *gorm.DB) int64 {
var stats Statistics
db.First(&stats)
return stats.TotalUsers
}
// 查询极快,但需要维护计数表
1.4 连接池优化
go
// 📌 连接池配置优化
func InitDBWithPool() *gorm.DB {
db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{})
if err != nil {
panic(err)
}
sqlDB, err := db.DB()
if err != nil {
panic(err)
}
// ✅ 连接池配置
// 最大空闲连接数
// 原则:根据并发请求数设置,一般为最大连接数的 1/2
sqlDB.SetMaxIdleConns(50)
// 最大打开连接数
// 原则:不要超过数据库的最大连接数限制
// MySQL 默认:151,生产环境通常设置为 1000+
sqlDB.SetMaxOpenConns(100)
// 连接最大存活时间
// 原则:小于数据库的 wait_timeout(MySQL 默认 8 小时)
sqlDB.SetConnMaxLifetime(time.Hour)
// 连接最大空闲时间
// 原则:根据请求频率设置,低频可以设短一些
sqlDB.SetConnMaxIdleTime(10 * time.Minute)
return db
}
// 连接池大小计算公式:
// 最大连接数 = ((核心数 * 2) + 有效磁盘数)
// 例如:4核CPU + 1个磁盘 = (4 * 2) + 1 = 9个连接
// ⚠️ 常见问题:
// 1. 连接数过小:高并发时连接不够,请求等待
// 2. 连接数过大:消耗数据库资源,增加调度开销
// 3. 没有设置超时:连接泄露导致连接耗尽
1.5 读写分离与分库分表
📌 读写分离实现
go
// ✅ 使用 GORM 实现读写分离
import (
"gorm.io/driver/mysql"
"gorm.io/gorm"
"gorm.io/plugin/dbresolver"
)
func InitDBWithReadWrite() *gorm.DB {
// 主库(写)
db, err := gorm.Open(mysql.Open(masterDSN), &gorm.Config{})
if err != nil {
panic(err)
}
// 配置读写分离
db.Use(dbresolver.Register(dbresolver.Config{
// 从库(读)
Replicas: []gorm.Dialector{
mysql.Open(slave1DSN),
mysql.Open(slave2DSN),
},
// 负载均衡策略
Policy: dbresolver.RandomPolicy{},
}))
return db
}
// 使用示例:
func Example(db *gorm.DB) {
// 读操作自动路由到从库
var users []User
db.Find(&users) // 从库
// 写操作自动路由到主库
db.Create(&User{Name: "test"}) // 主库
// 强制使用主库
db.Clauses(dbresolver.Write).Find(&users) // 主库
}
// 📊 读写分离的性能提升:
// - 读请求分散到多个从库,QPS 可提升 3-5 倍
// - 主库专注处理写请求,写入性能更稳定
// - 适用场景:读多写少的业务(读写比 > 3:1)
📌 分库分表策略
go
// 场景:订单表数据量过大,需要分表
// ✅ 方案1:按时间分表(适合时序数据)
func GetOrderTableByTime(orderTime time.Time) string {
// 按月分表:orders_202401, orders_202402...
return fmt.Sprintf("orders_%s", orderTime.Format("200601"))
}
func CreateOrder(db *gorm.DB, order Order) error {
tableName := GetOrderTableByTime(order.CreatedAt)
return db.Table(tableName).Create(&order).Error
}
// ✅ 方案2:按用户ID分表(适合用户数据)
func GetOrderTableByUserID(userID int64) string {
// 按用户ID取模:orders_0, orders_1, ..., orders_9
tableIndex := userID % 10
return fmt.Sprintf("orders_%d", tableIndex)
}
func GetUserOrders(db *gorm.DB, userID int64) []Order {
var orders []Order
tableName := GetOrderTableByUserID(userID)
db.Table(tableName).Where("user_id = ?", userID).Find(&orders)
return orders
}
// ✅ 方案3:使用 ShardingSphere 等中间件
// ShardingSphere 可以自动处理分片路由,对业务代码透明
// 分表建议:
// - 单表数据量 < 500万 时无需分表
// - 单表数据量 500万-2000万 时考虑分表
// - 单表数据量 > 2000万 时强烈建议分表