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时做些处理+定时循环机制通过判断达到最大内存占比处理。

相关推荐
zhaotiannuo_19981 小时前
Python之2.7.9-3.9.1-3.14.2共存
开发语言·python
2601_949868362 小时前
Flutter for OpenHarmony 电子合同签署App实战 - 主入口实现
开发语言·javascript·flutter
三水不滴2 小时前
Redis缓存更新策略
数据库·经验分享·redis·笔记·后端·缓存
helloworldandy2 小时前
高性能图像处理库
开发语言·c++·算法
2401_836563182 小时前
C++中的枚举类高级用法
开发语言·c++·算法
chao1898442 小时前
矢量拟合算法在网络参数有理式拟合中的应用
开发语言·算法
EmbedLinX3 小时前
C++ 面向对象
开发语言·c++
小邓吖3 小时前
自己做了一个工具网站
前端·分布式·后端·中间件·架构·golang
weixin_445402303 小时前
C++中的命令模式变体
开发语言·c++·算法