Skip to content

Latest commit

 

History

14 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

hecc-blot-cache

本地内存 + Redis 双层缓存:Hash 操作、过期清理、读穿透,可选接入链路追踪。

安装

go get github.com/hecc-blot/cache

接口定义

import cacheContract "github.com/hecc-blot/cache/contract"

type ICacheFactory interface {
    Local() ILocalCache
    Redis() IRedisCache
    Orchestrator() IOrchestrator
}

type IBaseCache interface {
    Set(ctx context.Context, key string, val interface{}, expire time.Duration) error
    Get(ctx context.Context, key string) (interface{}, error)
    Del(ctx context.Context, key string) error
    Exists(ctx context.Context, key string) (bool, error)
}

type LoadFunc func(ctx context.Context) (interface{}, error)

type IOrchestrator interface {
    GetOrLoad(ctx context.Context, key string, load LoadFunc) (interface{}, error)
    GetOrLoadWithTTL(ctx context.Context, key string, load LoadFunc, ttl time.Duration) (interface{}, error)
}

type ILocalCache interface {
    IBaseCache
}

type IRedisCache interface {
    IBaseCache
    HSet(ctx context.Context, key string, values ...interface{}) error
    HGet(ctx context.Context, key, field string) (string, error)
    HDel(ctx context.Context, key string, fields ...string) error
    Close() error
}

初始化

import (
    cache "github.com/hecc-blot/cache/service"
)

// 传入 traceSvc 以开启缓存操作的链路追踪,不需要可传 nil
cacheFactory := cache.NewCacheFactory(&config.Cache, traceSvc)

container.Set(new(cacheContract.ICacheFactory), cacheFactory)

本地缓存

基于 sync.RWMutex 的内存缓存,定时清理过期条目。

// Set — 写入缓存(expire 为 0 表示永不过期)
err := cacheFactory.Local().Set(ctx, "user:1", userData, 10*time.Minute)

// Get — 读取缓存(key 不存在或已过期返回 nil, nil)
result, err := cacheFactory.Local().Get(ctx, "user:1")

// Del — 删除缓存
err = cacheFactory.Local().Del(ctx, "user:1")

// Exists — 判断 key 是否存在
ok, err := cacheFactory.Local().Exists(ctx, "user:1")

过期清理

配置 clear_interval 后,框架启动独立 goroutine 定期清理过期条目(读锁收集 → 写锁二次确认删除,不阻塞正常读写):

cache:
  local:
    enable: true
    clear_interval: 3600  # 每隔 3600 秒清理一次,≤0 不启动

Redis 缓存

基于 go-redis v9,支持 String 和 Hash 操作。

// 基础操作
err := cacheFactory.Redis().Set(ctx, "key", "value", time.Hour)
result, err := cacheFactory.Redis().Get(ctx, "key")
err = cacheFactory.Redis().Del(ctx, "key")
ok, err := cacheFactory.Redis().Exists(ctx, "key")

// Hash 操作
err = cacheFactory.Redis().HSet(ctx, "user:profile", "name", "john", "age", "30")
name, err := cacheFactory.Redis().HGet(ctx, "user:profile", "name")
err = cacheFactory.Redis().HDel(ctx, "user:profile", "name", "age")

// 关闭连接(框架不自动关闭)
cacheFactory.Redis().Close()

缓存编排层(cache-aside + singleflight)

基础 Get/Set 只提供原语,缓存穿透/击穿/回填需要业务手动编排。编排层 IOrchestrator 把「查缓存 → 查库 → 回填」这条链路做成一等公民:业务只传「取数闭包」,框架统一处理缓存未命中、并发合并、回填与空值防穿透。

type GetApi struct {
    CacheFactory cacheContract.ICacheFactory `inject:""`
    DbFactory    dbContract.IDbFactory       `inject:""`
}

func (a GetApi) Call(ctx *gin.Context) (interface{}, iCoreError.IError) {
    // 一行:缓存命中直接返回;未命中调用闭包取数并回填缓存
    val, err := a.CacheFactory.Orchestrator().GetOrLoad(ctx, "account:1",
        func(ctx context.Context) (interface{}, error) {
            db := a.DbFactory.Build(ctx)
            data := AccountModel{}
            if err := db.Where("id = ?", 1).Take(&data); err != nil {
                return nil, err
            }
            return data, nil
        })
    if err != nil {
        return nil, errorSvc.NewError(response.Fail, err)
    }
    return val, nil
}

内置能力:

  • singleflight 防击穿:同一 key 的并发未命中只触发一次取数,其余请求等待共享结果,避免瞬时打爆数据源。
  • cache-aside 回填:未命中取数成功后自动写回缓存(GetOrLoad 默认 5 分钟;GetOrLoadWithTTL 可显式指定 TTL,0 表示永不过期)。
  • 空值防穿透:取数闭包返回 (nil, nil)(数据不存在)时,框架缓存空值哨兵(短 TTL 1 分钟),避免不存在的 key 反复穿透到数据源。

编排层默认基于本地缓存(Go 值原生往返,适合结构化对象)。Redis 后端 Get 返回字符串,仅适合字符串值场景;如需基于 Redis 编排,可 service.NewOrchestrator(cacheFactory.Redis(), traceSvc) 显式构造。

链路追踪集成

传入 traceSvc 后,每次缓存操作自动创建 Span:

  • 本地缓存:记录 SET / GET / DEL / EXISTS
  • Redis 缓存:记录 SET / GET / DEL / EXISTS / HSET / HGET / HDEL

Span 属性包含 cache.typecache.operationcache.key(Redis 为 db.system: redis)。

配置

cache:
  local:
    enable: true
    clear_interval: 3600       # 过期缓存清理间隔(秒)

  redis:
    addr: "127.0.0.1:6379"     # Redis 地址
    password: ""               # Redis 密码
    db: 0                      # Redis DB 编号
    pool_size: 100             # 连接池大小

相关模块

模块 说明
db 缓存读穿透的数据源
trace 缓存操作的 Trace Span
framework IOC 注入与统一错误

About

本地内存 + Redis 双层缓存:Hash 操作、过期清理、读穿透

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages