项目中使用进程内缓存的一些经验及注意事项

前言

  对于项目中一些比较简单的功能,比如在项目启动的时候将简单的userId与configId的对应关系存放的缓存从而减少RDS中的IO操作,这种简单的应用场景我们可以使用进程缓存来代替redis达到同样的效果。

  本文分享一下进程内缓存的使用以及存在的问题分析。

直接使用全局变量的方法

  使用进程内缓存最简单的方式就是定义一个全局的变量,在项目启动的生命周期内将数据存放在这个全局变量下即可:

var globalMap map[string]interface{}

并发情况下使用sync.Map

  在并发情况下,多个goroutine同时操作golang原生的map会出现竞争条件(race condition)问题。

  如果需求是要将进程内缓存存放在一个map中,并且项目会遇到多个goroutine同时操作这个map的情况,最简单的可以使用golang内置的sync.Map去替代原生的map,可以避免race condition问题。

  关于sync.Map的操作可以参考这篇文章:go sync.Map使用和介绍

使用go-cache第三方包

  有一个很好用的第三方包go-cache它也实现了进程内缓存,并且使用加锁的方式确保了多goroutine下的并发安全(看一下源码就知道了,对外暴露的方法都有加锁的操作)。

  项目地址如下:patrickmn/go-cache

  说明文档如下:go-cache包说明文档

  自己做了一下简单的练习:

package test1

import (
    "fmt"
    "github.com/patrickmn/go-cache"
    "testing"
)

func TestInnerProcessCache(t *testing.T) {
    // 永久生效,除非重启服务
    cacheBaby := cache.New(0, 0)
    cacheBaby.Set("name", "whw", 0)

    if name, ok := cacheBaby.Get("name"); ok {
        fmt.Println("name: ", name)
    } else {
        fmt.Println("nothing...")
    }

}

进程缓存存在的问题

  这里先引用一下上面介绍的go-cache包的一个介绍(这个作者还是十分严谨的):

多台机器做负载均衡存在的缓存同步问题    

  设想一下,如果我们的项目部署在了多台服务器上做负载均衡,像下面这样:

  那么问题就来了,项目启动时在后面2台EC2中分别存了一份缓存数据,如果其中一个EC2的缓存数据更新了,对于数据实时性要求特别高的服务来说如何“瞬间”将这个改动同步到另外一台机器上是一个很棘手的问题(即时使用MQ这样的订阅功能也会存在网络传输的时延)。  

  所以对于数据实时性要求特别高的服务,并且服务器也做了负载均衡,如果业务中有需要对缓存更新的操作的话,笔者不建议使用进程内缓存作为缓存数据的方式,推荐使用像redis这样的第三方服务去做。

  当然如果只是将一些固定的数据作为缓存的话,并且数据的更新依赖于项目重启,那么这种场景下使用进程内缓存还是十分方便的。

进程缓存的其他使用场景

  之前在做项目的时候有用到一个自定制的LRU cache的包,挺不错的:使用golang实现一个LRU进程缓存模块

参考文章

go-cache包说明文档 

go sync.Map使用和介绍

进程内缓存,究竟怎么玩?

 

posted on 2021-04-10 11:01  江湖乄夜雨  阅读(343)  评论(0编辑  收藏  举报