文章目录
- 一、本地缓存理论最佳实践
- 二、Go代码实践:
- 1、本地缓存设计
- 2、本地缓存加载
代码地址:https://gitee.com/lymgoforIT/golang-trick/tree/master/37-load-local-cache
一、本地缓存理论最佳实践
- 控制缓存大小:根据应用程序的需求和可用的存储空间,合理限制本地缓存的大小。过大会导致存储空间不足,过小可能无法有效缓存数据。
- 缓存过期策略:设置适当的缓存过期时间,以确保缓存的数据不会过时。根据数据的更新频率和重要性,制定合适的过期策略。
- 数据一致性:在使用本地缓存时,要考虑数据的一致性。如果数据在后端发生变化,需要及时更新本地缓存,以避免展示过时的数据。
- 缓存刷新机制:建立合适的缓存刷新机制,当后端数据发生变化时,及时更新本地缓存。可以通过定时刷新、监听后端事件或在请求数据时检查更新标志等方式实现。
- 缓存层次结构:考虑使用多层缓存结构,如内存缓存和磁盘缓存。将经常访问的数据放在内存中,可以提供更快的访问速度,而将不经常访问的数据放在磁盘上,以节省内存空间。
- 数据压缩:对于大数据量的缓存,可以考虑使用数据压缩技术,以减少存储空间的占用。
- 缓存失效处理:在缓存失效时,需要有适当的处理逻辑。可以返回默认值、从后端重新获取数据或进行其他适当的操作。
- 测试和监控:对本地缓存进行充分的测试,确保其正确性和性能。同时,监控缓存的命中率、过期时间和存储空间使用情况,以便及时调整缓存策略。
这些最佳实践可以帮助你更好地利用本地缓存,提高应用程序的性能和用户体验。具体的实施方式需要根据你的应用程序和需求进行调整。
二、Go代码实践:
1、本地缓存设计
之前已经有博客介绍过啦!!46.go实现一个本地内存缓存系统
2、本地缓存加载
本地缓存通常用于存储不经常变动,但又需要频繁访问的数据,例如用户配置,系统设置,常用的数据查询结果等。这样可以减少对数据库或者远程服务器的访问,提高程序的运行效率。
下面是一个Go
语言的例子,使用了内置的Map
与Slice
作为本地缓存:
场景假设:
假设我们负责了一个活动系统,管理这很多的活动元信息,此时将生效中的活动信息加入本地缓存能大大提高性能。注意这里本地缓存也不是要缓存所有的活动元信息,那样占用内存可能较大,得不偿失,所以只缓存生效中的。那些其他状态的使用频率较低,查DB就行。
model/activity.go
package modelimport "time"// Activity 实际工作中,肯定还有更多的字段,且字段可能是枚举、结构体等
type Activity struct {Id int64 `json:"id"` // 活动IDName string `json:"name"` // 活动名称Type int `json:"type"` // 活动类型ProductId int64 `json:"product_id"` // 产品线Desc string `json:"desc"` // 描述Status int `json:"status"` // 活动状态Rules string `json:"rules"` // 活动规则StartTime time.Time // 开始时间EndTime time.Time // 结束时间CreateTime time.Time // 创建时间UpdateTime time.Time // 更新时间
}
这里为了演示简单,就省去了和DB
的交互,直接模拟一下就好啦,如下:
dal/activity.go
package dalimport ("golang-trick/37-load-local-cache/model""time"
)// GetActivity 模拟从DB获取活动元信息
func GetActivity(minId int64, status []int, batchSize int) ([]*model.Activity, error) {return []*model.Activity{{Id: 1,Name: "限时返场",Type: 1, // 枚举更好,此处就把1当成返场类型ProductId: 1,Desc: "返场描述",Status: 1, // 枚举更好,此处就把1当成生效中Rules: "",StartTime: time.Time{},EndTime: time.Time{},CreateTime: time.Time{},UpdateTime: time.Time{},},{Id: 2,Name: "极速秒杀",Type: 2, // 秒杀类型ProductId: 1,Desc: "秒杀描述",Status: 1,Rules: "",StartTime: time.Time{},EndTime: time.Time{},CreateTime: time.Time{},UpdateTime: time.Time{},},}, nil}
本地缓存的加载,这里提供了Map
和Slice
两种结果,使得使用方可根据需要取用。主要是提供了本地缓存的加载和定时更新方法,此外,还提供了相关取用方法,这些取用方法可能根据实际情况增加更多方法。
注意看注释哦!!
cache/activity.go
package cacheimport ("fmt""github.com/opentracing/opentracing-go/log""golang-trick/37-load-local-cache/dal""golang-trick/37-load-local-cache/model""math/rand""sync""time"
)var ActivityMap map[int64]*model.Activity
var ActivityList []*model.Activity
var loadActivityLock = sync.Mutex{} // map与slice不是并发安全的,所以加锁控制// LoadActivity 活动信息不会太多,生效中的最多上千,所以可以从头开始加载全部生效中的到本地缓存
func LoadActivity() error {loadActivityLock.Lock()defer loadActivityLock.Unlock()// 每次从头分批加载minID := int64(0)// 加载过程使用临时变量,从而不影响之前已经在本地缓存的数据使用newActivityMap := make(map[int64]*model.Activity)newActivityList := make([]*model.Activity, 0)for {// 这里很多时候是调用一个RPC服务activities, err := dal.GetActivity(minID, []int{1}, 50)if err != nil {return err}// 全部记录加载完毕if len(activities) == 0 {break}for _, a := range activities {newActivityMap[a.Id] = anewActivityList = append(newActivityList, a)}// 记录本轮循环加载的最后一条记录的ID,下轮循环加载从此ID处开始minID = activities[len(activities)-1].Id}// 全部记录加载完毕后,更新本地缓存ActivityMap = newActivityMapActivityList = newActivityListreturn nil
}func RefreshCache() {go func() {defer func() {// 本地缓存加载出现panic,则需要上抛panic,否则可能引发更大的问题if err := recover(); err != nil {panic("RefreshCache panic")}}()rand.Seed(time.Now().UnixNano())for {randomInt := rand.Intn(60) + 60time.Sleep(time.Duration(randomInt) * time.Second)err := LoadActivity()if err != nil {log.Error(fmt.Errorf("load activity Info err:%v", err))}}}()
}func GetActivityList() []*model.Activity {// 如果有其他一些逻辑,便可以写到此处了,如灰度逻辑return ActivityList
}func GetActivityMap() map[int64]*model.Activity {return ActivityMap
}func GetActivityById(id int64) (*model.Activity, error) {if ActivityMap == nil {return nil, fmt.Errorf("ActivityMap is nil")}if _, ok := ActivityMap[id]; !ok {return nil, fmt.Errorf("ActivityMap[id] not found id:%v", id)}return ActivityMap[id], nil
}
使用方在main
方法中加载本地缓存,并启动异步更新方法,后续就可以在需要的地方调用本地缓存提供的相应取用方法即可。
main.go
package mainimport ("golang-trick/37-load-local-cache/cache"
)func main() {err := cache.LoadActivity()if err != nil {panic(err)}cache.RefreshCache()
}