2 Commits

Author SHA1 Message Date
2f99149d92 up 2025-02-21 08:42:56 +08:00
a0ee3b858b 添加GetAll 2024-08-17 13:45:32 +08:00
9 changed files with 153 additions and 117 deletions

View File

@@ -1,5 +1,3 @@
# cache
该库是go语言实现的低配版缓存库,类似redis超级阉割版本 \
该库只将淘汰时间精确到了秒 \
并且设计上并不支持高精度操作和储蓄大量数据(除非你有足够的内存和超高的性能)
go cache

View File

@@ -9,7 +9,7 @@ import (
type Cache[K comparable, V any] struct {
mu sync.RWMutex
data map[K]*Data[V] // 数据
data sync.Map // 数据
cSet func(K, Data[V]) // 设置数据回调
cGet func(K, Data[V]) // 获取数据回调
cGetData func(K, Data[V]) // 获取数据内容回调
@@ -33,7 +33,7 @@ type Data[V any] struct {
func NewCache[K comparable, V any]() *Cache[K, V] {
defFunc := func(k K, d Data[V]) {}
c := &Cache[K, V]{
data: make(map[K]*Data[V]),
data: sync.Map{},
cSet: defFunc,
cGet: defFunc,
cGetData: defFunc,
@@ -60,27 +60,14 @@ func (c *Cache[K, V]) collection() func() {
return
case <-ticker.C:
now := time.Now()
var dKeys []K
c.mu.RLock()
for k, v := range c.data {
if v.End.IsZero() && v.End.Before(now) {
dKeys = append(dKeys, k)
}
}
c.mu.RUnlock()
// 删除过期数据
if len(dKeys) > 0 {
c.mu.Lock()
for _, k := range dKeys {
if c.cDel != nil {
c.cDel(k, *c.data[k])
c.data.Range(func(key, value any) bool {
if v, ok := value.(*Data[V]); ok {
if !v.End.IsZero() && v.End.Before(now) {
c.data.Delete(key)
}
delete(c.data, k)
}
c.mu.Unlock()
}
return true
})
}
}
}()
@@ -104,5 +91,6 @@ func (c *Cache[K, V]) Destroy() {
if c.persist != nil {
c.persist()
}
c.data.Clear()
return
}

16
del.go
View File

@@ -1,17 +1,19 @@
package cache
func (c *Cache[K, V]) del(key K) (bool, error) {
c.mu.Lock()
defer c.mu.Unlock()
if _, ok := c.data[key]; !ok {
dataAny, load := c.data.LoadAndDelete(key)
if !load {
return false, nil
}
if c.cDel != nil {
c.cDel(key, *c.data[key])
data, ok := dataAny.(*Data[V])
if !ok {
return false, TypeErrMsg
}
if c.cDel != nil {
c.cDel(key, *data)
}
delete(c.data, key)
return true, nil
}

45
get.go
View File

@@ -14,14 +14,14 @@ import (
// Get 获取数据
// key: 键 返回值: 数据 剩余时间 是否存在
func (c *Cache[K, V]) Get(key K) (V, int64, bool) {
c.mu.RLock()
defer c.mu.RUnlock()
if data, ok := c.data[key]; ok {
if data.End.IsZero() {
return data.Val, -1, true
} else if data.End.Before(time.Now()) {
return data.Val, int64(data.End.Sub(time.Now()).Seconds()), true
if dataAny, ok := c.data.Load(key); ok {
data, ok := dataAny.(*Data[V])
if ok {
if data.End.IsZero() {
return data.Val, -1, true
} else if data.End.After(time.Now()) {
return data.Val, int64(data.End.Sub(time.Now()).Seconds()), true
}
}
}
return c.zero(), 0, false
@@ -37,14 +37,14 @@ func (c *Cache[K, V]) GetData(key K) (V, bool) {
// GetTime 获取数据到期时间
// key: 键 返回值: 到期时间 是否存在
func (c *Cache[K, V]) GetTime(key K) (time.Time, bool) {
c.mu.RLock()
defer c.mu.RUnlock()
if data, ok := c.data[key]; ok {
if c.cGetData != nil {
c.cGetData(key, *data)
if dataAny, ok := c.data.Load(key); ok {
data, ok := dataAny.(*Data[V])
if ok {
if c.cGetTTL != nil {
c.cGetTTL(key, *data)
}
return data.End, true
}
return data.End, true
}
return time.Time{}, false
}
@@ -57,6 +57,18 @@ func (c *Cache[K, V]) GetTTL(key K) (int64, bool) {
return ttl, ok
}
// GetAll 获取所有数据
// 返回值: 数据
// 该功能为一些状态API提供支持
func (c *Cache[K, V]) GetAll() map[K]Data[V] {
data := make(map[K]Data[V])
c.data.Range(func(key, val any) bool {
data[key.(K)] = *val.(*Data[V])
return true
})
return data
}
/*
下方仅为获取数据并且输出指定类型的方法
推荐在定义类型为any的情况下使用
@@ -71,6 +83,9 @@ var NegativeNumberErrMsg = errors.New("negative number")
// NumberBigErrMsg 数字过大
var NumberBigErrMsg = errors.New("number too big")
// KeyNotExists 不存在
var KeyNotExists = errors.New("key not exists")
// GetString 获取数据并转为string
// key: 键 返回值: 数据 是否存在 error
// 支持类型: string,int*,uint*,float*,bool

2
go.mod
View File

@@ -1,3 +1,3 @@
module git.slzz.org/gomod/cache
go 1.18
go 1.24

View File

@@ -4,3 +4,12 @@ func (c *Cache[K, V]) zero() V {
var zero V
return zero
}
// toData 不返回错误但可能为nil
func (c *Cache[K, V]) toData(val any) *Data[V] {
data, ok := val.(*Data[V])
if ok {
return data
}
return nil
}

View File

@@ -21,6 +21,20 @@ func (c *Cache[K, V]) SetPersist(path string) error {
return nil
}
// PersistSave 手动保存持久化数据
// 会覆盖指定路径的文件 也可以用作创建快照
func (c *Cache[K, V]) PersistSave(path string) {
datas := make(map[K]*Data[V])
c.data.Range(func(key, value any) bool {
datas[key.(K)] = value.(*Data[V])
return true
})
err := c.saveCache(path, datas)
if err != nil {
c.fatal(err)
}
}
func (c *Cache[K, V]) setPersist(path string) func() {
gob.Register(map[K]*Data[V]{})
stopChan := make(chan struct{})
@@ -30,34 +44,28 @@ func (c *Cache[K, V]) setPersist(path string) func() {
if err != nil {
c.fatal(err)
}
c.mu.Lock()
if data != nil {
c.data = data
for k, v := range data {
c.data.Store(k, v)
}
}
c.mu.Unlock()
go func() {
wg.Add(1)
var newData map[K]*Data[V] // 提取缓存降低锁的时间
var oldData map[K]*Data[V]
// 关闭时保存一次数据
defer func() {
c.mu.RLock()
err := c.saveCache(path, c.data)
if err != nil {
c.fatal(err)
}
c.mu.RUnlock()
}()
//defer c.PersistSave(path)
for {
select {
case <-stopChan:
wg.Done()
return
default:
c.mu.RLock()
newData = c.data
c.mu.RUnlock()
newData = make(map[K]*Data[V])
c.data.Range(func(key, value any) bool {
newData[key.(K)] = value.(*Data[V])
return true
})
if compareMaps(newData, oldData) {
continue
}
@@ -81,7 +89,7 @@ func (c *Cache[K, V]) setPersist(path string) func() {
// loadCache 加载持久化数据(会覆盖原有数据)
func (c *Cache[K, V]) loadCache(path string) (map[K]*Data[V], error) {
// 判断文件是否存在
if _, err := os.Stat(path); errors.Is(err, os.ErrNotExist) {
if info, err := os.Stat(path); errors.Is(err, os.ErrNotExist) || info.Size() == 0 {
return nil, nil
}
// 读取文件
@@ -90,9 +98,6 @@ func (c *Cache[K, V]) loadCache(path string) (map[K]*Data[V], error) {
return nil, err
}
// 解析文件
c.mu.Lock()
defer c.mu.Unlock()
var data map[K]*Data[V]
err = gob.NewDecoder(bytes.NewReader(file)).Decode(&data)
if err != nil {
@@ -111,7 +116,7 @@ func (c *Cache[K, V]) saveCache(path string, data map[K]*Data[V]) error {
return err
}
// 写入/覆盖文件
err = os.WriteFile(path, buf.Bytes(), 0666)
err = os.WriteFile(path, buf.Bytes(), 0644)
if err != nil {
return err
}

50
set.go
View File

@@ -5,36 +5,35 @@ import (
"time"
)
func (c *Cache[K, V]) set(key K, val V, t time.Time) (bool, error) {
c.mu.Lock()
defer c.mu.Unlock()
if _, ok := c.data[key]; ok {
return false, errors.New("key already exists")
func (c *Cache[K, V]) set(key K, val V, t time.Time) error {
data := &Data[V]{End: t, Val: val}
if dataAny, ok := c.data.LoadOrStore(key, data); ok {
data = dataAny.(*Data[V])
if data.End.After(time.Now()) {
return errors.New("key already exists")
} else {
c.data.Store(key, data)
}
}
c.data[key] = &Data[V]{End: t, Val: val}
if c.cSet != nil {
c.cSet(key, *c.data[key])
c.cSet(key, *data)
}
return true, nil
return nil
}
// setForced 强制设置数据 数据存在则覆盖
func (c *Cache[K, V]) setForced(key K, val V, t time.Time) error {
c.mu.Lock()
defer c.mu.Unlock()
c.data[key] = &Data[V]{End: t, Val: val}
data := &Data[V]{End: t, Val: val}
c.data.Store(key, data)
if c.cSet != nil {
c.cSet(key, *c.data[key])
c.cSet(key, *data)
}
return nil
}
// Set 设置数据 数据存在则报错
// key: 键 Val: 数据 ttl: 过期时间(秒) 0表示永不过期 返回值: 是否设置成功 错误
func (c *Cache[K, V]) Set(key K, val V, ttl int64) (bool, error) {
// key: 键 Val: 数据 ttl: 过期时间(秒) 0表示永不过期 返回值: 错误
func (c *Cache[K, V]) Set(key K, val V, ttl int64) error {
if ttl == 0 {
return c.set(key, val, time.Time{})
}
@@ -43,6 +42,21 @@ func (c *Cache[K, V]) Set(key K, val V, ttl int64) (bool, error) {
// SetData 设置数据 数据存在则报错 永不过期
// key: 键 Val: 数据 返回值: 是否设置成功 错误 返回值: 是否设置成功 错误
func (c *Cache[K, V]) SetData(key K, val V) (bool, error) {
func (c *Cache[K, V]) SetData(key K, val V) error {
return c.set(key, val, time.Time{})
}
// SetF 设置数据 数据存在则覆盖
// key: 键 Val: 数据 ttl: 过期时间(秒) 0表示永不过期 返回值: 错误
func (c *Cache[K, V]) SetF(key K, val V, ttl int64) error {
if ttl == 0 {
return c.setForced(key, val, time.Time{})
}
return c.setForced(key, val, time.Now().Add(time.Second*time.Duration(ttl)))
}
// SetFData 设置数据 数据存在则覆盖 永不过期
// key: 键 Val: 数据 返回值: 是否设置成功 错误 返回值: 是否设置成功 错误
func (c *Cache[K, V]) SetFData(key K, val V) error {
return c.setForced(key, val, time.Time{})
}

69
up.go
View File

@@ -1,67 +1,72 @@
package cache
import (
"errors"
"time"
)
func (c *Cache[K, V]) update(key K, val V, t time.Time) (bool, error) {
c.mu.Lock()
defer c.mu.Unlock()
if _, ok := c.data[key]; !ok {
return false, errors.New("key not exists")
func (c *Cache[K, V]) update(key K, val V, t time.Time) error {
if _, ok := c.data.Load(key); !ok {
return KeyNotExists
}
c.data[key] = &Data[V]{End: t, Val: val}
data := &Data[V]{End: t, Val: val}
c.data.Store(key, data)
if c.cUpData != nil {
c.cUpData(key, *c.data[key])
c.cUpData(key, *data)
}
if c.cUpTTL != nil {
c.cUpTTL(key, *c.data[key])
c.cUpTTL(key, *data)
}
return true, nil
return nil
}
func (c *Cache[K, V]) updateData(key K, val V) (bool, error) {
c.mu.Lock()
defer c.mu.Unlock()
if _, ok := c.data[key]; !ok {
return false, errors.New("key not exists")
func (c *Cache[K, V]) updateData(key K, val V) error {
dataAny, ok := c.data.Load(key)
if !ok {
return KeyNotExists
}
c.data[key].Val = val
data, ok := dataAny.(*Data[V])
if !ok {
return TypeErrMsg
}
data.Val = val
c.data.Store(key, data)
if c.cUpData != nil {
c.cUpData(key, *c.data[key])
c.cUpData(key, *data)
}
return true, nil
return nil
}
func (c *Cache[K, V]) updateTTL(key K, ttl time.Time) (bool, error) {
c.mu.Lock()
defer c.mu.Unlock()
if _, ok := c.data[key]; !ok {
return false, errors.New("key not exists")
func (c *Cache[K, V]) updateTTL(key K, ttl time.Time) error {
dataAny, ok := c.data.Load(key)
if !ok {
return KeyNotExists
}
c.data[key].End = ttl
data, ok := dataAny.(*Data[V])
if !ok {
return TypeErrMsg
}
data.End = ttl
c.data.Store(key, data)
if c.cUpTTL != nil {
c.cUpTTL(key, *c.data[key])
c.cUpTTL(key, *data)
}
return true, nil
return nil
}
// UpdateData 更新数据
// key: 键 Val: 数据 返回值: 是否更新成功 错误
func (c *Cache[K, V]) UpdateData(key K, val V) (bool, error) {
func (c *Cache[K, V]) UpdateData(key K, val V) error {
return c.updateData(key, val)
}
// UpdateTTL 更新数据过期时间
// key: 键 ttl: 过期时间(秒) 0表示永不过期 返回值: 是否更新成功 错误
func (c *Cache[K, V]) UpdateTTL(key K, ttl int64) (bool, error) {
func (c *Cache[K, V]) UpdateTTL(key K, ttl int64) error {
if ttl == 0 {
return c.updateTTL(key, time.Time{})
}
@@ -70,6 +75,6 @@ func (c *Cache[K, V]) UpdateTTL(key K, ttl int64) (bool, error) {
// UpdateTime 更新数据到期时间(time.Time)
// key: 键 t: 到期时间 返回值: 是否更新成功 错误
func (c *Cache[K, V]) UpdateTime(key K, t time.Time) (bool, error) {
func (c *Cache[K, V]) UpdateTime(key K, t time.Time) error {
return c.updateTTL(key, t)
}