Golang使用ttl機制保存內存數(shù)據(jù)方法詳解
ttl(time-to-live) 數(shù)據(jù)存活時間,我們這里指數(shù)據(jù)在內存中保存一段時間,超過期限則不能被讀取到,與Redis的ttl機制類似。本文僅實現(xiàn)ttl部分,不考慮序列化和反序列化。
獲取當前時間
涉及時間計算,這里首先介紹如何獲取當前時間,以及時間的精度,這里為了簡化,精度到秒級。
使用time.Now可以獲取當前時間,time.Unix 或 time.UnixNano可以獲得時間戳。
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
輸出結果:
2023-02-19 16:52:51.5894329 +0800 CST m=+0.004286801
1676796771
1676796771589432900
數(shù)據(jù)結構
首先定義數(shù)據(jù)結構,數(shù)據(jù)結構及存儲數(shù)據(jù)容器的結構:
type Data struct { Key string Value interface{} Timestamp int64 } type Heap struct { dataMx *sync.RWMutex data map[string]Data }
Data 包括key和value以及ttl時間(單位秒),Heap容器包括map類型data以及RWMutex讀寫鎖,讀寫鎖是支持并發(fā)操作。
下面定義Heap結構一些方法。
Heap操作
主要方法包括New,Set,Del,Get三個方法。
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方法實現(xiàn)邏輯:如果ttl為0則直接返回,反之先初始化Data數(shù)據(jù),這里初始化當前時間為Data的時間戳;接著判斷ttl,如果大于零則Data的時間戳加上ttl,反之為-1;下面開始通過讀寫鎖存儲Heap的data。
Del方法,首先通過讀鎖讀取key對應數(shù)據(jù),如果失敗直接返回(可能已經(jīng)過期,其他協(xié)程已經(jīng)獲取過),反之直接刪除數(shù)據(jù)。
Get方法,讀取邏輯與Del一樣,如果正確讀取,則判斷時間戳,不等于-1且小于當前時間則表明已過期,調用Del方法進行刪除,返回nil和false;反之返回value及true。
測試ttl容器Heap
首先定義heap,然后調用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秒后調用Get方法,兩次直接結果和預期一致:
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) }
總結
本文解釋Golang如果實現(xiàn)ttl機制在內存存儲自動失效數(shù)據(jù)。首先介紹時間戳原理,然后定義數(shù)據(jù)結構,并簡單實現(xiàn)Set、Get、Del方法實現(xiàn)了ttl機制。未來再增加序列化功能:保存和恢復。參考實現(xiàn):https://github.com/leprosus/golang-ttl-map。
到此這篇關于Golang使用ttl機制保存內存數(shù)據(jù)方法詳解的文章就介紹到這了,更多相關Go保存內存數(shù)據(jù)內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
關于golang利用channel和goroutine完成統(tǒng)計素數(shù)的思路
這篇文章主要介紹了golang利用channel和goroutine完成統(tǒng)計素數(shù)的思路詳解,通過思路圖分析及實例代碼相結合給大家介紹的非常詳細,需要的朋友可以參考下2021-08-08Go語言構建流數(shù)據(jù)pipeline的示例詳解
Go的并發(fā)原語可以輕松構建流數(shù)據(jù)管道,從而高效利用?I/O?和多個?CPU,?本文展示了此類pipelines的示例,強調了操作失敗時出現(xiàn)的細微之處,并介紹了干凈地處理失敗的技術,希望對大家有所幫助2024-02-02