49.Go避免大量并發訪問DB、避免緩存擊穿、緩存穿透、緩存雪崩以及使用延遲雙刪保證數據一致性

文章目錄

  • 一、在高并發下,如何避免大量請求直接訪問數據庫?
  • 二、避免緩存擊穿
  • 二、避免緩存穿透
  • 三、避免緩存雪崩
  • 四、延遲雙刪保證數據一致性
  • 五、在使用 Go 的 time.AfterFunc 函數時,如果刪除緩存操作失敗怎么辦?

MySQLRedis是工作中最常見的兩個組件,那么在使用過程中也有一些常見的問題和解決辦法。本文通過 Go實現介紹它們。

一、在高并發下,如何避免大量請求直接訪問數據庫?

在高并發下,如果讓大量請求直接訪問數據庫,可能會造成數據庫壓力過大,響應延遲上升,嚴重時甚至可能導致數據庫崩潰。所以我們需要采取一些措施來保護數據庫,避免大量請求直接訪問數據庫。以下是一些常見的策略:

  1. 使用緩存:緩存是最常見、最直接的方法,可以大大減少數據庫的請求壓力。對于讀多寫少的場景尤其有效。不僅如此,合理使用緩存,還可以提高系統的響應速度。但是在使用緩存時要考慮BigKeyHotKey、緩存擊穿、緩存穿透、緩存雪崩以及數據一致性等眾多問題。HotKeyBigKey可以看本人之前的兩篇文章:
    HotKey:29.Go處理Redis HotKey
    BigKey: 30.Go處理Redis BigKey

  2. 限流:使用限流算法(如漏桶算法、令牌桶算法)來對訪問進行限制,保證系統在可接受的壓力范圍內運行,防止數據庫被過多的請求沖垮。
    限流:13. Go中常見限流算法示例代碼

  3. 熔斷機制:引入熔斷器,當偵測到某個服務的錯誤次數過多(如因數據庫連接問題導致的錯誤)時,熔斷該服務的所有請求,直到服務恢復。
    限流與熔斷:48.Go簡要實現令牌桶限流與熔斷器并集成到Gin框架中

  4. 負載均衡:如果有足夠的資源,可以采用負載均衡策略,如數據庫讀寫分離,主從復制等,將讀寫壓力分散到多個數據庫節點上。
    負載均衡:15. Go實現負載均衡算法

  5. 使用隊列:對于非實時性的數據請求,可以采用異步處理的方式,將請求先放入隊列中,然后通過隊列對數據庫請求進行削峰填谷,避免大量請求同時涌向數據庫。
    MQ:28.windows安裝kafka,Go操作kafka示例

  6. 數據庫優化:適當地對數據庫進行優化,如合理的索引、合理的表結構設計、SQL 函數優化等,都可以提高數據庫處理請求的能力。

以上就是一些避免數據庫在高并發下被大量請求直接訪問的策略,可以根據具體的場景和需求選擇適合的策略。

二、避免緩存擊穿

緩存擊穿問題也叫熱點Key問題,就是一個被高并發訪問并且緩存重建業務較復雜的Key突然失效了,無數的請求訪問會在瞬間給數據庫帶來巨大的沖擊,即瞬時的高并發擊穿了緩存去請求DB

緩存雪崩和緩存擊穿的區別在于緩存擊穿針對某一Key緩存,緩存雪崩則是很多Key。

由于緩存擊穿是某個熱點Key突然過期導致的,那么我們繼續把它加載進緩存就可以了,但是不能讓所有請求都去加載,只需要放行一個請求去加載,之后其他請求直接從緩存獲取,避免高并發流量打掛DB,通過互斥鎖即可實現。

互斥鎖:在讀取緩存的過程中,如果緩存未命中,則添加鎖并從數據庫中查詢。這樣可以避免在高并發下,大量的請求直接訪問數據庫。

注意:使用double check機制,此外,從DB查詢后,如果DB也不存在,應該緩存一個空對象,否則這些高并發請求會繼續請求DB。在設置空對象時,也可以設置一個較短的過期時間,避免長時間緩存空對象。

v, found := cache.Get(key)
if !found {lock.Lock()defer lock.Unlock()// double checkv, found = cache.Get(key)if !found {v, err := db.Get(key)if err != nil {// handle errorreturn nil, err}if v == nil {v = EmptyObject}cache.Set(key, v, cache.DefaultExpiration)}
}
return v, nil

二、避免緩存穿透

緩存穿透是指用戶不斷對一個緩存和數據庫都不存在的數據進行訪問,對于這種情況,由于既在緩存中查不到,也在數據庫中查不到,于是每次都會對數據庫進行一次查詢,造成數據庫壓力增大,這種請求有可能是一個惡意攻擊。

Go 語言中我們可以使用以下策略來避免緩存穿透:

緩存空對象:把空結果也進行緩存,當后續請求再次查詢時,即使查不到數據,也會在緩存中得到一個空結果,而不會再對數據庫進行查詢。同時,為了避免未來的查詢都返回空結果,需要對空結果設置一個較短的過期時間。

偽代碼如下:

v, found := cache.Get(key)
if found {return v
}v, err := db.Get(key)
if err != nil {// handle errorreturn nil, err
}if v == nil {v = EmptyObjectcache.Set(key, v, cache.DefaultExpiration)
}
return v, nil

使用布隆過濾器:布隆過濾器(Bloom filter)是一種用于測試一個元素是否在一個集合中的數據結構。由于它的存儲效率高且可以非常快速的查詢,所以常常被用來過濾掉一部分肯定不存在的數據,避免了對數據庫的無謂請求,在大數據量查找中有很高效率。

偽代碼如下:

if !bloomFilter.Exists(key) {return nil
}v, found := cache.Get(key)
if found {return v
}v, err := db.Get(key)
if err != nil {// handle errorreturn nil, err
}if v == nil {v = EmptyObject
}cache.Set(key, v, cache.DefaultExpiration)
return v, nil

在上面的示例中,如果布隆過濾器中不存在請求中的 key,則直接返回,不對數據庫做查詢。

上述兩種方法可以有效應對緩存穿透,能夠減輕數據庫的壓力,提升程序的響應速度。

三、避免緩存雪崩

緩存雪崩是指在緩存系統中,大量數據同時過期,在訪問頻率高的情況下可能會引起數據庫的過載。

在Go語言中,我們可以采用以下措施來避免緩存雪崩:

1、設置緩存失效時間的隨機性
使得每一個key的失效時間都是隨機的,防止所有緩存在同一時刻全部失效。例如,我們可以在原有的失效時間基礎上,增加一個隨機的延長時間。

expires := baseExpires + time.Duration(rand.Intn(randExpires)) * time.Second
cache.Set(key, value, expires)

2、使用數據版本控制
通過在每次緩存數據時將數據打上版本(可以是時間戳、或是遞增版本號等),每次訪問時,先訪問緩存,如果緩存不存在或者版本低于當前的版本,就更新緩存數據。這樣,即使緩存失效,也可以由單一線程去做更新,其它請求只需要等待即可。

func Get(key string, currentVersion int) (string, error) {v, version, found := cache.GetWithVersion(key)if found && version >= currentVersion {return v, nil}// Single flight to load from the database and put to the cache.// Usually done with a lock or using sync.Once type of logic.// ...return v, nil
}

3、熔斷機制和降級

在系統壓力過大或者服務不可用的情況下,可以進行熔斷降級,比如返回一些默認值,或者從備份緩存中讀數據。

整體來說,防止緩存雪崩主要是預防工作以及在系統異常時的快速應對。每一個解決方案都有其應用場景,需要根據具體業務情況進行選擇。

四、延遲雙刪保證數據一致性

使用到緩存,一般就需要考慮緩存與數據庫的一致性。如更新時,是先更新緩存還是先更新DB,或者是先刪除緩存還是先刪除DB、或者是否要通過監聽Binlog同步緩存,或者做一些其他旁路校驗等。方案很多,需要針對當前業務是否能夠容忍緩存與DB的不一致,以及容忍的程度如何來做具體涉設。但是最常用的還是延遲雙刪方案,成本低,容易實現,且能基本保障一致性。

延遲雙刪是解決緩存更新一致性問題的一個策略,具體策略如下:

  1. 先刪除緩存
  2. 再更新數據庫
  3. 最后延時刪除緩存

這樣做的目的是為了應對并發情況下緩存與數據庫數據不一致的問題。

Go語言實現延遲雙刪的一個簡單示例如下:

// 先刪除緩存
cache.Delete(key)// 更新數據庫
err := db.Update(key, value)
if err != nil {fmt.Printf("DB update error: %v", err)return
}// 延遲刪除緩存
time.AfterFunc(time.Duration(delayMillisecond)*time.Millisecond, func() {cache.Delete(key)
})

上述代碼的邏輯:

  • 首先,通過 cache.Delete(key) 刪除舊的緩存數據。
  • 然后,通過 db.Update(key, value) 更新數據庫數據。
  • 最后,使用 Gotime.AfterFunc 函數,實現一段時間后再次刪除緩存。時間可以依據實際業務情況設定。

這種方式可以在大部分場景下確保緩存和數據庫的數據一致,但還是存在極端情況下的問題,比如在第二次刪除緩存之前,有其他請求把舊的數據加載到了緩存。這種情況的出現幾率較小,如果業務對此有較高的要求,可能需要使用更嚴格的方案,如監聽Binlog同步緩存。

五、在使用 Go 的 time.AfterFunc 函數時,如果刪除緩存操作失敗怎么辦?

在使用time.AfterFunc時,如果刪除緩存操作失敗,最常見的處理辦法是進行重試操作。不過,在設置重試次數和重試延遲時,應謹慎考慮以防止無效操作導致系統資源的浪費。

func deleteCacheWithRetry(key string, retryTimes int, delay time.Duration) {for i := 0; i < retryTimes; i++ {// 嘗試刪除緩存err := cache.Delete(key)if err == nil {// 刪除成功return}// 如果刪除失敗,則等待一段時間再重試time.Sleep(delay)}// 在此處處理連續失敗的情況,例如記錄日志、發送告警等fmt.Printf("Failed to delete cache for key %s after %d attempts\n", key, retryTimes)
}// 在 time.AfterFunc 中使用
time.AfterFunc(time.Duration(delayMillisecond)*time.Millisecond, func() {deleteCacheWithRetry(key, 3, 1*time.Second)
})

在以上代碼中,我們定義了一個名為 deleteCacheWithRetry 的函數,它接受一個緩存鍵、重試次數和每次重試延遲的時間。它將嘗試刪除緩存,如果失敗,將等待一段時間后重試,直到達到最大重試次數。如果超過重試次數仍未成功,將通過日志記錄這個異常情況。
當然,具體的處理方式要根據項目具體需求和場景去判斷,以上只是一個參考示例。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/212369.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/212369.shtml
英文地址,請注明出處:http://en.pswp.cn/news/212369.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

vue自定義指令實現按鈕只允許點擊一次

vue自定義指令實現按鈕只允許點擊一次 vue自定義指令實現按鈕只允許點擊一次 這個例子中創建了一個名為 click-once 的自定義指令&#xff0c;通過 bind 鉤子函數給元素綁定了一個點擊事件&#xff0c;并且利用一個變量 clicked 控制了按鈕只能點擊一次的行為。在點擊后會執行傳…

【ITK庫學習】使用itk庫進行圖像濾波ImageFilter:Voting濾波器

目錄 1、itkVotingBinaryImageFilter2、itkVotingBinaryHoleFillingImageFilter 洞穴充填濾波器3、itkVotingBinaryIterativeHoleFillingImageFilter4、itkLabelVotingImageFilter 1、itkVotingBinaryImageFilter 該類是一個基類&#xff0c;用于根據前景和背景像素的鄰域投票…

【數據結構實踐課設】新生報道注冊管理信息系統

目錄 1.主要框架 2.寫入文件 3.讀取文件 4.注冊學生信息 5.增加學生信息 6.刪除學生信息 7.按姓名查詢 8.按班級查詢 9.按專業查詢 10.打印學生信息 11.完整代碼 &#x1f308;嗨&#xff01;我是Filotimo__&#x1f308;。很高興與大家相識&#xff0c;希望我的博客能對你有所…

git commit語義規范

合理的應當如 [header]fix(core): remove ....(#33949) These .... RP Close #33949(可選) Header可選 代碼類 新增功能(feat) 修復缺陷(fix) 改進性能(perf) 格式化代碼(style) 優化代碼(refactor) 非代碼類 更新測試代碼(test) 部署相關變更(ci) 文檔類變更(do…

【Linux】第二十七站:內存管理與文件頁緩沖區

文章目錄 一、物理內存和磁盤交換數據的最小單位二、操作系統如何管理內存三、文件的頁緩沖區四、基數樹or基數&#xff08;字典樹&#xff09;五、總結 一、物理內存和磁盤交換數據的最小單位 我們知道系統當中除了進程管理、文件管理以外&#xff0c;還有內存管理 內存的本質…

思科最新版Cisco Packet Tracer 8.2.1安裝

思科最新版Cisco Packet Tracer 8.2.1安裝 一. 注冊并登錄CISCO賬號二. 下載 Cisco Packet Tracer 8.2.1三. 安裝四. 漢化五. cisco packet tracer教學文檔六. 正常使用圖 前言 這是我在這個網站整理的筆記,有錯誤的地方請指出&#xff0c;關注我&#xff0c;接下來還會持續更新…

[香橙派]orange pi zero 3 燒錄Ubuntu系統鏡像——無需HDMI數據線安裝

一、前言 本文我們將介紹如何使用orange pi zero 3 安裝Ubuntu系統&#xff0c;本文相關步驟均參考自開發手冊。 二、實施準備 根據開發手冊中所提到的&#xff0c;我們應該擁有如下配件: 1.orange pi zero 3 開發板 2.TF 卡——最小 8GB 容量的 class10 級或以上的高速閃迪卡。…

鴻蒙OS應用開發之語句

在程序開發中&#xff0c;已經有上面的運算符和數據類型了&#xff0c;可以滿足了大部的需求&#xff0c;但是這些程序還是比較簡單的計算和邏輯運算&#xff0c;如果需要復雜的計算和邏輯處理&#xff0c;就需要采用復雜邏輯程序塊來處理了&#xff0c;也就是復雜條件語句才能…

nn.Sequential|nn.ModuleDict|nn.ModuleList 詳解

文章目錄 1、簡介2、三者之間的區別3、如何讓nn.ModuleList 和nn.ModuleDict實現推理3.1 方案1: 實現forward函數3.2 方案2: 將nn.ModuleList 和nn.ModuleDict轉換為nn.Sequential4、nn.ModuleDict、nn.ModuleList 的區別5、nn.ModuleList 、 nn.ModuleDict 與 Python list、…

模型 心流

本系列文章 主要是 分享模型&#xff0c;涉及各個領域&#xff0c;重在提升認知。完全投入其中。 1 心流的應用 1.1 優秀運動員的心流體驗 邁克爾喬丹&#xff08;Michael Jordan&#xff09;&#xff1a;籃球之神喬丹在比賽中經常進入心流狀態&#xff0c;他曾表示&#xff…

DIY手工藝作坊網站建設的作用如何

我國文化悠久流長&#xff0c;很多手工藝品制作技術放在如今依然有很高的需求度&#xff0c;加之現代新增的技藝&#xff0c;樣式多且藝術性強&#xff0c;比如常見的陶器手工制作技術&#xff0c;當然還有更多。 而對相關作坊來說&#xff0c;除了藝術傳承外&#xff0c;還需…

接觸剛性環境任務下的機器人力控(阻抗)性能測試

內涵 接觸剛性環境任務下的機器人力控&#xff08;阻抗&#xff09;性能測試旨在評估機器人在與剛性物體交互時的性能表現。這種測試通過調整機器人的控制參數&#xff0c;如期望剛度和期望阻尼等&#xff0c;并分析記錄的數據&#xff0c;旨在確保機器人能夠在執行任務時保持…

短劇分銷小程序/APP開發:開啟短劇收益時代

今年&#xff0c;短劇火爆出圈&#xff0c;市場規模將達至200億元至300億元。國內全全平臺付費短劇日充值金額為6000萬元&#xff0c;短劇作為一種“快餐式”文化迅速爆火。 短劇契合了觀眾娛樂時間碎片化的發展趨勢&#xff0c;相比于傳統的電視劇&#xff0c;短劇節奏快、劇…

Nacos源碼解讀10——配置中心的客戶端怎么處理服務端推送的配置信息變更

自動裝配 SpringBoot 自動裝配機制 加載 WEB/INF spring.factories org.springframework.cloud.bootstrap.BootstrapConfiguration\ com.alibaba.cloud.nacos.NacosConfigBootstrapConfigurationjava Configuration(proxyBeanMethods false) ConditionalOnProperty(name &q…

MongoDB的連接數據庫,創建、刪除數據庫,創建、刪除集合命令

本文主要介紹MongoDB的連接數據庫&#xff0c;創建、刪除數據庫&#xff0c;創建、刪除集合命令。 目錄 MongoDB連接數據庫連接到本地 MongoDB 實例連接到遠程 MongoDB 實例 MongoDB創建和刪除數據庫MongoDB創建和刪除集合創建集合刪除集合 MongoDB連接數據庫 連接 MongoDB 數…

P1317 低洼地題解

題目 一組數&#xff0c;分別表示地平線的高度變化。高度值為整數&#xff0c;相鄰高度用直線連接。找出并統計有多少個可能積水的低洼地&#xff1f; 如圖&#xff1a;地高變化為 [0,1,0,2,1,2,0,0,2,0]。 輸入輸出格式 輸入格式 兩行&#xff0c;第一行n, 表示有n個數。第…

Spark DataFrame和Dataset使用例子

文章目錄 1、基本操作1.1、創建SparkSession1.2、創建DataFrames1.3、創建Dataset操作1.4、運行sql查詢1.5、創建全局臨時視圖1.6、創建Datasets1.7、與rdd進行互操作1.7.1、使用反射推斷模式1.7.2、以編程方式指定模式 2、完整的測試例子 1、基本操作 1.1、創建SparkSession …

openGauss學習筆記-151 openGauss 數據庫運維-備份與恢復-物理備份與恢復之gs_basebackup

文章目錄 openGauss學習筆記-151 openGauss 數據庫運維-備份與恢復-物理備份與恢復之gs_basebackup151.1 背景信息151.2 前提條件151.3 語法151.4 示例151.5 從備份文件恢復數據 openGauss學習筆記-151 openGauss 數據庫運維-備份與恢復-物理備份與恢復之gs_basebackup 151.1 …

NeuralKG運行備忘

環境配置&#xff1a; conda create -n neuralkg python3.8 conda activate neuralkg pip install torch1.9.1cu111 -f https://download.pytorch.org/whl/torch_stable.html pip install dgl-cu111 dglgo -f https://data.dgl.ai/wheels/repo.html pip install neuralkg! co…

基于java swing 藥品銷售管理系統

大家好&#xff0c;我是DeBug&#xff0c;很高興你能來閱讀&#xff01;作為一名熱愛編程的程序員&#xff0c;我希望通過這些教學筆記與大家分享我的編程經驗和知識。在這里&#xff0c;我將會結合實際項目經驗&#xff0c;分享編程技巧、最佳實踐以及解決問題的方法。無論你是…