缓存的目的都是为了减少跟数据库的直接交互,提高可用性。常用的方法如,对热点数据缓存、对部分数据预加载、对频繁操作的数据放到缓存中操作等等。
在开发的过程中,我尝试了一种自动处理缓存的方法,记录下,以供参考。
在业务代码,和ORM之间,引入一个模块(SaSql)。将对于数据库的操作,分为单行操作、list操作,即读写单条记录,和读写多条记录。
以list为例:
SaSql获取list数据时,优先从缓存拿,没有则通过ORM从数据库拿。核心代码示意如下:

//读取缓存
cacheParams := &map[string]string{}
if ignoreCache == false {
    saData.Merge(cacheParams, pk)
    saData.Merge(cacheParams, ma)
    if err = saCache.ListGet(tblName, cacheParams, aryPtr); err == nil && aryPtr != nil {
        return nil
    } else {
        if onlyCache {
            return err
        }
    }
}

//读取数据库
if err := session.Find(aryPtr); err == nil {
    //写入缓存
    if ignoreCache == false {
        saCache.ListSet(tblName, cacheParams, aryPtr)
    }

    return nil
} else {
    return err
}

示例中,saCache则是缓存处理,其中,将参数以k_v的形式组合成字符串,作为Redis缓存的key。假如获取同一张表的list,但是参数不同,则会缓存成两条记录。

该方案只是缓存的补充手段,对于缓存数量要有限制,因为同一条数据,可能会出现在多条缓存记录中(因为不同参数都会单独缓存),会出现较多的重复数据,浪费空间。

示例代码中,对分页数据,只缓存前2页数据,就是基于次点考虑。

以下为获取缓存Key的示意代码:

func listKey(tbl string, params *map[string]string) (key string) {
    if tbl == "" || params == nil {
        return ""
    }

    key = revel.AppName + "_" + tbl + "_list_"

    var keyAry []string
    for k := range *params {
        keyAry = append(keyAry, k)
    }
    sort.Strings(keyAry)

    for i, k := range keyAry {
        key += saUtils.SnakeStr(k) + "=" + (*params)[k]
        if i+1 < len(keyAry) {
            key += "_"
        }
    }
    return
}

此方案的优点是,可以应对突发或者意向之外的流量,作为主要缓存策略的一个补充。
再者,每次取数据,都会先走缓存,而Redis是单线程(不考虑最新版本的),对于请求会自动做排队,也会降低数据库压力。

附上基于此方案的压测结果

Server Software:        
Server Hostname:        xxx
Server Port:            443
SSL/TLS Protocol:       TLSv1.2,ECDHE-RSA-AES128-GCM-SHA256,2048,128

Document Path:          xxx
Document Length:        23 bytes

Concurrency Level:      100
Time taken for tests:   484.310 seconds
Complete requests:      250000
Failed requests:        1
   (Connect: 0, Receive: 0, Length: 1, Exceptions: 0)
Write errors:           0
Non-2xx responses:      249999
Total transferred:      155999376 bytes
HTML transferred:       5749977 bytes
Requests per second:    516.20 [#/sec] (mean)
Time per request:       193.724 [ms] (mean)
Time per request:       1.937 [ms] (mean, across all concurrent requests)
Transfer rate:          314.56 [Kbytes/sec] received

最后安利一个自己写的小程序,不喜请忽略
欢迎加微信交流 yf_good134