Golang 工做笔记 go-cache

后端技术基本都要和缓存打交道,最近恰好工做上碰到了这方法的需求,迷迷糊糊的用了一下golang的这个包如今打算写篇心得

首先开篇先稍微跑下题,什么是缓存(cache)?

缓存(cache,原始意义是指访问速度比通常 随机存取存储器(RAM)快的一种高速存储器,一般它不像系统主存那样使用 DRAM技术,而使用昂贵但较快速的 SRAM技术。缓存的设置是全部现代计算机系统发挥高性能的重要因素之一。


打个不恰当的比喻,当你想要作菜,你须要有原材料,在对它进行处理,而后在吃。mysql

若是你每次切一次菜从冰箱里拿一次,如此往复很是浪费时间,你本身也会累。这时候你能够拿个篮子,一次将冰箱的你要用的材料(蔬菜)装起来,而后放到菜板旁边备用。git

上面的例子就是:冰箱:数据库  蔬菜:数据   篮子:缓存容器  github

接下来就来介绍一下今天主要用的包 go-cachegolang

go-cache 是一个基于内存的、高速的,存储k-v格式的缓存工具。它适用于运行在单台机器上的应用程序,能够存储任何数据类型的值,并能够被多个goroutine安全地使用。 虽然go-cache 不打算用做持久数据存储,可是能够将整个缓存数据保存到文件(或任何io.Reader/Writer)中,而且能快速从中指定数据源加载,快速恢复状态。

go-cache核心代码

type cache struct {
    defaultExpiration time.Duration //默认的通用key实效时长
    items map[string]Item //底层的map存储
    mu sync.RWMutex //因为map是非线程安全的,增长的全局锁
    onEvicted func(string, interface{})//失效key时,回触发,我本身命名为回收函数
    janitor *janitor //监视器,Goroutine,定时轮询用于失效key
}复制代码

demo算法

import (
	"fmt"
	"github.com/patrickmn/go-cache"
	"time"
)
func main() {
	// 默认过时时间为5min,每10min清理一次过时缓存
	c := cache.New(5*time.Minute, 10*time.Minute)

	// 设置key-value,并设置为默认过时时间
	c.Set("foo", "bar", cache.DefaultExpiration)

	// 设置一个不会过时的key,该key不会自动删除,从新更新key或者使用c.Delete("baz")
	c.Set("baz", 42, cache.NoExpiration)

	// 从缓存获取对应key的值
	foo, found := c.Get("foo")
	if found {
		fmt.Println(foo)
	}

	// Since Go is statically typed, and cache values can be anything, type
	// assertion is needed when values are being passed to functions that don't
	// take arbitrary types, (i.e. interface{}). The simplest way to do this for
	// values which will only be used once--e.g. for passing to another
	// function--is:
	foo, found := c.Get("foo")
	if found {
		MyFunction(foo.(string))
	}

	// This gets tedious if the value is used several times in the same function.
	// You might do either of the following instead:
	if x, found := c.Get("foo"); found {
		foo := x.(string)
		// ...
	}
	// or
	var foo string
	if x, found := c.Get("foo"); found {
		foo = x.(string)
	}
	// ...
	// foo can then be passed around freely as a string

	// Want performance? Store pointers!
	c.Set("foo", &MyStruct, cache.DefaultExpiration)
	if x, found := c.Get("foo"); found {
		foo := x.(*MyStruct)
			// ...
	}
}复制代码

前面的例子对应到程序里面就是我此次面对的一个小问题sql

我在网页上须要导出一个报表,每行每一个单元格都须要从对应的model中取出相应的数据可是由于这项数据确定不可能只存在一个model里,因此须要去查相关联的表。若是每次用哪一个model就去库里去查相应的数据,速度就会巨慢无比(原来的人就是这么写的因此咱们须要去优化它)mongodb

首先咱们须要把数据从数据库取出,这里我用的是mongodb,也就是将里面collection的数据所有取出来(collection你能够理解为mysql中的表)数据库

for _, collection := range collections {
		switch collection {
		case "product":
			products, err := gql.FindProduct(ctx, mongoplus.M{})
			if err != nil {
				logrus.Warn(err)
			}
			for _, product := range products {
				temp := product
				err := coll.Set("product_"+product.ID, &temp)
				if err != nil {
					logrus.Warn(err)
				}
			}
		case "user":
			users, err := gql.FindUser(ctx, mongoplus.M{})
			if err != nil {
				logrus.Warn(err)
			}
			for _, user := range users {
				temp := user
				err := coll.Set("user_"+user.ID, &temp)
				if err != nil {
					logrus.Warn(err)
				}
			}
		case "company":
			companys, err := gql.FindCompanyCache(ctx, mongoplus.M{})
			if err != nil {
				logrus.Warn(err)
			}
			for _, com := range companys {
				temp := com
				err := coll.Set("com_"+com.ID, &temp)
				if err != nil {
					logrus.Warn(err)
				}
			}
		case "region":
			Regions, err := gql.FindRegion(ctx, mongoplus.M{})
			if err != nil {
				logrus.Warn(err)
			}
			for _, Region := range Regions {
				temp := Region
				err := coll.Set("region_"+Region.ID, &temp)
				if err != nil {
					logrus.Warn(err)
				}
			}
		case "industry":
			industrys, err := gql.FindIndustry(ctx, mongoplus.M{})
			if err != nil {
				logrus.Warn(err)
			}
			for _, industry := range industrys {
				temp := industry
				err := coll.Set("industry_"+industry.ID, &temp)
				if err != nil {
					logrus.Warn(err)
				}
			}
		}

	}
	return coll
}复制代码

上面的代码我把去出的数据都放在了容器里面coll.Set("product_"+product.ID, &temp)后端

我采用的方式是字段id_+model的形式缓存

而后要用的时候直接从数据库中读取数据就能优化一部分时间

总结:

gocache相对简单,用了map[string]Item来进行存储,没有限制大小,只要内存容许能够一直存,没有上限,这个在实际生产中须要注意。

gocache很简单,可是也有很多问题没有作,简单列一些本身想到的,能够一块儿优化下:
1. cache数量没有上限,这个在线上使用的时候仍是容易出问题
2. 调用get获取对象的时候,若是对象不存在,get方法会直接返回nil,其实这里能够优化一下若是没有命中能够从数据库load一下。

三、一些命中没法跟踪。

结语:

其实对于go-cache还有其余地方能够分析,好比它锁的粒度,结合系统的垃圾回收等等,可是因为我学习golang语言时间不长,不少地方没有学通就不写出来丢人啦。等之后系统的学习go的多线程和其余算法后,我会更新go-cache相关的知识。将来加油!

相关文章
相关标签/搜索