緩存的目的都是爲了減少跟數據庫的直接交互,提高可用性。常用的方法如,對熱點數據緩存、對部分數據預加載、對頻繁操作的數據放到緩存中操作等等。
在開發的過程中,我嘗試了一種自動處理緩存的方法,記錄下,以供參考。
在業務代碼,和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