61.本地缓存加载与使用实践

news/2024/11/30 7:53:26/

文章目录

  • 一、本地缓存理论最佳实践
  • 二、Go代码实践:
    • 1、本地缓存设计
    • 2、本地缓存加载

代码地址:https://gitee.com/lymgoforIT/golang-trick/tree/master/37-load-local-cache

一、本地缓存理论最佳实践

  1. 控制缓存大小:根据应用程序的需求和可用的存储空间,合理限制本地缓存的大小。过大会导致存储空间不足,过小可能无法有效缓存数据。
  2. 缓存过期策略:设置适当的缓存过期时间,以确保缓存的数据不会过时。根据数据的更新频率和重要性,制定合适的过期策略。
  3. 数据一致性:在使用本地缓存时,要考虑数据的一致性。如果数据在后端发生变化,需要及时更新本地缓存,以避免展示过时的数据。
  4. 缓存刷新机制:建立合适的缓存刷新机制,当后端数据发生变化时,及时更新本地缓存。可以通过定时刷新、监听后端事件或在请求数据时检查更新标志等方式实现。
  5. 缓存层次结构:考虑使用多层缓存结构,如内存缓存和磁盘缓存。将经常访问的数据放在内存中,可以提供更快的访问速度,而将不经常访问的数据放在磁盘上,以节省内存空间。
  6. 数据压缩:对于大数据量的缓存,可以考虑使用数据压缩技术,以减少存储空间的占用。
  7. 缓存失效处理:在缓存失效时,需要有适当的处理逻辑。可以返回默认值、从后端重新获取数据或进行其他适当的操作。
  8. 测试和监控:对本地缓存进行充分的测试,确保其正确性和性能。同时,监控缓存的命中率、过期时间和存储空间使用情况,以便及时调整缓存策略。
    这些最佳实践可以帮助你更好地利用本地缓存,提高应用程序的性能和用户体验。具体的实施方式需要根据你的应用程序和需求进行调整。

二、Go代码实践:

1、本地缓存设计

之前已经有博客介绍过啦!!46.go实现一个本地内存缓存系统

2、本地缓存加载

本地缓存通常用于存储不经常变动,但又需要频繁访问的数据,例如用户配置,系统设置,常用的数据查询结果等。这样可以减少对数据库或者远程服务器的访问,提高程序的运行效率。

下面是一个Go语言的例子,使用了内置的MapSlice作为本地缓存:

场景假设:
假设我们负责了一个活动系统,管理这很多的活动元信息,此时将生效中的活动信息加入本地缓存能大大提高性能。注意这里本地缓存也不是要缓存所有的活动元信息,那样占用内存可能较大,得不偿失,所以只缓存生效中的。那些其他状态的使用频率较低,查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}

本地缓存的加载,这里提供了MapSlice两种结果,使得使用方可根据需要取用。主要是提供了本地缓存的加载和定时更新方法,此外,还提供了相关取用方法,这些取用方法可能根据实际情况增加更多方法。

注意看注释哦!!

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()
}

http://www.ppmy.cn/news/1290599.html

相关文章

c# 编程点滴--元组

1. 元组 Tuple 是 C# 中表示元组(Tuple)的数据结构。元组是一个用于存储一组有序元素的数据结构,每个元素可以是不同类型的数据。在 C# 中,元组是值类型,允许存储多个值,并且可以通过索引或者具名字段访问…

力扣hot100 翻转二叉树 递归

👨‍🏫 题目地址 😋 AC code /*** Definition for a binary tree node.* public class TreeNode {* int val;* TreeNode left;* TreeNode right;* TreeNode() {}* TreeNode(int val) { this.val val; }* TreeNod…

CCNP课程实验-05-Comprehensive_Experiment

目录 实验条件网络拓朴 基础配置实现IGP需求:1. 根据拓扑所示,配置OSPF和EIGRP2. 在R3上增加一个网段:33.33.33.0/24 (用Loopback 1模拟) 宣告进EIGRP,并在R3上将EIGRP重分布进OSPF。要求重分布进OSPF后的路由Tag值设置为666&…

CISSP 第7章:PKI和密码学应用

第七章 PKI和密码学应用 7.1 非对称密码学 对称密码系统具有共享的秘钥系统,从而产生了安全秘钥分发的问题 非对称密码学使用公钥和私钥对,无需支出复杂密码分发系统 7.1.1 公钥与私钥 7.1.2 RSA(兼具加密和数字签名) RSA算法依赖…

Linux引导过程和服务

一、Linux操作系统引导过程 1.引导过程 bios 加电自检——mbr——grub——加载内核——启动进程 加电后BIOS程序回自检硬件,硬件无故障后,会根据第一次启动项去找内核,一般来说第一启动项是硬盘,找到硬盘后,会根据mb…

哪些洗地机比较好?洗地机选购指南

随着社会生活水平的提高,人们对居家环境的卫生和清洁要求不断提升。家用洗地机作为一种先进的清洁工具,带来了许多便利和优势,特别是在解决一些特殊需求的家庭环境方面。 以下是一些家用洗地机的优势和适用场景: 1.高效清洁&…

【番外】【Airsim in Windows ROS in WSL2-Ubuntu20.04】环境配置大全

【番外】【Airsim in Windows &ROS in WSL2-Ubuntu20.04】环境配置大全 【前言(可省略不看)】1.在windows上面部署好UE4AirSim联合仿真环境2.在windows上面部署wsl2系统以及在wsl2上面部署ubuntu系统3.安装好ubuntu系统之后,目前只能在命…

【华为数据之道学习笔记】10-1数据被列为生产要素:制度层面的肯定

数字化转型不能一蹴而就,数据治理也不是一朝一夕之功。数字化转型带来机遇的同时,也给整个企业的数据治理带来了新的挑战。 基于对华为公司数字化转型的解读,我们建立了数据综合治理体系,发布了信息架构,构建了数据湖、…