diff --git a/README.md b/README.md index aa2cc59..70376fe 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,3 @@ # cache -该库是go语言实现的低配版缓存库,类似redis超级阉割版本 \ -该库只将淘汰时间精确到了秒 \ -并且设计上并不支持高精度操作和储蓄大量数据(除非你有足够的内存和超高的性能) \ No newline at end of file +go cache \ No newline at end of file diff --git a/cache.go b/cache.go index ae46a4c..71c1469 100644 --- a/cache.go +++ b/cache.go @@ -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 } diff --git a/del.go b/del.go index 9265ee9..faf500b 100644 --- a/del.go +++ b/del.go @@ -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 } diff --git a/get.go b/get.go index 40d5965..9f504ea 100644 --- a/get.go +++ b/get.go @@ -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 } @@ -61,13 +61,11 @@ func (c *Cache[K, V]) GetTTL(key K) (int64, bool) { // 返回值: 数据 // 该功能为一些状态API提供支持 func (c *Cache[K, V]) GetAll() map[K]Data[V] { - c.mu.RLock() - defer c.mu.RUnlock() - - data := make(map[K]Data[V], len(c.data)) - for k, v := range c.data { - data[k] = *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 } @@ -85,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 diff --git a/go.mod b/go.mod index 6fd5904..e1e588d 100644 --- a/go.mod +++ b/go.mod @@ -1,3 +1,3 @@ module git.slzz.org/gomod/cache -go 1.18 +go 1.24 diff --git a/other.go b/other.go index fdaa745..29d0fce 100644 --- a/other.go +++ b/other.go @@ -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 +} diff --git a/persist.go b/persist.go index fc6eef7..aa7bfea 100644 --- a/persist.go +++ b/persist.go @@ -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 } diff --git a/set.go b/set.go index ae5fa89..abdacc3 100644 --- a/set.go +++ b/set.go @@ -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{}) +} diff --git a/up.go b/up.go index 73ed0d4..e712abc 100644 --- a/up.go +++ b/up.go @@ -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) }