hotime/cache/cache.go
hoteas 3d83c41905 feat(cache): 增加批量操作支持以提升性能
- 在 HoTimeCache 中新增 SessionsGet、SessionsSet 和 SessionsDelete 方法,支持批量获取、设置和删除 Session 缓存
- 优化缓存逻辑,减少数据库写入次数,提升性能
- 更新文档,详细说明批量操作的使用方法和性能对比
- 添加调试日志记录,便于追踪批量操作的执行情况
2026-01-30 17:51:43 +08:00

605 lines
16 KiB
Go
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package cache
import (
"encoding/json"
"errors"
"os"
"time"
. "code.hoteas.com/golang/hotime/common"
)
const debugLogPath = `d:\work\hotimev1.5\.cursor\debug.log`
// debugLog 写入调试日志
func debugLog(hypothesisId, location, message string, data map[string]interface{}) {
// #region agent log
logFile, _ := os.OpenFile(debugLogPath, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
if logFile != nil {
logEntry, _ := json.Marshal(map[string]interface{}{
"sessionId": "cache-debug",
"runId": "test-run",
"hypothesisId": hypothesisId,
"location": location,
"message": message,
"data": data,
"timestamp": time.Now().UnixMilli(),
})
logFile.Write(append(logEntry, '\n'))
logFile.Close()
}
// #endregion
}
// HoTimeCache 可配置memorydbredis默认启用memory默认优先级为memory>redis>db,memory与数据库缓存设置项一致
// 缓存数据填充会自动反方向反哺加入memory缓存过期将自动从redis更新但memory永远不会更新redis如果是集群建议不要开启memory配置即启用
type HoTimeCache struct {
*Error
dbCache *CacheDb
redisCache *CacheRedis
memoryCache *CacheMemory
Config Map
}
func (that *HoTimeCache) Session(key string, data ...interface{}) *Obj {
var reData *Obj
if len(data) == 0 {
//内存缓存有
if that.memoryCache != nil && that.memoryCache.SessionSet {
reData = that.memoryCache.Cache(key, data...)
if reData.Data != nil {
return reData
}
}
//redis缓存有
if that.redisCache != nil && that.redisCache.SessionSet {
reData = that.redisCache.Cache(key, data...)
if reData.Data != nil {
if that.memoryCache != nil && that.memoryCache.SessionSet {
that.memoryCache.Cache(key, reData.Data)
}
return reData
}
}
//db缓存有
if that.dbCache != nil && that.dbCache.SessionSet {
reData = that.dbCache.Cache(key, data...)
if reData.Data != nil {
if that.memoryCache != nil && that.memoryCache.SessionSet {
that.memoryCache.Cache(key, reData.Data)
}
if that.redisCache != nil && that.redisCache.SessionSet {
that.redisCache.Cache(key, reData.Data)
}
return reData
}
}
return reData
}
//设置缓存
//设置内存缓存
if that.memoryCache != nil && that.memoryCache.SessionSet {
reData = that.memoryCache.Cache(key, data...)
}
//redis缓存有
if that.redisCache != nil && that.redisCache.SessionSet {
reData = that.redisCache.Cache(key, data...)
}
//redis缓存有
if that.dbCache != nil && that.dbCache.SessionSet {
reData = that.dbCache.Cache(key, data...)
}
return reData
}
func (that *HoTimeCache) Db(key string, data ...interface{}) *Obj {
var reData *Obj
if len(data) == 0 {
//内存缓存有
if that.memoryCache != nil && that.memoryCache.DbSet {
reData = that.memoryCache.Cache(key, data...)
if reData.Data != nil {
return reData
}
}
//redis缓存有
if that.redisCache != nil && that.redisCache.DbSet {
reData = that.redisCache.Cache(key, data...)
if reData.Data != nil {
if that.memoryCache != nil && that.memoryCache.DbSet {
that.memoryCache.Cache(key, reData.Data)
}
return reData
}
}
//db缓存有
if that.dbCache != nil && that.dbCache.DbSet {
reData = that.dbCache.Cache(key, data...)
if reData.Data != nil {
if that.memoryCache != nil && that.memoryCache.DbSet {
that.memoryCache.Cache(key, reData.Data)
}
if that.redisCache != nil && that.redisCache.DbSet {
that.redisCache.Cache(key, reData.Data)
}
return reData
}
}
return reData
}
//设置缓存
//设置内存缓存
if that.memoryCache != nil && that.memoryCache.DbSet {
reData = that.memoryCache.Cache(key, data...)
}
//redis缓存有
if that.redisCache != nil && that.redisCache.DbSet {
reData = that.redisCache.Cache(key, data...)
}
//db缓存有
if that.dbCache != nil && that.dbCache.DbSet {
reData = that.dbCache.Cache(key, data...)
}
return reData
}
func (that *HoTimeCache) Cache(key string, data ...interface{}) *Obj {
var reData *Obj
if len(data) == 0 {
//内存缓存有
if that.memoryCache != nil {
reData = that.memoryCache.Cache(key, data...)
if reData != nil && reData.Data != nil {
return reData
}
}
//redis缓存有
if that.redisCache != nil {
reData = that.redisCache.Cache(key, data...)
if reData != nil && reData.Data != nil {
if that.memoryCache != nil {
that.memoryCache.Cache(key, reData.Data)
}
return reData
}
}
//db缓存有
if that.dbCache != nil {
reData = that.dbCache.Cache(key, data...)
if reData != nil && reData.Data != nil {
if that.memoryCache != nil {
that.memoryCache.Cache(key, reData.Data)
}
if that.redisCache != nil {
that.redisCache.Cache(key, reData.Data)
}
return reData
}
}
return reData
}
//设置缓存
//设置内存缓存
if that.memoryCache != nil {
reData = that.memoryCache.Cache(key, data...)
}
//redis缓存有
if that.redisCache != nil {
reData = that.redisCache.Cache(key, data...)
}
//db缓存有
if that.dbCache != nil {
reData = that.dbCache.Cache(key, data...)
}
return reData
}
// SessionsGet 批量获取 Session 缓存
// 返回 Mapkey 为缓存键value 为缓存值
// 优先级memory > redis > db低优先级数据会反哺到高优先级缓存
func (that *HoTimeCache) SessionsGet(keys []string) Map {
if len(keys) == 0 {
return Map{}
}
// #region agent log
debugLog("D", "cache.go:SessionsGet:start", "SessionsGet开始", map[string]interface{}{
"keys_count": len(keys),
"has_memory": that.memoryCache != nil,
"has_redis": that.redisCache != nil,
"has_db": that.dbCache != nil,
})
// #endregion
result := make(Map, len(keys))
missingKeys := keys
// 从 memory 获取
if that.memoryCache != nil && that.memoryCache.SessionSet {
memResult := that.memoryCache.CachesGet(keys)
// #region agent log
debugLog("D", "cache.go:SessionsGet:memory", "从Memory获取", map[string]interface{}{
"found_count": len(memResult),
})
// #endregion
for k, v := range memResult {
result[k] = v
}
// 计算未命中的 keys
missingKeys = make([]string, 0)
for _, k := range keys {
if _, exists := result[k]; !exists {
missingKeys = append(missingKeys, k)
}
}
}
// 从 redis 获取未命中的
if len(missingKeys) > 0 && that.redisCache != nil && that.redisCache.SessionSet {
redisResult := that.redisCache.CachesGet(missingKeys)
// #region agent log
debugLog("D", "cache.go:SessionsGet:redis", "从Redis获取", map[string]interface{}{
"missing_count": len(missingKeys),
"found_count": len(redisResult),
})
// #endregion
// 反哺到 memory
if that.memoryCache != nil && that.memoryCache.SessionSet && len(redisResult) > 0 {
that.memoryCache.CachesSet(redisResult)
// #region agent log
debugLog("D", "cache.go:SessionsGet:backfill_redis_to_mem", "Redis数据反哺到Memory", map[string]interface{}{
"backfill_count": len(redisResult),
})
// #endregion
}
for k, v := range redisResult {
result[k] = v
}
// 更新未命中的 keys
newMissing := make([]string, 0)
for _, k := range missingKeys {
if _, exists := result[k]; !exists {
newMissing = append(newMissing, k)
}
}
missingKeys = newMissing
}
// 从 db 获取未命中的
if len(missingKeys) > 0 && that.dbCache != nil && that.dbCache.SessionSet {
dbResult := that.dbCache.CachesGet(missingKeys)
// #region agent log
debugLog("D", "cache.go:SessionsGet:db", "从DB获取", map[string]interface{}{
"missing_count": len(missingKeys),
"found_count": len(dbResult),
})
// #endregion
// 反哺到 memory 和 redis
if len(dbResult) > 0 {
if that.memoryCache != nil && that.memoryCache.SessionSet {
that.memoryCache.CachesSet(dbResult)
// #region agent log
debugLog("D", "cache.go:SessionsGet:backfill_db_to_mem", "DB数据反哺到Memory", map[string]interface{}{
"backfill_count": len(dbResult),
})
// #endregion
}
if that.redisCache != nil && that.redisCache.SessionSet {
that.redisCache.CachesSet(dbResult)
// #region agent log
debugLog("D", "cache.go:SessionsGet:backfill_db_to_redis", "DB数据反哺到Redis", map[string]interface{}{
"backfill_count": len(dbResult),
})
// #endregion
}
}
for k, v := range dbResult {
result[k] = v
}
}
// #region agent log
debugLog("D", "cache.go:SessionsGet:end", "SessionsGet完成", map[string]interface{}{
"total_found": len(result),
})
// #endregion
return result
}
// SessionsSet 批量设置 Session 缓存
// data: Mapkey 为缓存键value 为缓存值
func (that *HoTimeCache) SessionsSet(data Map) {
if len(data) == 0 {
return
}
// #region agent log
debugLog("A", "cache.go:SessionsSet:start", "SessionsSet开始", map[string]interface{}{
"data_count": len(data),
"has_memory": that.memoryCache != nil,
"has_redis": that.redisCache != nil,
"has_db": that.dbCache != nil,
})
// #endregion
if that.memoryCache != nil && that.memoryCache.SessionSet {
that.memoryCache.CachesSet(data)
// #region agent log
debugLog("A", "cache.go:SessionsSet:memory", "写入Memory完成", map[string]interface{}{"count": len(data)})
// #endregion
}
if that.redisCache != nil && that.redisCache.SessionSet {
that.redisCache.CachesSet(data)
// #region agent log
debugLog("A", "cache.go:SessionsSet:redis", "写入Redis完成", map[string]interface{}{"count": len(data)})
// #endregion
}
if that.dbCache != nil && that.dbCache.SessionSet {
that.dbCache.CachesSet(data)
// #region agent log
debugLog("A", "cache.go:SessionsSet:db", "写入DB完成", map[string]interface{}{"count": len(data)})
// #endregion
}
// #region agent log
debugLog("A", "cache.go:SessionsSet:end", "SessionsSet完成", nil)
// #endregion
}
// SessionsDelete 批量删除 Session 缓存
func (that *HoTimeCache) SessionsDelete(keys []string) {
if len(keys) == 0 {
return
}
// #region agent log
debugLog("C", "cache.go:SessionsDelete:start", "SessionsDelete开始", map[string]interface{}{
"keys_count": len(keys),
"has_memory": that.memoryCache != nil,
"has_redis": that.redisCache != nil,
"has_db": that.dbCache != nil,
})
// #endregion
if that.memoryCache != nil && that.memoryCache.SessionSet {
that.memoryCache.CachesDelete(keys)
// #region agent log
debugLog("C", "cache.go:SessionsDelete:memory", "从Memory删除完成", map[string]interface{}{"count": len(keys)})
// #endregion
}
if that.redisCache != nil && that.redisCache.SessionSet {
that.redisCache.CachesDelete(keys)
// #region agent log
debugLog("C", "cache.go:SessionsDelete:redis", "从Redis删除完成", map[string]interface{}{"count": len(keys)})
// #endregion
}
if that.dbCache != nil && that.dbCache.SessionSet {
that.dbCache.CachesDelete(keys)
// #region agent log
debugLog("C", "cache.go:SessionsDelete:db", "从DB删除完成", map[string]interface{}{"count": len(keys)})
// #endregion
}
// #region agent log
debugLog("C", "cache.go:SessionsDelete:end", "SessionsDelete完成", nil)
// #endregion
}
// CachesGet 批量获取普通缓存
// 返回 Mapkey 为缓存键value 为缓存值
// 优先级memory > redis > db低优先级数据会反哺到高优先级缓存
func (that *HoTimeCache) CachesGet(keys []string) Map {
if len(keys) == 0 {
return Map{}
}
result := make(Map, len(keys))
missingKeys := keys
// 从 memory 获取
if that.memoryCache != nil {
memResult := that.memoryCache.CachesGet(keys)
for k, v := range memResult {
result[k] = v
}
// 计算未命中的 keys
missingKeys = make([]string, 0)
for _, k := range keys {
if _, exists := result[k]; !exists {
missingKeys = append(missingKeys, k)
}
}
}
// 从 redis 获取未命中的
if len(missingKeys) > 0 && that.redisCache != nil {
redisResult := that.redisCache.CachesGet(missingKeys)
// 反哺到 memory
if that.memoryCache != nil && len(redisResult) > 0 {
that.memoryCache.CachesSet(redisResult)
}
for k, v := range redisResult {
result[k] = v
}
// 更新未命中的 keys
newMissing := make([]string, 0)
for _, k := range missingKeys {
if _, exists := result[k]; !exists {
newMissing = append(newMissing, k)
}
}
missingKeys = newMissing
}
// 从 db 获取未命中的
if len(missingKeys) > 0 && that.dbCache != nil {
dbResult := that.dbCache.CachesGet(missingKeys)
// 反哺到 memory 和 redis
if len(dbResult) > 0 {
if that.memoryCache != nil {
that.memoryCache.CachesSet(dbResult)
}
if that.redisCache != nil {
that.redisCache.CachesSet(dbResult)
}
}
for k, v := range dbResult {
result[k] = v
}
}
return result
}
// CachesSet 批量设置普通缓存
// data: Mapkey 为缓存键value 为缓存值
func (that *HoTimeCache) CachesSet(data Map) {
if len(data) == 0 {
return
}
if that.memoryCache != nil {
that.memoryCache.CachesSet(data)
}
if that.redisCache != nil {
that.redisCache.CachesSet(data)
}
if that.dbCache != nil {
that.dbCache.CachesSet(data)
}
}
// CachesDelete 批量删除普通缓存
func (that *HoTimeCache) CachesDelete(keys []string) {
if len(keys) == 0 {
return
}
if that.memoryCache != nil {
that.memoryCache.CachesDelete(keys)
}
if that.redisCache != nil {
that.redisCache.CachesDelete(keys)
}
if that.dbCache != nil {
that.dbCache.CachesDelete(keys)
}
}
func (that *HoTimeCache) Init(config Map, hotimeDb HoTimeDBInterface, err ...*Error) {
//防止空数据问题
if config == nil {
config = Map{}
}
if err[0] != nil {
that.Error = err[0]
}
that.Config = config
//memory配置初始化
memory := that.Config.GetMap("memory")
if memory == nil {
memory = Map{
"db": true,
"session": true,
"sort": 0,
"timeout": 60 * 60 * 2,
}
}
if memory.Get("db") == nil {
memory["db"] = true
}
if memory.Get("session") == nil {
memory["session"] = true
}
if memory.Get("timeout") == nil {
memory["timeout"] = 60 * 60 * 2
}
that.Config["memory"] = memory
that.memoryCache = &CacheMemory{TimeOut: memory.GetCeilInt64("timeout"),
DbSet: memory.GetBool("db"), SessionSet: memory.GetBool("session")}
if err[0] != nil {
that.memoryCache.SetError(err[0])
}
//db配置初始化
redis := that.Config.GetMap("redis")
if redis != nil {
if redis.GetString("host") == "" || redis.GetString("port") == "" {
if err[0] != nil {
err[0].SetError(errors.New("请检查redis配置host和port配置"))
}
return
}
if redis.Get("db") == nil {
redis["db"] = true
}
if redis.Get("session") == nil {
redis["session"] = true
}
if redis.Get("timeout") == nil {
redis["timeout"] = 60 * 60 * 24 * 15
}
that.Config["redis"] = redis
that.redisCache = &CacheRedis{TimeOut: redis.GetCeilInt64("timeout"),
DbSet: redis.GetBool("db"), SessionSet: redis.GetBool("session"), Host: redis.GetString("host"),
Pwd: redis.GetString("password"), Port: redis.GetCeilInt64("port")}
if err[0] != nil {
that.redisCache.SetError(err[0])
}
}
//db配置初始化
db := that.Config.GetMap("db")
if db != nil {
if db.Get("db") == nil {
db["db"] = false
}
if db.Get("session") == nil {
db["session"] = true
}
if db.Get("timeout") == nil {
db["timeout"] = 60 * 60 * 24 * 30
}
// mode 默认为 "compatible"(兼容模式,便于老系统平滑升级)
if db.Get("mode") == nil {
db["mode"] = CacheModeCompatible
}
that.Config["db"] = db
that.dbCache = &CacheDb{
TimeOut: db.GetCeilInt64("timeout"),
DbSet: db.GetBool("db"),
SessionSet: db.GetBool("session"),
HistorySet: db.GetBool("history"),
Mode: db.GetString("mode"),
Db: hotimeDb,
}
if err[0] != nil {
that.dbCache.SetError(err[0])
}
}
}