golang mysql redis 缓存
时间: 2025-06-11 11:29:14 浏览: 15
### Golang中使用MySQL和Redis实现缓存的示例教程
在Golang项目中,结合MySQL和Redis可以有效提升数据读取性能并减少数据库压力。以下是一个完整的示例,展示如何在事务提交后更新Redis缓存,确保缓存与数据库的一致性。
#### 1. 数据库操作与事务管理
首先,通过GORM或`database/sql`进行数据库操作,并在事务完成后更新Redis缓存。以下是一个基于GORM的示例:
```go
package main
import (
"fmt"
"github.com/go-redis/redis/v8"
"gorm.io/driver/mysql"
"gorm.io/gorm"
)
var rdb *redis.Client
var db *gorm.DB
func init() {
// 初始化Redis客户端
rdb = redis.NewClient(&redis.Options{
Addr: "localhost:6379",
Password: "", // no password set
DB: 0, // use default DB
})
// 初始化MySQL数据库连接
var err error
dsn := "user:password@tcp(127.0.0.1:3306)/dbname?charset=utf8mb4&parseTime=True&loc=Local"
db, err = gorm.Open(mysql.Open(dsn), &gorm.Config{})
if err != nil {
panic("failed to connect database")
}
}
func main() {
// 开始事务
err := db.Transaction(func(tx *gorm.DB) error {
// 创建用户记录
user := User{Name: "Alice", Age: 25}
if err := tx.Create(&user).Error; err != nil {
return err
}
// 更新Redis缓存(仅在事务成功提交后执行)
go updateCache(user.ID, user.Name)
return nil
})
if err != nil {
fmt.Println("Transaction failed:", err)
} else {
fmt.Println("Transaction succeeded")
}
}
// 更新Redis缓存
func updateCache(id uint, name string) {
ctx := context.Background()
key := fmt.Sprintf("user:%d", id)
err := rdb.Set(ctx, key, name, 0).Err()
if err != nil {
fmt.Println("Failed to update cache:", err)
} else {
fmt.Println("Cache updated successfully")
}
}
// 定义User结构体
type User struct {
ID uint
Name string
Age int
}
```
上述代码展示了如何在事务提交后更新Redis缓存[^1]。通过这种方式,可以避免事务回滚导致的缓存与数据库不一致问题。
#### 2. Redis查询与更新
为了支持复杂的查询需求,可以将Redis作为缓存层存储部分数据。以下是一个查询和更新的示例:
```go
func GetRedisById(rdb *redis.Client, tableName string, id uint) (string, error) {
ctx := context.Background()
key := fmt.Sprintf("%s:%d", tableName, id)
val, err := rdb.Get(ctx, key).Result()
if err == redis.Nil {
return "", fmt.Errorf("key %s not found", key)
} else if err != nil {
return "", err
}
return val, nil
}
func UpdateRedisById(db *gorm.DB, rdb *redis.Client, tableName string, id uint) error {
// 查询数据库
var user User
err := db.First(&user, id).Error
if err != nil {
return err
}
// 更新Redis缓存
ctx := context.Background()
key := fmt.Sprintf("%s:%d", tableName, id)
err = rdb.Set(ctx, key, user.Name, 0).Err()
if err != nil {
return err
}
return nil
}
```
上述代码实现了基于ID的Redis查询和更新功能[^3]。
#### 3. 缓存穿透与击穿的解决方案
为了避免缓存穿透和击穿问题,可以采取以下措施:
- **缓存穿透**:对于不存在的键,设置一个空值并设置较短的过期时间。
- **缓存击穿**:为热点数据设置永不过期的缓存,或者使用互斥锁机制。
```go
func GetWithLock(rdb *redis.Client, key string) (string, error) {
ctx := context.Background()
// 尝试获取缓存
val, err := rdb.Get(ctx, key).Result()
if err == redis.Nil {
// 设置分布式锁
lockKey := fmt.Sprintf("lock:%s", key)
acquired, err := rdb.SetNX(ctx, lockKey, "1", 10*time.Second).Result()
if err != nil || !acquired {
return "", fmt.Errorf("failed to acquire lock for key %s", key)
}
// 缓存未命中,从数据库加载数据
val = loadDataFromDatabase(key)
if val == "" {
// 设置空值以防止穿透
rdb.Set(ctx, key, "NULL", 5*time.Minute).Err()
} else {
// 更新缓存
rdb.Set(ctx, key, val, 0).Err()
}
// 释放锁
rdb.Del(ctx, lockKey)
}
return val, nil
}
```
上述代码通过分布式锁解决了缓存击穿问题[^3]。
---
###
阅读全文
相关推荐


















