Golang后端性能优化手册(第一章:数据库性能优化)

前言:

"过早优化是万恶之源,但过晚优化可能让你失去用户"

---这是一篇帮助 你我 更好的做牛马,做更好的牛马 的文档

---刚刚开始,慢慢来

📋 目录

  • [🎯 文档说明](#🎯 文档说明)
  • [📊 性能优化全景图](#📊 性能优化全景图)
  • 💾 [第一章:数据库性能优化(#第一章数据库性能优化)- 点击跳转相应文档](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 读写分离与分库分表)
  • ⚡ [第二章:缓存策略与优化(#第二章缓存策略与优化) 点击跳转相应文档](https://blog.csdn.net/bojinyuan00/article/details/156271855?spm=1001.2014.3001.5502)
    • [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 倍,还能节省大量服务器成本。

本手册的特色

  • ✅ 实战导向:每个优化点都配有真实代码示例
  • ✅ 场景明确:清晰说明每种优化的适用场景
  • ✅ 对比鲜明:用 ❌ 和 ✅ 直观展示好坏实践
  • ✅ 深入浅出:用生动的比喻解释复杂概念
  • ✅ 可操作性强:提供完整的代码和配置示例

如何使用本手册

  1. 快速诊断:遇到性能问题时,查找对应章节
  2. 系统学习:按章节顺序学习性能优化知识体系
  3. 代码审查:用 Checklist 检查现有项目
  4. 方案设计:参考架构章节设计高性能系统

性能优化的黄金法则

💡 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万 时强烈建议分表

相关推荐
林伽一4 分钟前
从对比语言模型到智能体治理,AI基础设施迎来新一轮重构 | 2026年09月26日
人工智能·科技·ai
回眸&啤酒鸭6 分钟前
【回眸】AI 电商盲盒怎么营收?从营销创新到运营提效
人工智能
艺杯羹8 分钟前
240GB流量击穿与频繁OOM:从云端瘫痪到客户端自治的本地优先架构重构实录
性能优化·系统架构·fastapi·桌面客户端·本地优先
TK泰妞9 分钟前
从零搭建高效可复用的提示词工作流
大数据·人工智能
Zhou14113610 分钟前
SpringMVC_02_注解开发实战
开发语言·windows·python
温暖小土13 分钟前
Spring AI 接入 DeepSeek Chat 模型
数据库·人工智能·spring
GitCode官方15 分钟前
“开源共生 共赢未来”2026北京昇腾生态先锋中心系列活动之算子实操工坊成功举办
人工智能·开源·昇腾·atomgit
空奈qwq16 分钟前
深度学习入门指南:从核心概念到 PyTorch 实战
人工智能·pytorch·深度学习
红姐跨境书16 分钟前
AI 的底层逻辑:从数据、算法到智能的本质
人工智能·算法
繁华的地方不一定留下你的脚印17 分钟前
C++ 算法与 ranges:用 find_if、transform 写清数据处理意图
开发语言·c++·算法