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

相关推荐
IT_102417 分钟前
Spring Boot项目开发实战销售管理系统——系统设计!
大数据·spring boot·后端
ai小鬼头1 小时前
AIStarter最新版怎么卸载AI项目?一键删除操作指南(附路径设置技巧)
前端·后端·github
Touper.1 小时前
SpringBoot -- 自动配置原理
java·spring boot·后端
一只叫煤球的猫2 小时前
普通程序员,从开发到管理岗,为什么我越升职越痛苦?
前端·后端·全栈
一只鹿鹿鹿2 小时前
信息化项目验收,软件工程评审和检查表单
大数据·人工智能·后端·智慧城市·软件工程
专注VB编程开发20年2 小时前
开机自动后台运行,在Windows服务中托管ASP.NET Core
windows·后端·asp.net
程序员岳焱2 小时前
Java 与 MySQL 性能优化:MySQL全文检索查询优化实践
后端·mysql·性能优化
一只叫煤球的猫3 小时前
手撕@Transactional!别再问事务为什么失效了!Spring-tx源码全面解析!
后端·spring·面试
旷世奇才李先生3 小时前
Ruby 安装使用教程
开发语言·后端·ruby
沃夫上校6 小时前
Feign调Post接口异常:Incomplete output stream
java·后端·微服务