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 等其他缓存库

相关推荐
SimonKing2 分钟前
深入解析 Spring SpEL:SpelExpressionParser 的使用与实践
java·后端·程序员
rannn_1111 小时前
【MySQL学习|黑马笔记|Day1】数据库概述,SQL|通用语法、SQL分类、DDL
数据库·后端·学习·mysql
超浪的晨1 小时前
JavaWeb 入门:HTML 基础与实战详解(Java 开发者视角)
java·开发语言·前端·后端·html·个人开发
程序员爱钓鱼2 小时前
Go语言实战案例-计算字符串编辑距离
后端·google·go
程序员爱钓鱼2 小时前
Go语言实战案例-判断二叉树是否对称
后端·google·go
Victor3562 小时前
MySQL(165)MySQL如何处理并发控制?
后端
风象南2 小时前
用 4 张图解释 CAP 到底在纠结什么
java·分布式·后端
Victor3562 小时前
MySQL(166)MVCC在MySQL中的实现原理是什么?
后端
paopaokaka_luck7 小时前
基于SpringBoot+Uniapp的健身饮食小程序(协同过滤算法、地图组件)
前端·javascript·vue.js·spring boot·后端·小程序·uni-app
Villiam_AY7 小时前
Redis 缓存机制详解:原理、问题与最佳实践
开发语言·redis·后端