This commit is contained in:
2025-02-21 08:42:56 +08:00
parent a0ee3b858b
commit 2f99149d92
9 changed files with 146 additions and 124 deletions

View File

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

View File

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

16
del.go
View File

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

45
get.go
View File

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

2
go.mod
View File

@@ -1,3 +1,3 @@
module git.slzz.org/gomod/cache 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 var zero V
return zero 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 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() { func (c *Cache[K, V]) setPersist(path string) func() {
gob.Register(map[K]*Data[V]{}) gob.Register(map[K]*Data[V]{})
stopChan := make(chan struct{}) stopChan := make(chan struct{})
@@ -30,34 +44,28 @@ func (c *Cache[K, V]) setPersist(path string) func() {
if err != nil { if err != nil {
c.fatal(err) c.fatal(err)
} }
c.mu.Lock()
if data != nil { if data != nil {
c.data = data for k, v := range data {
c.data.Store(k, v)
}
} }
c.mu.Unlock()
go func() { go func() {
wg.Add(1) wg.Add(1)
var newData map[K]*Data[V] // 提取缓存降低锁的时间 var newData map[K]*Data[V] // 提取缓存降低锁的时间
var oldData map[K]*Data[V] var oldData map[K]*Data[V]
// 关闭时保存一次数据 // 关闭时保存一次数据
defer func() { //defer c.PersistSave(path)
c.mu.RLock()
err := c.saveCache(path, c.data)
if err != nil {
c.fatal(err)
}
c.mu.RUnlock()
}()
for { for {
select { select {
case <-stopChan: case <-stopChan:
wg.Done() wg.Done()
return return
default: default:
c.mu.RLock() newData = make(map[K]*Data[V])
newData = c.data c.data.Range(func(key, value any) bool {
c.mu.RUnlock() newData[key.(K)] = value.(*Data[V])
return true
})
if compareMaps(newData, oldData) { if compareMaps(newData, oldData) {
continue continue
} }
@@ -81,7 +89,7 @@ func (c *Cache[K, V]) setPersist(path string) func() {
// loadCache 加载持久化数据(会覆盖原有数据) // loadCache 加载持久化数据(会覆盖原有数据)
func (c *Cache[K, V]) loadCache(path string) (map[K]*Data[V], error) { 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 return nil, nil
} }
// 读取文件 // 读取文件
@@ -90,9 +98,6 @@ func (c *Cache[K, V]) loadCache(path string) (map[K]*Data[V], error) {
return nil, err return nil, err
} }
// 解析文件 // 解析文件
c.mu.Lock()
defer c.mu.Unlock()
var data map[K]*Data[V] var data map[K]*Data[V]
err = gob.NewDecoder(bytes.NewReader(file)).Decode(&data) err = gob.NewDecoder(bytes.NewReader(file)).Decode(&data)
if err != nil { if err != nil {
@@ -111,7 +116,7 @@ func (c *Cache[K, V]) saveCache(path string, data map[K]*Data[V]) error {
return err return err
} }
// 写入/覆盖文件 // 写入/覆盖文件
err = os.WriteFile(path, buf.Bytes(), 0666) err = os.WriteFile(path, buf.Bytes(), 0644)
if err != nil { if err != nil {
return err return err
} }

50
set.go
View File

@@ -5,36 +5,35 @@ import (
"time" "time"
) )
func (c *Cache[K, V]) set(key K, val V, t time.Time) (bool, error) { func (c *Cache[K, V]) set(key K, val V, t time.Time) error {
c.mu.Lock() data := &Data[V]{End: t, Val: val}
defer c.mu.Unlock() if dataAny, ok := c.data.LoadOrStore(key, data); ok {
data = dataAny.(*Data[V])
if _, ok := c.data[key]; ok { if data.End.After(time.Now()) {
return false, errors.New("key already exists") 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 { if c.cSet != nil {
c.cSet(key, *c.data[key]) c.cSet(key, *data)
} }
return true, nil return nil
} }
// setForced 强制设置数据 数据存在则覆盖 // setForced 强制设置数据 数据存在则覆盖
func (c *Cache[K, V]) setForced(key K, val V, t time.Time) error { func (c *Cache[K, V]) setForced(key K, val V, t time.Time) error {
c.mu.Lock() data := &Data[V]{End: t, Val: val}
defer c.mu.Unlock() c.data.Store(key, data)
c.data[key] = &Data[V]{End: t, Val: val}
if c.cSet != nil { if c.cSet != nil {
c.cSet(key, *c.data[key]) c.cSet(key, *data)
} }
return nil return nil
} }
// Set 设置数据 数据存在则报错 // Set 设置数据 数据存在则报错
// key: 键 Val: 数据 ttl: 过期时间(秒) 0表示永不过期 返回值: 是否设置成功 错误 // key: 键 Val: 数据 ttl: 过期时间(秒) 0表示永不过期 返回值: 错误
func (c *Cache[K, V]) Set(key K, val V, ttl int64) (bool, error) { func (c *Cache[K, V]) Set(key K, val V, ttl int64) error {
if ttl == 0 { if ttl == 0 {
return c.set(key, val, time.Time{}) 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 设置数据 数据存在则报错 永不过期 // SetData 设置数据 数据存在则报错 永不过期
// key: 键 Val: 数据 返回值: 是否设置成功 错误 返回值: 是否设置成功 错误 // 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{}) 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 package cache
import ( import (
"errors"
"time" "time"
) )
func (c *Cache[K, V]) update(key K, val V, t time.Time) (bool, error) { func (c *Cache[K, V]) update(key K, val V, t time.Time) error {
c.mu.Lock() if _, ok := c.data.Load(key); !ok {
defer c.mu.Unlock() return KeyNotExists
if _, ok := c.data[key]; !ok {
return false, errors.New("key not exists")
} }
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 { if c.cUpData != nil {
c.cUpData(key, *c.data[key]) c.cUpData(key, *data)
} }
if c.cUpTTL != nil { 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) { func (c *Cache[K, V]) updateData(key K, val V) error {
c.mu.Lock() dataAny, ok := c.data.Load(key)
defer c.mu.Unlock() if !ok {
return KeyNotExists
if _, ok := c.data[key]; !ok {
return false, errors.New("key not exists")
} }
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 { 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) { func (c *Cache[K, V]) updateTTL(key K, ttl time.Time) error {
c.mu.Lock() dataAny, ok := c.data.Load(key)
defer c.mu.Unlock() if !ok {
return KeyNotExists
if _, ok := c.data[key]; !ok {
return false, errors.New("key not exists")
} }
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 { if c.cUpTTL != nil {
c.cUpTTL(key, *c.data[key]) c.cUpTTL(key, *data)
} }
return true, nil return nil
} }
// UpdateData 更新数据 // UpdateData 更新数据
// key: 键 Val: 数据 返回值: 是否更新成功 错误 // 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) return c.updateData(key, val)
} }
// UpdateTTL 更新数据过期时间 // UpdateTTL 更新数据过期时间
// key: 键 ttl: 过期时间(秒) 0表示永不过期 返回值: 是否更新成功 错误 // 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 { if ttl == 0 {
return c.updateTTL(key, time.Time{}) 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) // UpdateTime 更新数据到期时间(time.Time)
// key: 键 t: 到期时间 返回值: 是否更新成功 错误 // 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) return c.updateTTL(key, t)
} }