Golang微服务缓存机制探讨

Golang微服务缓存机制探讨 在 Go 微服务中我们将进行何种缓存。该服务将从 Oracle 数据库拉取记录。

2 回复

更多关于Golang微服务缓存机制探讨的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


在Go微服务中实现缓存机制时,通常采用多级缓存策略以提高性能并减轻数据库压力。以下是针对Oracle数据库的几种常见缓存方案及示例代码:

1. 内存缓存(使用sync.Map或第三方库)

import (
    "sync"
    "time"
)

type CacheItem struct {
    Value      interface{}
    Expiration int64
}

type MemoryCache struct {
    items sync.Map
    mu    sync.RWMutex
}

func (c *MemoryCache) Set(key string, value interface{}, duration time.Duration) {
    expiration := time.Now().Add(duration).UnixNano()
    c.items.Store(key, CacheItem{
        Value:      value,
        Expiration: expiration,
    })
}

func (c *MemoryCache) Get(key string) (interface{}, bool) {
    item, found := c.items.Load(key)
    if !found {
        return nil, false
    }
    cacheItem := item.(CacheItem)
    if time.Now().UnixNano() > cacheItem.Expiration {
        c.items.Delete(key)
        return nil, false
    }
    return cacheItem.Value, true
}

2. Redis分布式缓存

import (
    "context"
    "encoding/json"
    "github.com/go-redis/redis/v8"
    "time"
)

type RedisCache struct {
    client *redis.Client
    ctx    context.Context
}

func NewRedisCache(addr string) *RedisCache {
    return &RedisCache{
        client: redis.NewClient(&redis.Options{
            Addr: addr,
        }),
        ctx: context.Background(),
    }
}

func (rc *RedisCache) Set(key string, value interface{}, expiration time.Duration) error {
    jsonData, err := json.Marshal(value)
    if err != nil {
        return err
    }
    return rc.client.Set(rc.ctx, key, jsonData, expiration).Err()
}

func (rc *RedisCache) Get(key string, dest interface{}) error {
    data, err := rc.client.Get(rc.ctx, key).Bytes()
    if err != nil {
        return err
    }
    return json.Unmarshal(data, dest)
}

3. 数据库查询结果缓存示例

type UserService struct {
    db    *sql.DB
    cache *MemoryCache
}

func (s *UserService) GetUserByID(userID string) (*User, error) {
    // 先尝试从缓存获取
    if cached, found := s.cache.Get("user_" + userID); found {
        return cached.(*User), nil
    }

    // 缓存未命中,查询数据库
    var user User
    query := `SELECT id, name, email FROM users WHERE id = :1`
    err := s.db.QueryRow(query, userID).Scan(&user.ID, &user.Name, &user.Email)
    if err != nil {
        return nil, err
    }

    // 存入缓存,设置5分钟过期
    s.cache.Set("user_"+userID, &user, 5*time.Minute)
    return &user, nil
}

4. 缓存穿透防护(布隆过滤器实现)

import "github.com/bits-and-blooms/bloom/v3"

type BloomFilterCache struct {
    filter *bloom.BloomFilter
    cache  *MemoryCache
}

func NewBloomFilterCache() *BloomFilterCache {
    return &BloomFilterCache{
        filter: bloom.NewWithEstimates(1000000, 0.01),
        cache:  &MemoryCache{},
    }
}

func (bfc *BloomFilterCache) GetWithProtection(key string) (interface{}, bool) {
    // 先检查布隆过滤器
    if !bfc.filter.TestString(key) {
        return nil, false
    }
    
    // 再检查实际缓存
    return bfc.cache.Get(key)
}

5. 缓存预热机制

func (s *UserService) WarmUpCache() {
    rows, err := s.db.Query("SELECT id, name, email FROM users WHERE active = 1")
    if err != nil {
        return
    }
    defer rows.Close()

    for rows.Next() {
        var user User
        rows.Scan(&user.ID, &user.Name, &user.Email)
        s.cache.Set("user_"+user.ID, &user, 30*time.Minute)
    }
}

这些方案可根据实际场景组合使用。内存缓存适合高频访问的少量数据,Redis适合分布式环境下的数据共享,布隆过滤器能有效防止缓存穿透。对于Oracle数据库,建议在服务启动时进行缓存预热,并在数据更新时实现缓存失效策略。

回到顶部