很多人在 Debian 上写 Go 服务时,第一反应是直接接 Redis,但如果你只是想先搞懂“缓存到底怎么工作”,从一个本地内存版开始反而更清楚。下面这篇就按环境准备、代码实现、运行验证和方案边界四步展开,带你看清一个最小可用缓存的组成,以及它为什么只适合入门和轻量场景。
在 Debian 上先准备 Go 环境
如果系统里还没有 Go,可以先安装官方仓库提供的 golang-go 包:
sudo apt update
sudo apt install golang-go
装好之后,新建一个示例文件,例如 cache.go,后面的代码直接放进去即可。
用 Go 写一个最小缓存示例
这个示例只依赖 Go 标准库,核心思路有三点:用 map 存键值、用 sync.Mutex 处理并发访问、用 time.Time 记录过期时间。

package main
import (
"fmt"
"sync"
"time"
)
// Cache 是一个简单的缓存结构体
type Cache struct {
items map[string]*cacheItem
mu sync.Mutex
}
// cacheItem 是缓存中的一个条目
type cacheItem struct {
value interface{}
expiration time.Time
}
// NewCache 创建一个新的缓存实例
func NewCache() *Cache {
return &Cache{
items: make(map[string]*cacheItem),
}
}
// Get 从缓存中获取一个值
func (c *Cache) Get(key string) (interface{}, bool) {
c.mu.Lock()
defer c.mu.Unlock()
item, found := c.items[key]
if !found || item.expiration.Before(time.Now()) {
return nil, false
}
return item.value, true
}
// Set 向缓存中设置一个值
func (c *Cache) Set(key string, value interface{}, duration time.Duration) {
c.mu.Lock()
defer c.mu.Unlock()
expiration := time.Now().Add(duration)
c.items[key] = &cacheItem{
value: value,
expiration: expiration,
}
}
// Delete 从缓存中删除一个值
func (c *Cache) Delete(key string) {
c.mu.Lock()
defer c.mu.Unlock()
delete(c.items, key)
}
func main() {
cache := NewCache()
// 设置缓存
cache.Set("key1", "value1", 5*time.Minute)
cache.Set("key2", "value2", 10*time.Minute)
// 获取缓存
if value, found := cache.Get("key1"); found {
fmt.Println("key1:", value)
} else {
fmt.Println("key1 not found or expired")
}
// 等待一段时间后再次获取缓存
time.Sleep(6 * time.Minute)
if value, found := cache.Get("key1"); found {
fmt.Println("key1:", value)
} else {
fmt.Println("key1 not found or expired")
}
// 删除缓存
cache.Delete("key2")
// 尝试获取已删除的缓存
if value, found := cache.Get("key2"); found {
fmt.Println("key2:", value)
} else {
fmt.Println("key2 not found or expired")
}
}
这个缓存结构体解决了什么问题
Cache 结构体里有两个关键字段:
items map[string]*cacheItem:负责保存缓存内容。mu sync.Mutex:保证多个 goroutine 同时读写时不会把数据弄乱。
而 cacheItem 里除了值本身,还额外保存了 expiration time.Time。这意味着缓存不是“永久存进去就不管”,而是每个键都带着自己的失效时间。
三个核心方法分别怎么工作
这个示例只有三个对外方法,但已经把缓存最基础的行为说明白了:
Set:写入键值,并根据duration计算过期时间。Get:读取键值时顺带检查当前是否已过期,过期就直接返回未命中。Delete:手动删除指定键。
其中最值得注意的是 Get。它并不会主动清理所有过期数据,而是在读取某个键时判断:
- 键不存在,返回
nil, false; - 键存在但
item.expiration.Before(time.Now())为真,也返回nil, false; - 只有未过期时,才返回真实值和
true。
也就是说,这是一种“访问时检查过期”的最简实现。
运行示例并验证缓存是否生效
代码保存后,直接运行:

go run cache.go
主函数演示了完整流程:
- 先写入
key1和key2,过期时间分别是5*time.Minute和10*time.Minute; - 立刻读取
key1,这时应该能拿到value1; - 等待
6 * time.Minute后再次读取key1,由于已经超过 5 分钟,应返回key1 not found or expired; - 随后删除
key2,再次读取时也会返回未命中。
这个运行过程可以帮助你确认两件事:一是缓存写入和读取链路是通的,二是过期判断和手动删除都确实生效。
这个方案适合学习,不适合直接上生产
这个示例的价值在于把缓存机制拆到了最基础的层面,但它距离生产可用还有明显差距。

当前版本缺了哪些关键能力
- 没有淘汰策略,比如 LRU、LFU;
- 没有后台清理机制,过期数据可能继续留在
map里; - 没有持久化,进程退出后缓存全部丢失;
- 没有分布式能力,无法跨实例共享缓存。
因此,它更适合本地实验、理解原理,或者非常轻量的单进程场景,不适合作为复杂业务系统的最终方案。
什么时候该换成熟库或独立服务
如果你的需求已经超出“学原理”阶段,通常有两条更实际的路径:
- 继续留在 Go 进程内,但改用成熟库,例如
groupcache、bigcache; - 直接使用专业缓存服务,例如 Redis、Memcached。
前者适合追求本地高性能和更成熟的数据管理能力,后者则更适合多实例、分布式和独立运维场景。先把这个最小示例理解透,再去看这些方案,判断会更有依据。







