Golang 实现缓存系统

本文涉及的产品
云数据库 Tair(兼容Redis),内存型 2GB
Redis 开源版,标准版 2GB
推荐场景:
搭建游戏排行榜
简介: Golang 实现缓存系统

缓存

缓存(Cache)在计算机硬件中普遍存在。比如在 CPU 中就有一级缓存,二级缓存,甚至三级缓存。缓存的工作原理一般是 CPU 需要读取数据时,会首先从缓存中查找需要的数据,如果找到了就直接进行处理,如果没有找到则从内存中读取数据。由于 CPU 中的缓存工作速度比内存还要快,所以缓存的使用能加快 CPU 处理速度。缓存不仅仅存在于硬件中,在各种软件系统中也处处可见。比如在 Web 系统中,缓存存在于服务器端,客户端或者代理服务器中。广泛使用的 CDN 网络,也可以看作一个巨大的缓存系统。缓存在 Web 系统中的使用有很多好处,不仅可以减少网络流量,降低客户访问延迟,还可以减轻服务器负载。

目前已经存在很多高性能的缓存系统,比如 Memcache,Redis 等,尤其是 Redis,现已经广泛用于各种 Web 服务中。既然有了这些功能完善的缓存系统,那为什么我们还需要自己实现一个缓存系统呢?这么做主要有两个原因,第一,通过动手实现我们可以了解缓存系统的工作原理,这也是老掉牙的理由了。第二,Redis 之类的缓存系统都是独立存在的,如果只是开发一个简单的应用还需要单独使用 Redis 服务器,难免会过于复杂。这时候如果有一个功能完善的软件包实现了这些功能,只需要引入这个软件包就能实现缓存功能,而不需要单独使用 Redis 服务器,就最好不过了。

缓存系统的设计

缓存系统中,缓存的数据一般都存储在内存中,所以我们设计的缓存系统要以某一种方式管理内存中的数据。既然缓存数据是存储在内存中的,那如果系统停机,那数据岂不就丢失了吗?其实一般情况下,缓存系统还支持将内存中的数据写入到文件中,在系统再次启动时,再将文件中的数据加载到内存中,这样一来就算系统停机,缓存数据也不会丢失。

同时缓存系统还提供过期数据清理机制,也就是说缓存的数据项是有生存时间的,如果数据项过期,则数据项会从内存中被删除,这样一来热数据会一直存在,而冷数据则会被删除,也没有必要进行缓存。

缓存系统还需要对外提供操作的接口,这样系统的其他组件才能使用缓存。一般情况下,缓存系统需要支持 CRUD 操作,也就算创建(添加),读取,更新和删除操作。

通过以上分析,可以总结出缓存系统需要有以下功能:

  1. 缓存数据的存储
  2. 过期数据项管理
  3. 内存数据导出,导入
  4. 提供 CRUD 接口

代码实现

package examples
import (
  "sync"
  "testing"
  "time"
)
type ICache interface {
  //size support: 1KB,100KB,1MB,2MB,1GB
  SetMaxMemory(size string) bool
  //key expire after expire time
  Set(key string, val interface{}, expire time.Duration)
  //get one key
  Get(key string) (interface{}, bool)
  //delete one key
  Del(key string) bool
  //exists one key
  Exists(key string) bool
  //delete all key
  Flush() bool
  //get all key
  Keys() []string
  //garbage collection every second
  GcLoop()
  //delete expired key
  DeleteExpired()
}
type Item struct {
  Object     interface{}
  Expiration int64
}
type cache struct {
  size     string
  items    map[string]Item
  mu       sync.RWMutex
  interval time.Duration
}
func (c *cache) SetMaxMemory(size string) bool {
  c.mu.RLock()
  defer c.mu.RUnlock()
  c.size = size
  return true
}
func (c *cache) Set(k string, x interface{}, d time.Duration) {
  c.mu.Lock()
  defer c.mu.Unlock()
  e := time.Now().Add(d * time.Second).Unix()
  c.items[k] = Item{
    Object:     x,
    Expiration: e,
  }
}
func (c *cache) Get(k string) (interface{}, bool) {
  c.mu.RLock()
  defer c.mu.RUnlock()
  item, found := c.items[k]
  if !found {
    return nil, false
  }
  if time.Now().Unix() > item.Expiration {
    return nil, false
  }
  return item.Object, true
}
func (c *cache) Del(k string) bool {
  c.mu.Lock()
  defer c.mu.Unlock()
  if _, found := c.items[k]; found {
    delete(c.items, k)
    return true
  }
  return false
}
func (c *cache) Exists(k string) bool {
  c.mu.RLock()
  defer c.mu.RUnlock()
  if _, found := c.items[k]; found {
    return true
  }
  return false
}
func (c *cache) Flush() bool {
  c.mu.Lock()
  defer c.mu.Unlock()
  c.items = map[string]Item{}
  return true
}
func (c *cache) Keys() []string {
  c.mu.RLock()
  defer c.mu.RUnlock()
  var keys []string
  for k := range c.items {
    keys = append(keys, k)
  }
  return keys
}
func (c *cache) GcLoop() {
  ticker := time.NewTicker(c.interval)
  for {
    select {
    case <-ticker.C:
      c.DeleteExpired()
    }
  }
}
func (c *cache) DeleteExpired() {
  now := time.Now().Unix()
  for k, v := range c.items {
    if now > v.Expiration {
      c.Del(k)
    }
  }
}
func NewCache() ICache {
  c := &cache{
    size:     "1024",
    items:    map[string]Item{},
    interval: time.Second,
  }
  go c.GcLoop()
  return c
}
func TestCacheExample(t *testing.T) {
  c := NewCache()
  c.Set("foo", "bar", 5)
  time.Sleep(10 * time.Second)
  r, b := c.Get("foo")
  t.Log(r, b)
  t.Log("test cache finished.")
}
相关实践学习
基于Redis实现在线游戏积分排行榜
本场景将介绍如何基于Redis数据库实现在线游戏中的游戏玩家积分排行榜功能。
云数据库 Redis 版使用教程
云数据库Redis版是兼容Redis协议标准的、提供持久化的内存数据库服务,基于高可靠双机热备架构及可无缝扩展的集群架构,满足高读写性能场景及容量需弹性变配的业务需求。 产品详情:https://www.aliyun.com/product/kvstore &nbsp; &nbsp; ------------------------------------------------------------------------- 阿里云数据库体验:数据库上云实战 开发者云会免费提供一台带自建MySQL的源数据库&nbsp;ECS 实例和一台目标数据库&nbsp;RDS实例。跟着指引,您可以一步步实现将ECS自建数据库迁移到目标数据库RDS。 点击下方链接,领取免费ECS&amp;RDS资源,30分钟完成数据库上云实战!https://developer.aliyun.com/adc/scenario/51eefbd1894e42f6bb9acacadd3f9121?spm=a2c6h.13788135.J_3257954370.9.4ba85f24utseFl
相关文章
|
1月前
|
消息中间件 缓存 NoSQL
Redis 是一个高性能的键值对存储系统,常用于缓存、消息队列和会话管理等场景。
【10月更文挑战第4天】Redis 是一个高性能的键值对存储系统,常用于缓存、消息队列和会话管理等场景。随着数据增长,有时需要将 Redis 数据导出以进行分析、备份或迁移。本文详细介绍几种导出方法:1)使用 Redis 命令与重定向;2)利用 Redis 的 RDB 和 AOF 持久化功能;3)借助第三方工具如 `redis-dump`。每种方法均附有示例代码,帮助你轻松完成数据导出任务。无论数据量大小,总有一款适合你。
74 6
|
22天前
|
缓存 Java Shell
Android 系统缓存扫描与清理方法分析
Android 系统缓存从原理探索到实现。
46 15
Android 系统缓存扫描与清理方法分析
|
2月前
|
缓存 运维 NoSQL
二级缓存架构极致提升系统性能
本文详细阐述了如何通过二级缓存架构设计提升高并发下的系统性能。
123 12
|
3月前
|
缓存 NoSQL Linux
【Azure Redis 缓存】Windows和Linux系统本地安装Redis, 加载dump.rdb中数据以及通过AOF日志文件追加数据
【Azure Redis 缓存】Windows和Linux系统本地安装Redis, 加载dump.rdb中数据以及通过AOF日志文件追加数据
128 1
【Azure Redis 缓存】Windows和Linux系统本地安装Redis, 加载dump.rdb中数据以及通过AOF日志文件追加数据
|
3月前
|
缓存 应用服务中间件 nginx
[nginx]proxy_cache缓存系统
[nginx]proxy_cache缓存系统
|
3月前
|
Go 开发者
|
4月前
|
消息中间件 缓存 算法
基于Netty的自研流系统缓存实现挑战: 内存碎片与OOM困境
基于Netty的自研流系统缓存实现挑战: 内存碎片与OOM困境
53 1
基于Netty的自研流系统缓存实现挑战: 内存碎片与OOM困境
|
3月前
|
缓存 架构师 数据库
缓存系统稳定性 - 架构师峰会演讲实录
缓存系统稳定性 - 架构师峰会演讲实录
|
4月前
|
自然语言处理 Go 数据处理
云计算自旋锁问题之引入Golang插件系统后iLogtail的输入输出通道和处理能力如何解决
云计算自旋锁问题之引入Golang插件系统后iLogtail的输入输出通道和处理能力如何解决
36 1
|
4月前
|
消息中间件 缓存 架构师
对抗软件复杂度问题之降低代码的复杂度,如何解决
对抗软件复杂度问题之降低代码的复杂度,如何解决