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

Go語言使用singleflight解決緩存擊穿

 更新時間:2024年03月22日 09:28:38   作者:陳明勇  
在構(gòu)建高性能的服務(wù)時,緩存是優(yōu)化數(shù)據(jù)庫壓力和提高響應(yīng)速度的關(guān)鍵技術(shù),但使用緩存也會帶來一些問題,其中就包括緩存擊穿,下面我們就來看看Go語言中如何使用singleflight解決緩存擊穿問題吧

前言

在構(gòu)建高性能的服務(wù)時,緩存是優(yōu)化數(shù)據(jù)庫壓力和提高響應(yīng)速度的關(guān)鍵技術(shù)。使用緩存也會帶來一些問題,其中就包括 緩存擊穿,它不僅會導(dǎo)致數(shù)據(jù)庫壓力劇增,引起數(shù)據(jù)庫性能的下降,嚴(yán)重時甚至?xí)艨鍞?shù)據(jù)庫,導(dǎo)致數(shù)據(jù)庫不可用。

在 Go 語言中,golang.org/x/sync/singleflight 包提供了一種機制,確保對于任何特定 key 的并發(fā)請求在同一時刻只執(zhí)行一次。這個機制有效地防止了緩存擊穿問題。

本文將深入探討 Go 語言中 singleflight 包的使用。從緩存擊穿問題的基礎(chǔ)知識開始,進(jìn)而詳細(xì)介紹 singleflight 包的使用,展示如何利用它來避免緩存擊穿。

準(zhǔn)備好了嗎?準(zhǔn)備一杯你最喜歡的咖啡或茶,隨著本文一探究竟吧。

緩存擊穿

緩存擊穿 是指在高并發(fā)的情況下,某個熱點的 key 突然過期,導(dǎo)致大量的請求直接訪問數(shù)據(jù)庫,造成數(shù)據(jù)庫的壓力過大,甚至宕機的現(xiàn)象。

緩存擊穿流程圖.png

常見的解決方案:

  • 設(shè)置熱點數(shù)據(jù)永不過期:對于一些確定的熱點數(shù)據(jù),可以將其設(shè)置為 永不過期,這樣就可以確保不會因為緩存失效而導(dǎo)致請求直接訪問數(shù)據(jù)庫。
  • 設(shè)置互斥鎖:為了防止緩存失效時所有請求同時查詢數(shù)據(jù)庫,可以采用鎖機制確保僅有一個請求查詢數(shù)據(jù)庫并更新緩存,而其他請求則在緩存更新后再進(jìn)行訪問。
  • 提前更新:后臺監(jiān)控緩存的使用情況,當(dāng)緩存即將過期時,異步更新緩存,延長過期時間。

singleflight 包

Package singleflight provides a duplicate function call suppression mechanism.

這段英文來自官方文檔的介紹,直譯過來的意思是:singleflight 包提供了一種“重復(fù)函數(shù)調(diào)用抑制機制”。

換句話說,當(dāng)多個 goroutine 同時嘗試調(diào)用同一個函數(shù)(基于某個給定的 key)時,singleflight 會確保該函數(shù)只會被第一個到達(dá)的 goroutine 調(diào)用,其他 goroutine 會等待這次調(diào)用的結(jié)果,然后共享這個結(jié)果,而不是同時發(fā)起多個調(diào)用。

一句話概括就是 singleflight 將多個請求合并成一個請求,多個請求共享同一個結(jié)果。

組成部分

Group:這是 singleflight 包的核心結(jié)構(gòu)體。它管理著所有的請求,確保同一時刻,對同一資源的請求只會被執(zhí)行一次。Group 對象不需要顯式創(chuàng)建,直接聲明后即可使用。

Do 方法:Group 結(jié)構(gòu)體提供了 Do 方法,這是實現(xiàn)合并請求的主要方法,該方法接收兩個參數(shù):一個是字符串 key(用于標(biāo)識請求資源),另一個是函數(shù) fn,用來執(zhí)行實際的任務(wù)。在調(diào)用 Do 方法時,如果已經(jīng)有一個相同 key 的請求正在執(zhí)行,那么 Do 方法會等待這個請求完成并共享結(jié)果,否則執(zhí)行 fn 函數(shù),然后返回結(jié)果。

Do 方法有三個返回值,前兩個返回值是 fn 函數(shù)的返回值,類型分別為 interface{} 和 error,最后一個返回值是一個 bool 類型,表示 Do 方法的返回結(jié)果是否被多個調(diào)用共享。

DoChan:該方法與 Do 方法類似,但它返回的是一個通道,通道在操作完成時接收到結(jié)果。返回值是通道,意味著我們能以非阻塞的方式等待結(jié)果。

Forget:該方法用于從 Group 中刪除一個 key 以及相關(guān)的請求記錄,確保下次用同一 key 調(diào)用 Do 時,將立即執(zhí)行新請求,而不是復(fù)用之前的結(jié)果。

Result:這是 DoChan 方法返回結(jié)果時所使用的結(jié)構(gòu)體類型,用于封裝請求的結(jié)果。這個結(jié)構(gòu)體包含三個字段,具體如下:

  • Valinterface{} 類型):請求返回的結(jié)果。
  • Errerror 類型):請求過程中發(fā)生的錯誤信息。
  • Sharedbool 類型):表示這個結(jié)果是否被當(dāng)前請求以外的其他請求共享。

安裝

通過以下命令,在 go 應(yīng)用中安裝 singleflight 依賴:

go get golang.org/x/sync/singleflight

使用示例

// https://github.com/chenmingyong0423/blog/blob/master/tutorial-code/go/singleflight/usage/main.go
package main

import (
    "errors"
    "fmt"
    "golang.org/x/sync/singleflight"
    "sync"
)

var errRedisKeyNotFound = errors.New("redis: key not found")

func fetchDataFromCache() (any, error) {
    fmt.Println("fetch data from cache")
    returnnil, errRedisKeyNotFound
}

func fetchDataFromDataBase() (any, error) {
    fmt.Println("fetch data from database")
    return"程序員陳明勇", nil
}

func fetchData() (any, error) {
    cache, err := fetchDataFromCache()
    if err != nil && errors.Is(err, errRedisKeyNotFound) {
        fmt.Println(errRedisKeyNotFound.Error())
        return fetchDataFromDataBase()
    }
    return cache, err
}

func main() {
    var (
        sg singleflight.Group
        wg sync.WaitGroup
    )

    forrange5 {
        wg.Add(1)
        gofunc() {
            defer wg.Done()
            v, err, shared := sg.Do("key", fetchData)
            if err != nil {
                panic(err)
            }
            fmt.Printf("v: %v, shared: %v\n", v, shared)
        }()
    }
    wg.Wait()
}

singleflight.png

這段代碼模擬了一個典型的并發(fā)訪問場景:從緩存獲取數(shù)據(jù),若緩存未命中,則從數(shù)據(jù)庫檢索。在此過程中,singleflight 庫起到了至關(guān)重要的作用。它確保在多個并發(fā)請求嘗試同時獲取相同數(shù)據(jù)時,實際的獲取操作(不論是訪問緩存還是查詢數(shù)據(jù)庫)只會執(zhí)行一次。這樣不僅減輕了數(shù)據(jù)庫的壓力,還有效防止了高并發(fā)環(huán)境下可能發(fā)生的緩存擊穿問題。

代碼運行結(jié)果如下所示:

fetch data from cache
redis: key not found         
fetch data from database     
v: 程序員陳明勇, shared: true
v: 程序員陳明勇, shared: true
v: 程序員陳明勇, shared: true
v: 程序員陳明勇, shared: true
v: 程序員陳明勇, shared: true

根據(jù)運行結(jié)果可知,當(dāng) 5 個 goroutine 并發(fā)獲取相同數(shù)據(jù)時,數(shù)據(jù)獲取操作實際上只由一個goroutine執(zhí)行了一次。此外,由于所有返回的 shared 值均為 true,這表明返回的結(jié)果被其他 4 個goroutine共享。

最佳實踐

key 的設(shè)計

在生成 key 的時候,我們應(yīng)該保證它的唯一性與一致性。

  • 唯一性:確保傳遞給 Do 方法的 key 具有唯一性,以便 Group 區(qū)分不同請求。推薦使用結(jié)構(gòu)化的命名方式來保證 key 的唯一性,例如,可以遵循類似 {類型}):{標(biāo)識} 的規(guī)范來構(gòu)建 key。以獲取用戶信息為例,相應(yīng)的 key 可以是 user:1234,其中 user 標(biāo)識數(shù)據(jù)類型,而 1234 則是具體的用戶標(biāo)識。
  • 一致性:對于相同的請求,無論何時調(diào)用,生成的 key 應(yīng)該保持一致,以便 Group 正確地合并相同的請求,防止非預(yù)期的錯誤。

超時控制

在調(diào)用 Group.Do 方法時,第一個到達(dá)的 goroutine 可以成功執(zhí)行 fn 函數(shù),而其他隨后到達(dá)的 goroutine 將進(jìn)入阻塞狀態(tài)。如果阻塞狀態(tài)持續(xù)過長,可能需要采取降級策略以保證系統(tǒng)的響應(yīng)性,這時候,我們可以利用 Group.DoChan 方法和結(jié)合 select 語句實現(xiàn)超時控制。

以下是一個實現(xiàn)超時控制的簡單示例:

// https://github.com/chenmingyong0423/blog/blob/master/tutorial-code/go/singleflight/timeout_control/main.go
package main

import (
    "fmt"
    "golang.org/x/sync/singleflight"
    "time"
)

func main() {
    var sg singleflight.Group
    doChan := sg.DoChan("key", func() (interface{}, error) {
        time.Sleep(4 * time.Second)
        return"程序員陳明勇", nil
    })
    select {
    case <-doChan:
        fmt.Println("done")
    case <-time.After(2 * time.Second):
        fmt.Println("timeout")
        // 采用其他降級策略
    }
}

小結(jié)

本文首先介紹了 緩存擊穿 的含義及其常見的解決方案。

然后深入探討了 singleflight 包,從基礎(chǔ)概念、組成部分到具體的安裝和使用示例。

接著通過模擬一個典型的并發(fā)訪問場景來演示如何利用 singleflight 來防止在高并發(fā)場景下可能發(fā)生的緩存擊穿問題。

最后,探討在實踐中設(shè)計 key 和控制請求超時的最佳策略,以便更好地理解和應(yīng)用 singleflight,從而優(yōu)化并發(fā)處理邏輯。

到此這篇關(guān)于Go語言使用singleflight解決緩存擊穿的文章就介紹到這了,更多相關(guān)Go singleflight緩存擊穿內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • 利用go-kit組件進(jìn)行服務(wù)注冊與發(fā)現(xiàn)和健康檢查的操作

    利用go-kit組件進(jìn)行服務(wù)注冊與發(fā)現(xiàn)和健康檢查的操作

    這篇文章主要介紹了利用go-kit組件進(jìn)行服務(wù)注冊與發(fā)現(xiàn)和健康檢查的操作,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2021-04-04
  • Golang自旋鎖的相關(guān)介紹

    Golang自旋鎖的相關(guān)介紹

    自旋鎖是指當(dāng)一個線程在獲取鎖的時候,如果鎖已經(jīng)被其他線程獲取,那么該線程將循環(huán)等待,然后不斷地判斷是否能夠被成功獲取,知直到獲取到鎖才會退出循環(huán)
    2022-10-10
  • go微服務(wù)PolarisMesh源碼解析服務(wù)端啟動流程

    go微服務(wù)PolarisMesh源碼解析服務(wù)端啟動流程

    這篇文章主要為大家介紹了go微服務(wù)PolarisMesh源碼解析服務(wù)端啟動流程詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-01-01
  • Go 語言的指針的學(xué)習(xí)筆記

    Go 語言的指針的學(xué)習(xí)筆記

    這篇文章主要介紹了Go 語言的指針的學(xué)習(xí)筆記,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2018-09-09
  • go module使用本地包的方法示例

    go module使用本地包的方法示例

    這篇文章主要介紹了go module使用本地包的方法示例,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2019-03-03
  • golang語言中for循環(huán)語句用法實例

    golang語言中for循環(huán)語句用法實例

    這篇文章主要介紹了golang語言中for循環(huán)語句用法,實例分析了for循環(huán)遍歷的使用技巧,具有一定參考借鑒價值,需要的朋友可以參考下
    2015-01-01
  • golang讀取http的body時遇到的坑及解決

    golang讀取http的body時遇到的坑及解決

    這篇文章主要介紹了golang讀取http的body時遇到的坑及解決方案,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教
    2024-03-03
  • 使用Viper處理Go應(yīng)用程序的配置方法

    使用Viper處理Go應(yīng)用程序的配置方法

    Viper是一個應(yīng)用程序配置解決方案,用于Go應(yīng)用程序,它支持JSON、TOML、YAML、HCL、envfile和Java properties配置文件格式,這篇文章主要介紹了使用Viper處理Go應(yīng)用程序的配置,需要的朋友可以參考下
    2023-09-09
  • 詳解Go語言中的結(jié)構(gòu)體的特性

    詳解Go語言中的結(jié)構(gòu)體的特性

    結(jié)構(gòu)體是Go語言中重要且靈活的概念之一,本文旨在深入介紹Go語言中的結(jié)構(gòu)體,揭示其重要性和靈活性,并向讀者展示結(jié)構(gòu)體支持的眾多特性,需要的可以參考一下
    2023-06-06
  • 使用Go語言實現(xiàn)HTTP客戶端請求并解析響應(yīng)的完整流程

    使用Go語言實現(xiàn)HTTP客戶端請求并解析響應(yīng)的完整流程

    在日常開發(fā)中,無論是調(diào)用 RESTful API、采集網(wǎng)頁數(shù)據(jù),還是進(jìn)行微服務(wù)之間的通信,HTTP 客戶端幾乎無處不在,本文聚焦于如何使用 Go 實現(xiàn)一個 HTTP 客戶端,完成請求發(fā)送、響應(yīng)解析、錯誤處理、Header與Body提取等完整流程,需要的朋友可以參考下
    2025-08-08

最新評論

报价| 威远县| 汕头市| 崇文区| 班玛县| 文昌市| 容城县| 高安市| 安顺市| 得荣县| 江油市| 闻喜县| 大同县| 永定县| 正安县| 佛冈县| 定日县| 泉州市| 绩溪县| 黄大仙区| 大同市| 阿鲁科尔沁旗| 白朗县| 夏津县| 鄢陵县| 嘉义县| 资阳市| 高淳县| 张家川| 剑河县| 洪江市| 伊宁县| 拜泉县| 泸水县| 嘉定区| 科尔| 淮滨县| 闵行区| 溆浦县| 石家庄市| 明溪县|