内存缓存
从java转到golang,一个很不方便的点就是golang没有切面。最近项目里需要做一个内存缓存,对于数据库中的一些配置信息,避免每次都从数据库查询,以减轻数据库的压力,也降低数据库查询导致的接口失败问题。
最开始实现很简单,最简单的业务实现:
if cache 存在&&未过期
返回cache中的数据
else
从数据库查询
放入cache
返回查询的数据
但是写多了之后,就会想每次重复的一块逻辑能不能抽出来呢?java里有一个很方便也很通用的做法是,通过注解实现一个切面,将从cache中取数据
以及放入数据到cache
的部分抽离出公共逻辑,但是golang作为一个还不成熟的baby,目前还处于全靠堆业务逻辑代码实现功能的阶段,所以有没有一个比较好的实现呢?
我期望的一个实现是对业务代码(从数据库查询)的侵入越小越好
下面是一个实现示例:
import (
"fmt"
"time"
)
var CacheMap = make(map[string]Cache)
type Cache struct {
Value string
ExpireTime time.Time
}
func withithCache(cacheKey string, expireTime int64, function func() (string, error)) (string, error) {
if value, ok := CacheMap[cacheKey]; ok {
if value.ExpireTime.After(time.Now()) {
fmt.Println("get from cache")
return value.Value, nil
}
}
value, err := function()
if err != nil {
return "", err
}
//todo 此处应该加锁
CacheMap[cacheKey] = Cache{
Value: value,
ExpireTime: time.Now().Add(time.Duration(expireTime) * time.Second),
}
//
return value, nil
}
下边写一个调用示例:
func queryDbData(param string) (string, error) {
return withithCache("queryDbData", 4, func() (s string, err error) {
fmt.Println("get from db")
return param, nil
})
}
写一个测试方法测试一下:
func TestCacheProxy(t *testing.T) {
for i := 0; i < 10; i++ {
data, err := queryDbData("abc")
fmt.Println(data, err)
time.Sleep(1 * time.Second)
}
}
其中queryDbData
是一个简单的数据库查询操作,在TestCacheProxy
中可以直接调用
而queryDbData
的实现也很简单
不使用cache的实现方式:
func queryDbDataDirectly(param string) (string, error) {
fmt.Println("get from db")
return param, nil
}
可以看到,相比queryDbData
来说,只是多了withithCache
一个调用,以设置缓存的key和缓存时间。而withithCache
也可以同时被其他希望做内存缓存的方法调用。
进阶
以上示例代码只是一个最简单的实现,目前github上有很多localcache的实现,对内存缓存做了更多丰富的实现,包括:
- 内存缓存的过期策略配置,参考redis的过期策略,包括:
定时清理
,惰性清理
,定期清理
- 设置最大使用内存
- 在达到了最大使用内存后,内存的淘汰策略:
FIFO
,LRU
,LFU
想做工程使用的,可以将示例代码中的map
使用更完善的内存缓存替换下