最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Golang實(shí)現(xiàn)Redis過期時(shí)間實(shí)例探究

 更新時(shí)間:2024年01月24日 10:09:44   作者:紹納?nullbody筆記  
這篇文章主要介紹了Golang實(shí)現(xiàn)Redis過期時(shí)間實(shí)例探究,

引言

用11篇文章實(shí)現(xiàn)一個(gè)可用的Redis服務(wù),姑且叫EasyRedis吧,希望通過文章將Redis掰開撕碎了呈現(xiàn)給大家,而不是僅僅停留在八股文的層面,并且有非常爽的感覺,歡迎持續(xù)關(guān)注學(xué)習(xí)。

  • [x] easyredis之TCP服務(wù)
  • [x] easyredis之網(wǎng)絡(luò)請(qǐng)求序列化協(xié)議(RESP)
  • [x] easyredis之內(nèi)存數(shù)據(jù)庫(kù)
  • [x] easyredis之過期時(shí)間 (時(shí)間輪實(shí)現(xiàn))
  • [x] easyredis之持久化 (AOF實(shí)現(xiàn))
  • [ ] easyredis之發(fā)布訂閱功能
  • [ ] easyredis之有序集合(跳表實(shí)現(xiàn))
  • [ ] easyredis之 pipeline 客戶端實(shí)現(xiàn)
  • [ ] easyredis之事務(wù)(原子性/回滾)
  • [ ] easyredis之連接池
  • [ ] easyredis之分布式集群存儲(chǔ)

【第四篇】EasyRedis之過期時(shí)間

在使用Redis的時(shí)候經(jīng)常會(huì)對(duì)緩存設(shè)定過期時(shí)間,例如set key value ex 3,設(shè)定過期時(shí)間3s,等到過期以后,我們?cè)賵?zhí)行get key正常情況下是得不到數(shù)據(jù)的。不同的key會(huì)設(shè)定不同的過期時(shí)間1s 5s 2s等等。按照八股文我們知道key過期的時(shí)候,有兩種刪除策略:

  • 惰性刪除:不主動(dòng)刪除過期key,當(dāng)訪問該key的時(shí)候,如果發(fā)現(xiàn)過期了再刪除 好處:對(duì)CPU友好,不用頻繁執(zhí)行刪除,但是對(duì)內(nèi)存不友好,都過期了還占用內(nèi)存
  • 定時(shí)刪除:主動(dòng)刪除key,到了key的過期時(shí)間,立即執(zhí)行刪除 好處:對(duì)內(nèi)存友好,可以緩解內(nèi)存壓力,對(duì)CPU不友好,需要頻繁的執(zhí)行刪除

所以redis就把兩種策略都實(shí)現(xiàn)了,我們看下代碼如何使下?

惰性刪除

本質(zhì)就是訪問的時(shí)候判斷下key是否過期,過期就刪除并返回空。 代碼路徑engine/database.go在獲取key的值時(shí)候,我們會(huì)執(zhí)行一次 db.IsExpire(key)判斷key是否過期

func (db *DB) GetEntity(key string) (*payload.DataEntity, bool) {
	// key 不存在
	val, exist := db.dataDict.Get(key)
	if !exist {
		returnnil, false
	}
	// key是否過期(主動(dòng)檢測(cè)一次)
	if db.IsExpire(key) {
		returnnil, false
	}
	// 返回內(nèi)存數(shù)據(jù)
	dataEntity, ok := val.(*payload.DataEntity)
	if !ok {
		returnnil, false
	}
	return dataEntity, true
}

就是從過期字典ttlDict中獲取key的過期時(shí)間

  • 如果沒有獲取到,說明沒有設(shè)定過期時(shí)間(do nothing)

  • 如果有過期時(shí)間,并且時(shí)間已經(jīng)過期,主動(dòng)刪除之

// 判斷key是否已過期
func (db *DB) IsExpire(key string) bool {
	val, result := db.ttlDict.Get(key)
	if !result {
		returnfalse
	}
	expireTime, _ := val.(time.Time)
	isExpire := time.Now().After(expireTime)
	if isExpire { // 如果過期,主動(dòng)刪除
		db.Remove(key)
	}
	return isExpire
}

定時(shí)刪除

本質(zhì)是對(duì)key設(shè)定一個(gè)過期時(shí)間,時(shí)間一到立即執(zhí)行刪除的任務(wù)。 正常的思路肯定是設(shè)定一個(gè)固定的定時(shí)器,例如3s檢測(cè)一次,這種思路可以,但是存在一個(gè)問題,

  • 如果key的過期時(shí)間為1s,那你3s才檢測(cè)是否太不夠及時(shí)了?

  • 那就把檢測(cè)間隔設(shè)定為1s吧,那如果key的過期時(shí)間都為3s,到執(zhí)行時(shí)間檢測(cè)一遍發(fā)現(xiàn)任務(wù)都沒過期,那不就白白浪費(fèi)CPU時(shí)間了嗎?

這就要推出我們的時(shí)間輪算法了,時(shí)間輪算法就是在模擬現(xiàn)實(shí)世界鐘表的原理

  • 我想里面增加2個(gè)3s的任務(wù),那就將任務(wù)添加到距離當(dāng)前位置pos + 3的位置

  • 同時(shí)再加1個(gè)5s的任務(wù),那就將任務(wù)添加到距離當(dāng)前位置pos + 5的位置

當(dāng)鐘表的指針指向pos + 3的位置,就執(zhí)行任務(wù)鏈表的任務(wù)即可。 因?yàn)殓姳硎茄h(huán)往復(fù)的運(yùn)行,那如果我再添加11s的任務(wù),可以發(fā)現(xiàn)該任務(wù)也是放置到 pos+3的位置,那任務(wù)就要區(qū)分下,到底是3s的任務(wù)還是11s的任務(wù)

所以里面又有了一個(gè)circle的標(biāo)記,表示當(dāng)前任務(wù)是第幾圈的任務(wù)

代碼路徑tool/timewheel

代碼中通過切片模型環(huán),通過鏈表模擬任務(wù)鏈表

// 循環(huán)隊(duì)列 + 鏈表
type TimeWheel struct {
	// 間隔
	interval time.Duration
	// 定時(shí)器
	ticker *time.Ticker
	// 游標(biāo)
	curSlotPos int
	// 循環(huán)隊(duì)列大小
	slotNum int
	// 底層存儲(chǔ)
	slots []*list.List
	m     map[string]*taskPos
	// 任務(wù)通道
	addChannel   chan *task
	cacelChannel chanstring
	// 停止
	stopChannel chanstruct{}
}

當(dāng)添加任務(wù)的時(shí)候,需要通過延遲時(shí)間計(jì)算當(dāng)前任務(wù)的圈數(shù)circle

func (tw *TimeWheel) posAndCircle(d time.Duration) (pos, circle int) {
	// 延遲(秒)
	delaySecond := int(d.Seconds())
	// 間隔(秒)
	intervalSecond := int(tw.interval.Seconds())
	// delaySecond/intervalSecond 表示從curSlotPos位置偏移
	pos = (tw.curSlotPos + delaySecond/intervalSecond) % tw.slotNum
	circle = (delaySecond / intervalSecond) / tw.slotNum
	return
}
func (tw *TimeWheel) addTask(t *task) {
	// 定位任務(wù)應(yīng)該保存在循環(huán)隊(duì)列的位置 & 圈數(shù)
	pos, circle := tw.posAndCircle(t.delay)
	t.circle = circle
	// 將任務(wù)保存到循環(huán)隊(duì)列pos位置
	ele := tw.slots[pos].PushBack(t)
	// 在map中記錄 key -> { pos, ele } 的映射
	if t.key != "" {
		// 已經(jīng)存在重復(fù)的key
		if _, ok := tw.m[t.key]; ok {
			tw.cancelTask(t.key)
		}
		tw.m[t.key] = &taskPos{pos: pos, ele: ele}
	}
}

代碼中注釋的很清晰,也就100多行建議看代碼結(jié)合上圖體會(huì)下(很簡(jiǎn)單)

額外補(bǔ)充

我們?cè)趫?zhí)行set key value ex 3的時(shí)候,先設(shè)定過期時(shí)間為3s,但是在1s的時(shí)候,我們又執(zhí)行了set key value,請(qǐng)問key還會(huì)過期嗎??

答案:不會(huì)過期了。相當(dāng)于對(duì)key去掉了過期時(shí)間。所以在代碼處理中,我們需要考慮這種情況,重復(fù)設(shè)定的問題

代碼細(xì)節(jié)位于engine/string.go set命令處理函數(shù)func cmdSet(db *DB, args [][]byte) protocal.Reply 的尾部位置

項(xiàng)目代碼地址: https://github.com/gofish2020/easyredis 

以上就是Golang實(shí)現(xiàn)Redis過期時(shí)間實(shí)例探究的詳細(xì)內(nèi)容,更多關(guān)于Golang Redis過期時(shí)間的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • Golang實(shí)現(xiàn)IP地址轉(zhuǎn)整數(shù)的方法詳解

    Golang實(shí)現(xiàn)IP地址轉(zhuǎn)整數(shù)的方法詳解

    在 Go 語(yǔ)言中,將 IP 地址轉(zhuǎn)換為整數(shù)涉及到解析 IP 地址并處理其字節(jié)表示,本文給大家介紹了Golang實(shí)現(xiàn)IP地址轉(zhuǎn)整數(shù)的方法,文中有詳細(xì)的代碼示例供大家參考,需要的朋友可以參考下
    2024-02-02
  • go mod init 和go mod tidy命令的使用

    go mod init 和go mod tidy命令的使用

    本文主要介紹了go mod init 和go mod tidy命令的使用,兩者是Go項(xiàng)目依賴管理的關(guān)鍵步驟,下面就來(lái)介紹一下如何使用,感興趣的可以了解一下
    2025-06-06
  • go并發(fā)數(shù)據(jù)一致性事務(wù)的保障面試應(yīng)答

    go并發(fā)數(shù)據(jù)一致性事務(wù)的保障面試應(yīng)答

    這篇文章主要為大家介紹了go并發(fā)數(shù)據(jù)一致性事務(wù)的保障面試應(yīng)答,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-12-12
  • go?singleflight緩存雪崩源碼分析與應(yīng)用

    go?singleflight緩存雪崩源碼分析與應(yīng)用

    這篇文章主要為大家介紹了go?singleflight緩存雪崩源碼分析與應(yīng)用示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-09-09
  • Golang單元測(cè)試、go協(xié)程和管道示例詳解

    Golang單元測(cè)試、go協(xié)程和管道示例詳解

    在Go語(yǔ)言中并發(fā)編程是一種核心特性,這篇文章主要介紹了Golang單元測(cè)試、go協(xié)程和管道的相關(guān)資料,文中通過代碼介紹的非常詳細(xì),需要的朋友可以參考下
    2025-11-11
  • 基于Go?goroutine實(shí)現(xiàn)一個(gè)簡(jiǎn)單的聊天服務(wù)

    基于Go?goroutine實(shí)現(xiàn)一個(gè)簡(jiǎn)單的聊天服務(wù)

    對(duì)于聊天服務(wù),想必大家都不會(huì)陌生,因?yàn)樵谖覀兊纳钪薪?jīng)常會(huì)用到,本文我們用?Go?并發(fā)來(lái)實(shí)現(xiàn)一個(gè)聊天服務(wù)器,這個(gè)程序可以讓一些用戶通過服務(wù)器向其它所有用戶廣播文本消息,文中通過代碼示例介紹的非常詳細(xì),需要的朋友可以參考下
    2023-06-06
  • Go語(yǔ)言中的recover()函數(shù)用法解讀

    Go語(yǔ)言中的recover()函數(shù)用法解讀

    Go語(yǔ)言recover()用于捕獲panic,需與defer配合使用,在defer中調(diào)用時(shí)返回panic值,否則返回nil,Go1.21后panic(nil)觸發(fā)runtime panic,recover返回*runtime.PanicNilError,應(yīng)僅用于處理意外情況,注意作用域限制及性能影響
    2025-07-07
  • Go語(yǔ)言有狀態(tài)goroutine的具體使用

    Go語(yǔ)言有狀態(tài)goroutine的具體使用

    Go語(yǔ)言中的有狀態(tài)goroutine提供了一種基于通信的并發(fā)狀態(tài)管理范式,通過將狀態(tài)的讀寫權(quán)限封裝在單個(gè)goroutine中,避免傳統(tǒng)互斥鎖的競(jìng)爭(zhēng)問題,感興趣的可以了解一下
    2025-07-07
  • golang批量執(zhí)行任務(wù)的通用模板分享

    golang批量執(zhí)行任務(wù)的通用模板分享

    這篇文章主要為大家詳細(xì)介紹了golang實(shí)現(xiàn)批量執(zhí)行任務(wù)的通用模板,文中的示例代碼講解詳細(xì),具有一定的學(xué)習(xí)價(jià)值,感興趣的小伙伴可以了解一下
    2023-11-11
  • 解決Goland中利用HTTPClient發(fā)送請(qǐng)求超時(shí)返回EOF錯(cuò)誤DEBUG

    解決Goland中利用HTTPClient發(fā)送請(qǐng)求超時(shí)返回EOF錯(cuò)誤DEBUG

    這篇文章主要介紹了解決Goland中利用HTTPClient發(fā)送請(qǐng)求超時(shí)返回EOF錯(cuò)誤DEBUG,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來(lái)看看吧
    2020-12-12

最新評(píng)論

临湘市| 陆河县| 临高县| 东兰县| 海盐县| 修文县| 霸州市| 南川市| 宜兰市| 客服| 油尖旺区| 吉木萨尔县| 多伦县| 长阳| 忻城县| 万安县| 稷山县| 西畴县| 宜黄县| 融水| 蕉岭县| 昆山市| 甘肃省| 萍乡市| 垣曲县| 秭归县| 界首市| 榆中县| 石楼县| 江川县| 肇州县| 桐柏县| 丰原市| 抚顺县| 延津县| 清镇市| 玉田县| 双辽市| 库车县| 长治县| 海阳市|