go-cache 单机缓存

go-cache 是一个 golang 的缓存库, 用于缓存 k, v 对, 缓存时间过期后存储的值会失效, 底层是一个 map, 过期后内部 Item 是不会自动清除, 需要手动调用DeleteExpired方法清除过期项

安装

bash 复制代码
go get github.com/patrickmn/go-cache

使用方法

go 复制代码
// 创建Cache对象, 第一个参数为缓存时间, 第二个参数为清理缓存项的时间间隔
// 底层是一个map, 过期后内部Item是不会自动清除
c := cache.New(30*time.Second, 1*time.Minute)

// 设置k, v对
c.Set("name", "codepzj", cache.DefaultExpiration)

// 获得k, v对, found 为false则代表过期
val, found := c.Get("name")

其他参数

方法名 类型 说明 示例
GetWithExpiration(key) 方法 获取值和过期时间 val, exp, found := c.GetWithExpiration("k")
Add(key, value, duration) 方法 添加新项,若存在返回错误 err := c.Add("k", "v", 1*time.Hour)
Replace(key, value, duration) 方法 替换已存在项,否则报错 err := c.Replace("k", "new", 2*time.Minute)
Delete(key) 方法 删除指定 key c.Delete("k")
DeleteExpired() 方法 删除所有过期项 c.DeleteExpired()
Flush() 方法 清空所有缓存项 c.Flush()
Items() 方法 返回所有缓存项(包含过期未清理的) items := c.Items()

示例代码

这里写一个简单的示例代码, 使用 gin 框架

go 复制代码
package main

import (
	"fmt"
	"time"

	"github.com/gin-gonic/gin"
	"github.com/patrickmn/go-cache"
)

func main() {
	r := gin.Default()
	c := cache.New(30*time.Second, 1*time.Minute)
	r.GET("/", func(ctx *gin.Context) {
		c.Set("name", "codepzj", cache.DefaultExpiration)
		ctx.String(200, "set name successfully")
	})
	r.GET("/cache", func(ctx *gin.Context) {
		fmt.Println("cache items", c.Items())
		val, exp, found := c.GetWithExpiration("name")
		if found {
			ctx.String(200, "get cache successfully, value %s, expired at %s", val, exp.Format("2006-01-02 15:04:05"))
			return
		}
		ctx.String(200, "get cache failed, expired")
	})
	r.GET("/delete", func(ctx *gin.Context) {
		c.Delete("name")
		ctx.String(200, "delete cache successfully")
	})
	r.Run(":8080")
}

底层剖析

数据结构

go 复制代码
type cache struct {
	defaultExpiration time.Duration
	items             map[string]Item
	mu                sync.RWMutex
	onEvicted         func(string, interface{})
	janitor           *janitor
}

type Item struct {
	Object     interface{}
	Expiration int64
}
  • defaultExpiration是默认过期时间, 如果设置为 0, 则表示永不过期
  • items是缓存项的 map, 键是 string, 值是 Item
  • mu是互斥锁, 用于保护items的并发访问
  • onEvicted是删除缓存项时的回调函数, 当缓存项过期时, 会调用该函数
  • janitor是清理缓存项的定时器, 会定时清理过期缓存项

Get 和 Set 详解

Get 方法

go 复制代码
func (c *cache) Get(k string) (interface{}, bool) {
	c.mu.RLock()
	// "Inlining" of get and Expired
	item, found := c.items[k]
	if !found {
		c.mu.RUnlock()
		return nil, false
	}
	if item.Expiration > 0 {
		if time.Now().UnixNano() > item.Expiration {
			c.mu.RUnlock()
			return nil, false
		}
	}
	c.mu.RUnlock()
	return item.Object, true
}

在 Get 方法中, 首先会加锁, 可以防止在读取的时候, 其他 goroutine 修改 items, 并且能够并发访问

然后会判断缓存项是否过期, 如果过期, 则返回 false, 否则返回 true

Set 方法

go 复制代码
func (c *cache) Set(k string, x interface{}, d time.Duration) {
	// "Inlining" of set
	var e int64
	if d == DefaultExpiration {
		d = c.defaultExpiration
	}
	if d > 0 {
		e = time.Now().Add(d).UnixNano()
	}
	c.mu.Lock()
	c.items[k] = Item{
		Object:     x,
		Expiration: e,
	}
	// TODO: Calls to mu.Unlock are currently not deferred because defer
	// adds ~200 ns (as of go1.)
	c.mu.Unlock()
}

设置写锁,防止多个 goroutine 同时修改一个 item, 然后设置过期时间

这就是 go-cache 库, 主要用于单机缓存,底层是一个 map, 使用RWMutex锁控制读写和time.Now().Add(d).UnixNano()设置过期时间, 基于本地内存 , 如果需要分布式缓存, 可以考虑使用 redis 等其他缓存库

相关推荐
程序猿DD8 小时前
OctaFuse Gateway 2.15.0:MCP 聚合、向量 Embeddings 与官方目录同步
后端·agent
余槐i10 小时前
PostgreSQL 函数索引报错 must be immutable:3种绕法与代价
数据库·后端·sql·postgresql·性能优化
需要82611 小时前
Spring Bean 生命周期:从实例化到销毁的完整钩子
java·后端·spring
凤山老林12 小时前
Spring Boot 接入国密 SM2/SM3/SM4 的一些实践记录
spring boot·后端·python
伯恩bourne13 小时前
Spring Boot Bean 注册的两种方式:@Component 家族 vs @Configuration + @Bean
java·spring boot·后端
张彦峰ZYF13 小时前
停顿短不等于延迟低:G1、ZGC 与低延迟服务的目标分野、代价账本与选型标定
后端·回收暂停、应用停止总时长·到达安全点、分配停滞·屏障吃吞吐、并发线程吃核心·并发窗口吃堆余量·分区式靠挑回收集兑现目标·染色指针式不支持压缩引用
pride.li13 小时前
第03章 Python 教程-基础语法:注释与变量
服务器·前端·后端·python·算法·计算机视觉
upala14 小时前
【仓颉语言入门 · 第二篇 第 6 课】
后端
罗丹丹Luna14 小时前
移动端显示“已提交”以后:SAP 与低代码集成该怎样暴露真实状态
后端·架构
码事漫谈14 小时前
因为太想讨好你,所以AI会骗你
后端