Golang本地内存缓存

通过golang语言实现一个本地内存缓存,接下来将通过代码的形式展示:

1.文件目录结构
2.具体实现
1.接口定义
Go 复制代码
package cache

import "time"

type Cache interface {

	// 设置缓存大小 1KB 100KB 1MB 2MB 1GB
	SetMaxMemory(size string) bool

	// 写入缓存
	Set(key string, value interface{}, expire time.Duration) bool

	// 获取缓存
	Get(key string) (interface{}, bool)

	// 删除缓存
	Del(key string) bool

	// 判断缓存是否存在
	Exists(key string) bool

	// 清空缓存
	Flush() bool

	// 获取缓存中keys的数量
	Keys() int64
}
2.接口实现
Go 复制代码
package impl

import (
	"fmt"
	"project08/cache"
	"project08/utils"
	"sync"
	"time"
)

type CacheImpl struct {
	// 最大内存
	maxMemorySize int64
	// 最大内存表示
	maxMemorySizeStr string
	// 当前使用内存
	currentMemorySize int64
	// 缓存键值对
	values map[string]*cacheValue
	// 读写锁
	locker sync.RWMutex
	// 清除过期缓存时间间隔
	clearExpireKeyInterval time.Duration
}

type cacheValue struct {
	// 缓存值
	val interface{}
	// 过期时间
	expireTime time.Time
	// 缓存大小
	size int64
}

func NewCache() cache.Cache {
	ci := &CacheImpl{
		values:                 make(map[string]*cacheValue),
		clearExpireKeyInterval: time.Second * 10,
	}
	clearExpireKey := func() {
		// 定时触发器
		timeTicker := time.NewTicker(ci.clearExpireKeyInterval)
		defer timeTicker.Stop()
		for {
			select {
			case <-timeTicker.C:
				for key, item := range ci.values {
					if time.Now().After(item.expireTime) {
						ci.locker.Lock()
						val, ok := ci.values[key]
						if ok {
							ci.currentMemorySize -= val.size
							delete(ci.values, key)
						}
						ci.locker.Unlock()
					}
				}
			}
		}
	}
	go clearExpireKey()
	return ci
}

// 结构体实现Cache接口
func (ci *CacheImpl) SetMaxMemory(size string) bool {
	// 通过当前设置的size 如1KB
	ci.maxMemorySize, ci.maxMemorySizeStr = utils.ParseMemory(size)
	fmt.Println(ci.maxMemorySize, ci.maxMemorySizeStr)
	return true
}

func (ci *CacheImpl) Set(key string, value interface{}, expire time.Duration) bool {
	ci.locker.Lock()
	defer ci.locker.Unlock()
	// 先判断当前key是否存在 将原先key对应的val删除
	val, ok := ci.values[key]
	if ok && val != nil {
		ci.currentMemorySize -= val.size
		delete(ci.values, key)
	}
	// 判断当前内存值是否大于最大内存值
	size := utils.GetValueSize(value)
	if ci.currentMemorySize+size > ci.maxMemorySize {
		// 可以加一些内存管理策略
		panic(fmt.Sprintf("over max memory size %v", ci.maxMemorySize))
	}
	v := &cacheValue{
		val:        value,
		expireTime: time.Now().Add(expire),
		size:       size,
	}
	// 设置键值
	ci.values[key] = v
	// 增加当前缓存
	ci.currentMemorySize += v.size
	return true
}

func (ci *CacheImpl) Get(key string) (interface{}, bool) {
	ci.locker.RLock()
	defer ci.locker.RUnlock()
	val, ok := ci.values[key]
	if ok {
		// 判断缓存是否过期
		if val.expireTime.Before(time.Now()) {
			ci.currentMemorySize -= val.size
			delete(ci.values, key)
			return nil, false
		}
		return val.val, true
	}
	return nil, false
}

func (ci *CacheImpl) Del(key string) bool {
	ci.locker.Lock()
	defer ci.locker.Unlock()
	val, ok := ci.values[key]
	if ok {
		ci.currentMemorySize -= val.size
		delete(ci.values, key)
		return true
	}
	return false
}

func (ci *CacheImpl) Exists(key string) bool {
	ci.locker.RLock()
	defer ci.locker.RUnlock()
	_, ok := ci.values[key]
	return ok
}

func (ci *CacheImpl) Flush() bool {
	ci.locker.Lock()
	defer ci.locker.Unlock()
	// 将values设置为长度为0的空对象
	ci.values = make(map[string]*cacheValue, 0)
	ci.currentMemorySize = 0
	return true
}

func (ci *CacheImpl) Keys() int64 {
	ci.locker.RLock()
	defer ci.locker.RUnlock()
	return int64(len(ci.values))
}
3.utils定义
Go 复制代码
package utils

import (
	"encoding/json"
	"fmt"
	"regexp"
	"strconv"
	"strings"
)

func ParseMemory(size string) (int64, string) {
	// 默认值 100MB
	// 匹配出数字
	re, _ := regexp.Compile("[0-9]+")
	// 单位KB MB...
	unit := string(re.ReplaceAll([]byte(size), []byte("")))
	// 数字 转为64位10进制
	num, _ := strconv.ParseInt(strings.Replace(size, unit, "", 1), 10, 64)
	// 单位大小写转换
	unit = strings.ToUpper(unit)
	var byteNum int64 = 0
	switch unit {
	case "B":
		byteNum = B
	case "KB":
		byteNum = num * KB
	case "MB":
		byteNum = num * MB
	case "GB":
		byteNum = num * GB
	case "TB":
		byteNum = num * TB
	case "PB":
		byteNum = num * PB
	default:
		num = 0
	}
	if num == 0 {
		fmt.Println("Cache set size just only support B,KB,MB,GB,TB,PB")
		num = 100
		byteNum = num * MB
		unit = "MB"
	}
	return byteNum, strconv.FormatInt(num, 10) + unit
}

func GetValueSize(val interface{}) int64 {
	bytes, _ := json.Marshal(val)
	return int64(len(bytes))
}
4.main主函数实现
Go 复制代码
package main

import (
	"project08/cache/impl"
)

func main() {
	cache := impl.NewCache()

	cache.SetMaxMemory("10KB")

	cache.Set("james", 2, 6)

	cache.Get("james")

	cache.Exists("james")

	cache.Keys()

	cache.Flush()
}
5.测试自己去编写吧
bash 复制代码
go test .\test\cache_test.go

其实现原理类似与redis,如有不当之处可参考redis命令做适当修改.

缺少内存淘汰机制。

思路:可在set时做些处理+定时循环机制通过判断达到最大内存占比处理。

相关推荐
xieliyu.11 小时前
Java算法精讲:双指针(三)
java·开发语言·算法
CryptoPP12 小时前
快速对接东京证券交易所API数据:实战指南与代码示例
开发语言·人工智能·windows·python·信息可视化·区块链
ZC跨境爬虫12 小时前
跟着 MDN 学JavaScript day_7:数学运算与逻辑判断实战测试
开发语言·前端·javascript·学习·ecmascript
阳区欠13 小时前
【LangChain】LLM基础介绍
开发语言·python·langchain
Jinkxs13 小时前
Java 跨域14-Java 与区块链(Hyperledger)集成
java·开发语言·区块链
晨曦中的暮雨14 小时前
Golang速通(Javaer版)
java·开发语言·后端·golang
小小编程路14 小时前
Python 还有容器类型互转、进制转换、字符编码转换
开发语言·windows·python
qeen8714 小时前
【C++】类与对象之类的默认成员函数(二)
android·c语言·开发语言·c++·笔记·学习
CRMEB系统商城14 小时前
CRMEB多商户系统(Java)v2.3公测版发布
java·开发语言·人工智能·小程序·开源·php
动能小子ohhh15 小时前
DocForge平台的设计与开发--文件上传接口的实现
开发语言·人工智能·python·langchain·ocr·fastapi