日韩成人免费在线_国产成人一二_精品国产免费人成电影在线观..._日本一区二区三区久久久久久久久不

當(dāng)前位置:首頁 > 科技  > 軟件

Go中使用sync.Map實(shí)現(xiàn)線程安全的緩存

來源: 責(zé)編: 時間:2023-10-24 09:00:30 339觀看
導(dǎo)讀緩存是優(yōu)化現(xiàn)代應(yīng)用程序性能的關(guān)鍵方面。它允許您存儲并快速檢索昂貴操作的結(jié)果或經(jīng)常訪問的數(shù)據(jù),減少了反復(fù)重新計(jì)算或獲取數(shù)據(jù)的需要。在本文中,我們將探討如何使用sync.Map包在Go中實(shí)現(xiàn)線程安全的緩存。這種緩存實(shí)現(xiàn)

緩存是優(yōu)化現(xiàn)代應(yīng)用程序性能的關(guān)鍵方面。它允許您存儲并快速檢索昂貴操作的結(jié)果或經(jīng)常訪問的數(shù)據(jù),減少了反復(fù)重新計(jì)算或獲取數(shù)據(jù)的需要。在本文中,我們將探討如何使用sync.Map包在Go中實(shí)現(xiàn)線程安全的緩存。這種緩存實(shí)現(xiàn)支持緩存條目的過期,確保過時的數(shù)據(jù)不會滯留在緩存中。81v28資訊網(wǎng)——每日最新資訊28at.com

81v28資訊網(wǎng)——每日最新資訊28at.com

為什么要費(fèi)心

在我們開始實(shí)現(xiàn)自己的線程安全內(nèi)存緩存之前,讓我們考慮一下其優(yōu)缺點(diǎn)。考慮到替代方案是使用為緩存而發(fā)明的、有長期使用和支持歷史的外部庫(工具),讓我們思考一下優(yōu)點(diǎn)和缺點(diǎn)。81v28資訊網(wǎng)——每日最新資訊28at.com

使用Go的sync.Map實(shí)現(xiàn)自己的線程安全緩存相比使用像Redis這樣的外部庫有幾個優(yōu)點(diǎn),這取決于您的用例和要求。以下是使用sync.Map創(chuàng)建自己的緩存可能有優(yōu)勢的一些原因:81v28資訊網(wǎng)——每日最新資訊28at.com

  • 更低的延遲:使用像sync.Map這樣的內(nèi)存緩存時,數(shù)據(jù)存儲在應(yīng)用程序的內(nèi)存中。這可能導(dǎo)致比需要應(yīng)用程序和緩存服務(wù)之間的網(wǎng)絡(luò)通信的單獨(dú)服務(wù),如Redis,有更低的訪問延遲。
  • 更簡單的部署:使用基于sync.Map的緩存,無需部署、配置和維護(hù)像Redis這樣的額外服務(wù)。您的緩存解決方案是應(yīng)用程序的一部分,使部署過程更簡單,并可能減少操作復(fù)雜性。
  • 減少資源使用:與像Redis這樣的外部服務(wù)相比,使用sync.Map的內(nèi)存緩存通常消耗更少的資源,從而節(jié)省了內(nèi)存和CPU使用。這對于小規(guī)模的應(yīng)用程序或資源緊張的應(yīng)用程序可能更加經(jīng)濟(jì)高效。
  • 更容易集成:在Go應(yīng)用程序中直接使用sync.Map實(shí)現(xiàn)緩存可以更容易地與現(xiàn)有的代碼庫集成。您不需要學(xué)習(xí)新的API或管理到外部服務(wù)的連接。
  • 定制性:創(chuàng)建自己的緩存實(shí)現(xiàn)時,您可以完全控制其行為和功能。您可以輕松地根據(jù)具體需求調(diào)整緩存,針對您的用例進(jìn)行優(yōu)化,并根據(jù)需要添加自定義過期策略或其他功能。
  • 樂趣:創(chuàng)建實(shí)現(xiàn)緩存的自己的代碼段會帶來很多樂趣,并幫助更好地理解提供緩存功能的外部庫。更好地理解它們有助于更好地利用它們提供的所有功能。

但是,值得注意的是,使用像Redis這樣的外部緩存解決方案對于較大規(guī)模的應(yīng)用程序或那些有更復(fù)雜的緩存需求的應(yīng)用程序有其自身的一系列優(yōu)勢。使用Redis的一些好處包括:81v28資訊網(wǎng)——每日最新資訊28at.com

  • 可擴(kuò)展性:Redis設(shè)計(jì)用于高性能,并可以水平擴(kuò)展以處理大量請求和數(shù)據(jù)大小。
  • 持久性:Redis支持不同級別的數(shù)據(jù)持久性,確保您的緩存數(shù)據(jù)在重啟或崩潰后仍然存在。
  • 高級功能:除了簡單的鍵值緩存外,Redis還提供了一系列功能,如數(shù)據(jù)結(jié)構(gòu)、發(fā)布/訂閱消息等。

最終,選擇使用sync.Map實(shí)現(xiàn)自己的緩存還是使用像Redis這樣的外部庫將取決于您的具體需求、應(yīng)用程序的規(guī)模以及您在性能、復(fù)雜性和資源方面愿意做的權(quán)衡。81v28資訊網(wǎng)——每日最新資訊28at.com

此外,實(shí)現(xiàn)您的緩存會帶來樂趣并幫助更好地理解像Redis這樣的更復(fù)雜的產(chǎn)品。因此,我們將在此文章中實(shí)現(xiàn)一個。81v28資訊網(wǎng)——每日最新資訊28at.com

為什么我們使用sync.Map

簡單地說,因?yàn)樗昝赖貪M足了我們的需要。更深入的解釋 - sync.Map是Go標(biāo)準(zhǔn)庫中的一個并發(fā)的、線程安全的map實(shí)現(xiàn)。它設(shè)計(jì)用于在多個goroutine并發(fā)訪問映射的情況下使用,并且鍵的數(shù)量是未知的或隨時間變化的。81v28資訊網(wǎng)——每日最新資訊28at.com

值得注意的是,雖然sync.Map是特定用例的一個很好的選擇,但它并不意味著要替換所有場景的內(nèi)置map類型。特別是,sync.Map最適合以下情況:81v28資訊網(wǎng)——每日最新資訊28at.com

  • 映射主要是讀取密集型,偶爾寫入。
  • 鍵的數(shù)量隨時間變化或事先不知道。
  • 映射由多個goroutine并發(fā)訪問。

在鍵的數(shù)量是固定的或事先知道的情況下,且映射可以預(yù)先分配,使用適當(dāng)?shù)耐饺鐂ync.Mutex或sync.RWMutex的內(nèi)置map類型可能會提供更好的性能。81v28資訊網(wǎng)——每日最新資訊28at.com

創(chuàng)建SafeCache

如上所述,我們的SafeCache是一個簡單的、線程安全的緩存,使用Go的sync.Map存儲其鍵值對。81v28資訊網(wǎng)——每日最新資訊28at.com

首先,我們定義一個CacheEntry結(jié)構(gòu)來保存值及其過期時間戳:81v28資訊網(wǎng)——每日最新資訊28at.com

type CacheEntry struct {    value      interface{}    expiration int64}

在SafeCache結(jié)構(gòu)中嵌入了一個sync.Map,它提供了對鍵值對的并發(fā)安全訪問:81v28資訊網(wǎng)——每日最新資訊28at.com

type SafeCache struct {    syncMap sync.Map}

向緩存中添加值

然后我們定義了一個 Set 方法,該方法允許我們在緩存中存儲一個帶有指定生存時間(TTL,Time To Live)的值。TTL 決定了緩存條目應(yīng)被認(rèn)為有效的時間長度。一旦 TTL 過期,在下一個清理周期中將會移除該緩存條目。81v28資訊網(wǎng)——每日最新資訊28at.com

func (sc *SafeCache) Set(key string, value interface{}, ttl time.Duration) {    expiration := time.Now().Add(ttl).UnixNano()    sc.syncMap.Store(key, CacheEntry{value: value, expiration: expiration})}

從緩存中檢索值

接下來需要的方法是 Get,它使用鍵從緩存中檢索值。如果沒有找到該值或該值已過期,該方法將返回 false:81v28資訊網(wǎng)——每日最新資訊28at.com

func (sc *SafeCache) Get(key string) (interface{}, bool) {    // ... (see the provided code for the full implementation)}

在 Get 方法中重要的是從緩存加載值后進(jìn)行類型斷言。我們依賴于 sync.Map 的 Load 方法,該方法返回接口。81v28資訊網(wǎng)——每日最新資訊28at.com

entry, found := sc.syncMap.Load(key) if !found {  return nil, false } // Type assertion to CacheEntry, as entry is an interface{} cacheEntry := entry.(CacheEntry)

從緩存中移除值

當(dāng)然,我們還需要一個 Delete 方法,使我們能夠從緩存中移除一個值:81v28資訊網(wǎng)——每日最新資訊28at.com

func (sc *SafeCache) Delete(key string) {    sc.syncMap.Delete(key)}

清理過期條目

我們通過 CleanUp 方法擴(kuò)展了緩存,該方法負(fù)責(zé)定期從緩存中刪除過期的條目。它使用 sync.Map 提供的 Range 方法遍歷緩存中的所有鍵值對,并刪除那些TTL已過期的條目:81v28資訊網(wǎng)——每日最新資訊28at.com

func (sc *SafeCache) CleanUp() {    // ... (see the provided code for the full implementation)}

要運(yùn)行 CleanUp 方法,我們可以在初始化緩存時啟動一個單獨(dú)的 Goroutine:81v28資訊網(wǎng)——每日最新資訊28at.com

cache := &SafeCache{}go cache.CleanUp()

完整的代碼片段

package cacheimport ( "sync" "time")// CacheEntry is a value stored in the cache.type CacheEntry struct { value      interface{} expiration int64}// SafeCache is a thread-safe cache.type SafeCache struct { syncMap sync.Map}// Set stores a value in the cache with a given TTL// (time to live) in seconds.func (sc *SafeCache) Set(key string, value interface{}, ttl time.Duration) { expiration := time.Now().Add(ttl).UnixNano() sc.syncMap.Store(key, CacheEntry{value: value, expiration: expiration})}// Get retrieves a value from the cache. If the value is not found// or has expired, it returns false.func (sc *SafeCache) Get(key string) (interface{}, bool) { entry, found := sc.syncMap.Load(key) if !found {  return nil, false } // Type assertion to CacheEntry, as entry is an interface{} cacheEntry := entry.(CacheEntry) if time.Now().UnixNano() > cacheEntry.expiration {  sc.syncMap.Delete(key)  return nil, false } return cacheEntry.value, true}// Delete removes a value from the cache.func (sc *SafeCache) Delete(key string) { sc.syncMap.Delete(key)}// CleanUp periodically removes expired entries from the cache.func (sc *SafeCache) CleanUp() { for {  time.Sleep(1 * time.Minute)  sc.syncMap.Range(func(key, entry interface{}) bool {   cacheEntry := entry.(CacheEntry)   if time.Now().UnixNano() > cacheEntry.expiration {    sc.syncMap.Delete(key)   }   return true  }) }}

最后,你可以運(yùn)行以下的 main.go 程序來檢查緩存是否工作。我們創(chuàng)建了一個HTTP服務(wù)器,它在“/compute”端點(diǎn)監(jiān)聽請求。該服務(wù)器接受一個整數(shù)n作為查詢參數(shù),并返回昂貴計(jì)算的結(jié)果(在這種情況下,帶有模擬延遲的簡單平方操作)。服務(wù)器首先檢查緩存,看看給定輸入的結(jié)果是否已經(jīng)被緩存;如果沒有,它會計(jì)算結(jié)果,將其存儲在緩存中,并將其返回給客戶端。81v28資訊網(wǎng)——每日最新資訊28at.com

要測試服務(wù)器,運(yùn)行代碼并請求http://localhost:8080/compute?n=5。第一個請求會花費(fèi)更長的時間(由于模擬的延遲),但具有相同n的后續(xù)請求將立即返回緩存的結(jié)果。81v28資訊網(wǎng)——每日最新資訊28at.com

package mainimport ( "fmt" "log" "net/http" "safe-cache/cache" "strconv" "time")func expensiveComputation(n int) int { // Simulate an expensive computation time.Sleep(2 * time.Second) return n * n}func main() { safeCache := &cache.SafeCache{} // Start a goroutine to periodically clean up the cache go safeCache.CleanUp() http.HandleFunc("/compute", func(w http.ResponseWriter, r *http.Request) {  query := r.URL.Query()  n, err := strconv.Atoi(query.Get("n"))  if err != nil {   http.Error(w, "Invalid input", http.StatusBadRequest)   return  }  cacheKey := fmt.Sprintf("result_%d", n)  cachedResult, found := safeCache.Get(cacheKey)  var result int  if found {   result = cachedResult.(int)  } else {   result = expensiveComputation(n)   safeCache.Set(cacheKey, result, 1*time.Minute)  }  _, err = fmt.Fprintf(w, "Result: %d/n", result)  if err != nil {   return  } }) log.Fatal(http.ListenAndServe(":8080", nil))}

結(jié)論

在本文中,我們展示了如何使用sync.Map包在Go中實(shí)現(xiàn)一個簡單、線程安全的緩存。81v28資訊網(wǎng)——每日最新資訊28at.com

這個緩存實(shí)現(xiàn)支持基于TTL的過期的鍵值存儲,并可以輕松地集成到你的Go應(yīng)用中,以提高性能并減少對你的數(shù)據(jù)源或計(jì)算資源的負(fù)載。81v28資訊網(wǎng)——每日最新資訊28at.com

本文鏈接:http://www.www897cc.com/showinfo-26-14723-0.htmlGo中使用sync.Map實(shí)現(xiàn)線程安全的緩存

聲明:本網(wǎng)頁內(nèi)容旨在傳播知識,若有侵權(quán)等問題請及時與本網(wǎng)聯(lián)系,我們將在第一時間刪除處理。郵件:2376512515@qq.com

上一篇: Java21新特性——虛擬線程

下一篇: 命令模式:將請求封裝為對象

標(biāo)簽:
  • 熱門焦點(diǎn)
Top 主站蜘蛛池模板: 双柏县| 鄢陵县| 顺平县| 马鞍山市| 安吉县| 从江县| 富民县| 徐州市| 洮南市| 平塘县| 武定县| 泸水县| 阿拉尔市| 苗栗县| 拜泉县| 上蔡县| 邵东县| 收藏| 贵定县| 常德市| 比如县| 女性| 平利县| 酒泉市| 洛宁县| 铁力市| 盐津县| 东阳市| 抚顺市| 炎陵县| 阿坝县| 长沙县| 界首市| 大名县| 平顺县| 永顺县| 抚顺市| 清水县| 陆河县| 邓州市| 仲巴县|