Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 2f99149d92 | |||
| a0ee3b858b |
@@ -1,5 +1,3 @@
|
||||
# cache
|
||||
|
||||
该库是go语言实现的低配版缓存库,类似redis超级阉割版本 \
|
||||
该库只将淘汰时间精确到了秒 \
|
||||
并且设计上并不支持高精度操作和储蓄大量数据(除非你有足够的内存和超高的性能)
|
||||
go cache
|
||||
30
cache.go
30
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.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)
|
||||
}
|
||||
}
|
||||
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 {
|
||||
c.persist()
|
||||
}
|
||||
c.data.Clear()
|
||||
return
|
||||
}
|
||||
|
||||
16
del.go
16
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
|
||||
}
|
||||
|
||||
|
||||
37
get.go
37
get.go
@@ -14,16 +14,16 @@ 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 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.Before(time.Now()) {
|
||||
} else if data.End.After(time.Now()) {
|
||||
return data.Val, int64(data.End.Sub(time.Now()).Seconds()), true
|
||||
}
|
||||
}
|
||||
}
|
||||
return c.zero(), 0, false
|
||||
}
|
||||
|
||||
@@ -37,15 +37,15 @@ 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 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
|
||||
|
||||
9
other.go
9
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
|
||||
}
|
||||
|
||||
45
persist.go
45
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
|
||||
}
|
||||
|
||||
50
set.go
50
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{})
|
||||
}
|
||||
|
||||
69
up.go
69
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)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user