Golang使用ttl機(jī)制保存內(nèi)存數(shù)據(jù)方法詳解
ttl(time-to-live) 數(shù)據(jù)存活時(shí)間,我們這里指數(shù)據(jù)在內(nèi)存中保存一段時(shí)間,超過期限則不能被讀取到,與Redis的ttl機(jī)制類似。本文僅實(shí)現(xiàn)ttl部分,不考慮序列化和反序列化。
獲取當(dāng)前時(shí)間
涉及時(shí)間計(jì)算,這里首先介紹如何獲取當(dāng)前時(shí)間,以及時(shí)間的精度,這里為了簡化,精度到秒級(jí)。
使用time.Now可以獲取當(dāng)前時(shí)間,time.Unix 或 time.UnixNano可以獲得時(shí)間戳。
now := time.Now() // current local time sec := now.Unix() // number of seconds since January 1, 1970 UTC nsec := now.UnixNano() // number of nanoseconds since January 1, 1970 UTC fmt.Println(now) // time.Time fmt.Println(sec) // int64 fmt.Println(nsec) // int64
輸出結(jié)果:
2023-02-19 16:52:51.5894329 +0800 CST m=+0.004286801
1676796771
1676796771589432900
數(shù)據(jù)結(jié)構(gòu)
首先定義數(shù)據(jù)結(jié)構(gòu),數(shù)據(jù)結(jié)構(gòu)及存儲(chǔ)數(shù)據(jù)容器的結(jié)構(gòu):
type Data struct {
Key string
Value interface{}
Timestamp int64
}
type Heap struct {
dataMx *sync.RWMutex
data map[string]Data
}
Data 包括key和value以及ttl時(shí)間(單位秒),Heap容器包括map類型data以及RWMutex讀寫鎖,讀寫鎖是支持并發(fā)操作。
下面定義Heap結(jié)構(gòu)一些方法。
Heap操作
主要方法包括New,Set,Del,Get三個(gè)方法。
func New() *Heap {
return &Heap{
dataMx: &sync.RWMutex{},
data: map[string]Data{},
}
}
func (h *Heap) Set(key string, value interface{}, ttl int64) {
if ttl == 0 {
return
}
data := Data{
Key: key,
Value: value,
Timestamp: time.Now().Unix(),
}
if ttl > 0 {
data.Timestamp += ttl
} else if ttl < 0 {
data.Timestamp = -1
}
h.dataMx.Lock()
h.data[key] = data
h.dataMx.Unlock()
}
func (h *Heap) Get(key string) (val interface{}, ok bool) {
var data Data
h.dataMx.RLock()
data, ok = h.data[key]
h.dataMx.RUnlock()
if ok {
if data.Timestamp != -1 && data.Timestamp <= time.Now().Unix() {
h.Del(key)
ok = false
} else {
val = data.Value
}
}
return
}
func (h *Heap) Del(key string) {
h.dataMx.RLock()
_, ok := h.data[key]
h.dataMx.RUnlock()
if !ok {
return
}
h.dataMx.Lock()
delete(h.data, key)
h.dataMx.Unlock()
}
New方法無需多解釋,我們直接看Set方法。
Set方法實(shí)現(xiàn)邏輯:如果ttl為0則直接返回,反之先初始化Data數(shù)據(jù),這里初始化當(dāng)前時(shí)間為Data的時(shí)間戳;接著判斷ttl,如果大于零則Data的時(shí)間戳加上ttl,反之為-1;下面開始通過讀寫鎖存儲(chǔ)Heap的data。
Del方法,首先通過讀鎖讀取key對(duì)應(yīng)數(shù)據(jù),如果失敗直接返回(可能已經(jīng)過期,其他協(xié)程已經(jīng)獲取過),反之直接刪除數(shù)據(jù)。
Get方法,讀取邏輯與Del一樣,如果正確讀取,則判斷時(shí)間戳,不等于-1且小于當(dāng)前時(shí)間則表明已過期,調(diào)用Del方法進(jìn)行刪除,返回nil和false;反之返回value及true。
測(cè)試ttl容器Heap
首先定義heap,然后調(diào)用Set方法,增加數(shù)據(jù)key,value,ttl為2秒:
func main() {
keyTag := "key"
heap := New()
defer func() {
heap.Del(keyTag)
}()
heap.Set(keyTag, "value", 2)
time.Sleep(1 * time.Second)
val, flag := heap.Get(keyTag)
fmt.Printf("%v, %v\n", val, flag)
time.Sleep(1 * time.Second)
val, flag = heap.Get(keyTag)
fmt.Printf("%v, %v\n", val, flag)
}
然后模擬等待1秒后調(diào)用Get方法,兩次直接結(jié)果和預(yù)期一致:
value, true
<nil>, false
完整代碼
下面給出完整代碼:
package main
import (
"fmt"
"sync"
"time"
)
type Data struct {
Key string
Value interface{}
Timestamp int64
}
type Heap struct {
dataMx *sync.RWMutex
data map[string]Data
}
func New() *Heap {
return &Heap{
dataMx: &sync.RWMutex{},
data: map[string]Data{},
}
}
func (h *Heap) Set(key string, value interface{}, ttl int64) {
if ttl == 0 {
return
}
data := Data{
Key: key,
Value: value,
Timestamp: time.Now().Unix(),
}
if ttl > 0 {
data.Timestamp += ttl
} else if ttl < 0 {
data.Timestamp = -1
}
h.dataMx.Lock()
h.data[key] = data
h.dataMx.Unlock()
}
func (h *Heap) Get(key string) (val interface{}, ok bool) {
var data Data
h.dataMx.RLock()
data, ok = h.data[key]
h.dataMx.RUnlock()
if ok {
if data.Timestamp != -1 && data.Timestamp <= time.Now().Unix() {
h.Del(key)
ok = false
} else {
val = data.Value
}
}
return
}
func (h *Heap) Del(key string) {
h.dataMx.RLock()
_, ok := h.data[key]
h.dataMx.RUnlock()
if !ok {
return
}
h.dataMx.Lock()
delete(h.data, key)
h.dataMx.Unlock()
}
func main() {
keyTag := "key"
heap := New()
defer func() {
heap.Del(keyTag)
}()
heap.Set(keyTag, "value", 2)
time.Sleep(1 * time.Second)
val, flag := heap.Get(keyTag)
fmt.Printf("%v, %v\n", val, flag)
time.Sleep(1 * time.Second)
val, flag = heap.Get(keyTag)
fmt.Printf("%v, %v\n", val, flag)
}
總結(jié)
本文解釋Golang如果實(shí)現(xiàn)ttl機(jī)制在內(nèi)存存儲(chǔ)自動(dòng)失效數(shù)據(jù)。首先介紹時(shí)間戳原理,然后定義數(shù)據(jù)結(jié)構(gòu),并簡單實(shí)現(xiàn)Set、Get、Del方法實(shí)現(xiàn)了ttl機(jī)制。未來再增加序列化功能:保存和恢復(fù)。參考實(shí)現(xiàn):https://github.com/leprosus/golang-ttl-map。
到此這篇關(guān)于Golang使用ttl機(jī)制保存內(nèi)存數(shù)據(jù)方法詳解的文章就介紹到這了,更多相關(guān)Go保存內(nèi)存數(shù)據(jù)內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
SingleFlight模式的Go并發(fā)編程學(xué)習(xí)
這篇文章主要為大家介紹了SingleFlight模式的Go并發(fā)編程學(xué)習(xí),有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-04-04
go slice 擴(kuò)容實(shí)現(xiàn)原理源碼解析
這篇文章主要為大家介紹了go slice 擴(kuò)容實(shí)現(xiàn)原理源碼解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-01-01
深入了解Go的interface{}底層原理實(shí)現(xiàn)
本文主要介紹了Go的interface{}底層原理實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2022-06-06
Go語言使用Timeout Context取消任務(wù)的實(shí)現(xiàn)
本文主要介紹了Go語言使用Timeout Context取消任務(wù)的實(shí)現(xiàn),包括基本的任務(wù)取消和控制HTTP客戶端請(qǐng)求的超時(shí),具有一定的參考價(jià)值,感興趣的可以了解一下2024-01-01
Go語言中切片(slice)和數(shù)組(array)的區(qū)別詳解
Go語言中切片(slice)和數(shù)組(array)是兩種不同的數(shù)據(jù)結(jié)構(gòu),它們?cè)谟梅ê托袨樯嫌幸恍┲匾獏^(qū)別,所以本文就通過一些代碼示例給大家詳細(xì)的介紹一下Go語言中切片(slice)和數(shù)組(array)的區(qū)別,需要的朋友可以參考下2023-09-09
golang版本升級(jí)的簡單實(shí)現(xiàn)步驟
個(gè)人感覺Go在眾多高級(jí)語言中,是在各方面都比較高效的,下面這篇文章主要給大家介紹了關(guān)于golang版本升級(jí)的簡單實(shí)現(xiàn)步驟,文中通過實(shí)例代碼介紹的非常詳細(xì),需要的朋友可以參考下2023-02-02

