国产 无码 综合区,色欲AV无码国产永久播放,无码天堂亚洲国产AV,国产日韩欧美女同一区二区

【go項(xiàng)目-geecache】動(dòng)手寫分布式緩存 - day6 - 防止緩存擊穿

這篇具有很好參考價(jià)值的文章主要介紹了【go項(xiàng)目-geecache】動(dòng)手寫分布式緩存 - day6 - 防止緩存擊穿。希望對(duì)大家有所幫助。如果存在錯(cuò)誤或未考慮完全的地方,請(qǐng)大家不吝賜教,您也可以點(diǎn)擊"舉報(bào)違法"按鈕提交疑問(wèn)。

索引

【go項(xiàng)目-geecache】動(dòng)手寫分布式緩存 - day1 - 實(shí)現(xiàn)LRU算法
【go項(xiàng)目-geecache】動(dòng)手寫分布式緩存 - day2 - 單機(jī)并發(fā)緩存
【go項(xiàng)目-geecache】動(dòng)手寫分布式緩存 - day3 - HTTP 服務(wù)端
【go項(xiàng)目-geecache】動(dòng)手寫分布式緩存 - day4 - 一致性哈希(hash)
【go項(xiàng)目-geecache】動(dòng)手寫分布式緩存 - day5 - 分布式節(jié)點(diǎn)
【go項(xiàng)目-geecache】動(dòng)手寫分布式緩存 - day6 - 防止緩存擊穿
【go項(xiàng)目-geecache】動(dòng)手寫分布式緩存 - day7 - 使用 Protobuf 通信

收獲

  1. 了解緩存穿透,緩存雪崩, 緩存擊穿
  2. 知道怎么防止緩存擊穿
  3. 了解匿名函數(shù)的使用
  4. 加深了鎖的使用
  5. 了解空接口interface{}和模板的區(qū)別和作用

介紹緩存穿透,緩存雪崩, 緩存擊穿

概念可以看我這篇博客緩存穿透,緩存雪崩,緩存擊穿概念及解決方法
簡(jiǎn)單說(shuō)一下緩存擊穿,就是在緩存中找不到數(shù)據(jù),導(dǎo)致直接訪問(wèn)數(shù)據(jù)庫(kù),降低效率

為什么我們要防止緩存擊穿

當(dāng)前我們所做的項(xiàng)目對(duì)數(shù)據(jù)庫(kù)的訪問(wèn)沒(méi)有做任何限制的,所以當(dāng)客戶端發(fā)起大量請(qǐng)求,很容易導(dǎo)致緩存擊穿和穿透,所以我們需要減少重復(fù)請(qǐng)求

引入singleflight

singleflight是一種用于減少重復(fù)請(qǐng)求的技術(shù),它可以避免在高并發(fā)場(chǎng)景下出現(xiàn)重復(fù)的請(qǐng)求。
singleflight的實(shí)現(xiàn)方式是在請(qǐng)求前先檢查是否已經(jīng)有相同的請(qǐng)求正在處理,如果有,則等待該請(qǐng)求的處理結(jié)果并直接返回,避免重復(fù)發(fā)起請(qǐng)求。

實(shí)現(xiàn)singleflight數(shù)據(jù)結(jié)構(gòu)

package singleflight  
  
import "sync"  
  
type call struct {  
	wg  sync.WaitGroup  
	val interface{}  
	err error  
}  
  
type Group struct {  
	mu sync.Mutex       // protects m  
	m  map[string]*call  
}
  • call:表示正在進(jìn)行中,或已經(jīng)完成的函數(shù)調(diào)用,其中wg字段sync.WaitGroup 鎖避免沖突,val字段是一個(gè)空接口,err字段表示函數(shù)調(diào)用是否發(fā)生了錯(cuò)誤。
  • Group:表示一組正在進(jìn)行中的函數(shù)調(diào)用,其中mu字段是一個(gè)互斥鎖,用于保護(hù)m字段的讀寫,m字段是一個(gè)map,用于存儲(chǔ)正在進(jìn)行中的函數(shù)調(diào)用

這里我對(duì)sync.WaitGroupinterface{} 空接口解釋一下:

sync.WaitGroup

用于等待一組goroutine執(zhí)行完成。主要用于在主goroutine等待一組子goroutine執(zhí)行完畢后再繼續(xù)執(zhí)行的場(chǎng)景。

interface{} 空接口

我們知道接口時(shí)一組方法的集合,接口類型定義了一組方法,但沒(méi)有實(shí)現(xiàn)這些方法??战涌诶锩鏇](méi)有方法,意味著它可以表示任意類型的值,可以方便地實(shí)現(xiàn)通用性較強(qiáng)的函數(shù)或數(shù)據(jù)結(jié)構(gòu)文章來(lái)源地址http://www.zghlxwxcb.cn/news/detail-418845.html

空接口和模板的區(qū)別

  • 空接口的類型判斷需要通過(guò)斷言,而模板的數(shù)據(jù)類型是利用編譯器進(jìn)行類型推導(dǎo)來(lái)獲得的
  • 模板在使用時(shí)需要在編譯階段進(jìn)行類型檢查,因此可以保證類型安全性

實(shí)現(xiàn)singleflight的Do方法

func (g *Group) Do(key string, fn func() (interface{}, error)) (interface{}, error) {  
	g.mu.Lock()  
	if g.m == nil {  
		g.m = make(map[string]*call)  
	}  
	if c, ok := g.m[key]; ok {  
		g.mu.Unlock()  
		c.wg.Wait()  
		return c.val, c.err  
	}  
	c := new(call)  
	c.wg.Add(1)  
	g.m[key] = c  
	g.mu.Unlock()  
  
	c.val, c.err = fn()  
	c.wg.Done()  
  
	g.mu.Lock()  
	delete(g.m, key)  
	g.mu.Unlock()  
  
	return c.val, c.err  
}
  • Do() : 這個(gè)函數(shù)傳入key和函數(shù)fn,只要key相同,那么fn函數(shù)指揮運(yùn)行一次
  1. 首先mutex鎖,防止g.m并發(fā)讀寫沖突
  2. 檢查group的m成員變量是否存在,不存在則初始化
  3. 檢查key的請(qǐng)求是否正在執(zhí)行,如果正在執(zhí)行,則等待結(jié)果并解鎖,等待結(jié)果后返回結(jié)果
  4. 否則如果沒(méi)有在執(zhí)行,新建一個(gè)call類型表示正在執(zhí)行
  5. 發(fā)起請(qǐng)求前加鎖
  6. 將這個(gè)call加入到group的m(map)中,表示正在執(zhí)行
  7. 調(diào)用fn
  8. c.wg.Done() 表示請(qǐng)求結(jié)束
  9. group的m(map)中從刪除call

把singleflight加入主進(jìn)程group.go

type Group struct {  
	name      string  
	getter    Getter  
	mainCache cache  
	peers     PeerPicker  
	
	loader *singleflight.Group  
}  
  
func NewGroup(name string, cacheBytes int64, getter Getter) *Group {  
	g := &Group{  
		loader:    &singleflight.Group{},  
	}  
	return g  
}  
  
func (g *Group) load(key string) (value ByteView, err error) {  
	viewi, err := g.loader.Do(key, func() (interface{}, error) {  
		if g.peers != nil {  
			if peer, ok := g.peers.PickPeer(key); ok {  
				if value, err = g.getFromPeer(peer, key); err == nil {  
					return value, nil  
				}  
				log.Println("[GeeCache] Failed to get from peer", err)  
			}  
		}  
		return g.getLocally(key)  
	})  
	if err == nil {  
		return viewi.(ByteView), nil  
	}  
	return  
}
  • 添加Group結(jié)構(gòu)體loader,更新NewGroup
  • 修改load函數(shù),使用匿名函數(shù)保證對(duì)于同一個(gè)key只會(huì)執(zhí)行一次

測(cè)試

$ ./run.sh  
2020/02/16 22:36:00 [Server http://localhost:8003] Pick peer http://localhost:8001  
2020/02/16 22:36:00 [Server http://localhost:8001] GET /_geecache/scores/Tom  
2020/02/16 22:36:00 [SlowDB] search key Tom  
630630630

更新后的group.go

// 負(fù)責(zé)與外部交互,控制緩存存儲(chǔ)和獲取的主流程
package geecache

import (
	"fmt"
	"geecache/singleflight"
	"log"
	"sync"
)

type Group struct {
	name      string
	getter    Getter
	mainCache cache
	peers     PeerPicker

	loader *singleflight.Group
}

type Getter interface {
	Get(key string) ([]byte, error)
}

type GetterFunc func(key string) ([]byte, error)

func (f GetterFunc) Get(key string) ([]byte, error) {
	return f(key)
}

var (
	mu     sync.RWMutex
	groups = make(map[string]*Group)
)

func NewGroup(name string, cacheBytes int64, getter Getter) *Group {
	if getter == nil {
		panic("nil Getter")
	}
	mu.Lock()
	defer mu.Unlock()
	g := &Group{
		name:      name,
		getter:    getter,
		mainCache: cache{cacheBytes: cacheBytes},
		loader:    &singleflight.Group{},
	}
	groups[name] = g
	return g
}

func GetGroup(name string) *Group {
	mu.RLock()
	g := groups[name]
	mu.RUnlock()
	return g
}

func (g *Group) Get(key string) (ByteView, error) {
	if key == "" {
		return ByteView{}, fmt.Errorf("key is required")
	}

	if v, ok := g.mainCache.get(key); ok {
		log.Println("[GeeCache] hit")
		return v, nil
	}

	return g.load(key)
}

func (g *Group) getLocally(key string) (ByteView, error) {
	bytes, err := g.getter.Get(key)
	if err != nil {
		return ByteView{}, err

	}
	value := ByteView{b: cloneBytes(bytes)}
	g.populateCache(key, value)
	return value, nil
}

func (g *Group) populateCache(key string, value ByteView) {
	g.mainCache.add(key, value)
}
func (g *Group) RegisterPeers(peers PeerPicker) {
	if g.peers != nil {
		panic("RegisterPeerPicker called more than once")
	}
	g.peers = peers
}

func (g *Group) load(key string) (value ByteView, err error) {
	// each key is only fetched once (either locally or remotely)
	// regardless of the number of concurrent callers.
	viewi, err := g.loader.Do(key, func() (interface{}, error) {
		if g.peers != nil {
			if peer, ok := g.peers.PickPeer(key); ok {
				if value, err = g.getFromPeer(peer, key); err == nil {
					return value, nil
				}
				log.Println("[GeeCache] Failed to get from peer", err)
			}
		}

		return g.getLocally(key)
	})

	if err == nil {
		return viewi.(ByteView), nil
	}
	return
}

func (g *Group) getFromPeer(peer PeerGetter, key string) (ByteView, error) {
	bytes, err := peer.Get(g.name, key)
	if err != nil {
		return ByteView{}, err
	}
	return ByteView{b: bytes}, nil
}

到了這里,關(guān)于【go項(xiàng)目-geecache】動(dòng)手寫分布式緩存 - day6 - 防止緩存擊穿的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!

本文來(lái)自互聯(lián)網(wǎng)用戶投稿,該文觀點(diǎn)僅代表作者本人,不代表本站立場(chǎng)。本站僅提供信息存儲(chǔ)空間服務(wù),不擁有所有權(quán),不承擔(dān)相關(guān)法律責(zé)任。如若轉(zhuǎn)載,請(qǐng)注明出處: 如若內(nèi)容造成侵權(quán)/違法違規(guī)/事實(shí)不符,請(qǐng)點(diǎn)擊違法舉報(bào)進(jìn)行投訴反饋,一經(jīng)查實(shí),立即刪除!

領(lǐng)支付寶紅包贊助服務(wù)器費(fèi)用

相關(guān)文章

  • 一文拿捏分布式、分布式緩存及其問(wèn)題解決

    一文拿捏分布式、分布式緩存及其問(wèn)題解決

    1.集中式 傳統(tǒng)的計(jì)算模型通常是集中式的,所有的計(jì)算任務(wù)和數(shù)據(jù)處理都由 單一的計(jì)算機(jī)或服務(wù)器 完成。然而,隨著數(shù)據(jù)量和計(jì)算需求的增加,集中式系統(tǒng)可能會(huì)面臨性能瓶頸和可靠性問(wèn)題。 故而引出了分布式↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓

    2024年02月07日
    瀏覽(20)
  • 分布式系統(tǒng)架構(gòu)設(shè)計(jì)之分布式緩存技術(shù)選型

    分布式系統(tǒng)架構(gòu)設(shè)計(jì)之分布式緩存技術(shù)選型

    隨著互聯(lián)網(wǎng)業(yè)務(wù)的快速發(fā)展,分布式系統(tǒng)已經(jīng)成為了解決大規(guī)模并發(fā)請(qǐng)求、高可用性、可擴(kuò)展性等問(wèn)題的重要手段。在分布式系統(tǒng)中,緩存作為提高系統(tǒng)性能的關(guān)鍵技術(shù),能夠顯著降低數(shù)據(jù)庫(kù)負(fù)載、減少網(wǎng)絡(luò)延遲、提高數(shù)據(jù)訪問(wèn)速度。當(dāng)面對(duì)大量并發(fā)請(qǐng)求時(shí),如果每次都直接

    2024年02月03日
    瀏覽(519)
  • SpringBoot整合Redis、以及緩存穿透、緩存雪崩、緩存擊穿的理解分布式情況下如何添加分布式鎖 【續(xù)篇】

    SpringBoot整合Redis、以及緩存穿透、緩存雪崩、緩存擊穿的理解分布式情況下如何添加分布式鎖 【續(xù)篇】

    上一篇實(shí)現(xiàn)了單體應(yīng)用下如何上鎖,這一篇主要說(shuō)明如何在分布式場(chǎng)景下上鎖 上一篇地址:加鎖 需要注意的點(diǎn)是: 在上鎖和釋放鎖的過(guò)程中要保證 原子性操作 核心是上鎖和解鎖的過(guò)程 關(guān)于解鎖使用腳本參考:SET key value [EX seconds] [PX milliseconds] [NX|XX] 3.1 一個(gè)服務(wù)按照多個(gè)端口同時(shí)

    2023年04月10日
    瀏覽(29)
  • Redis 分布式緩存

    Redis 分布式緩存

    單點(diǎn) Redis 的問(wèn)題及解決 數(shù)據(jù)丟失:實(shí)現(xiàn)Redis數(shù)據(jù)持久化 并發(fā)能力:搭建主從集群,實(shí)現(xiàn)讀寫分離 存儲(chǔ)能力:搭建分片集群,利用插槽機(jī)制實(shí)現(xiàn)動(dòng)態(tài)擴(kuò)容 故障恢復(fù)能力:利用哨兵機(jī)制,實(shí)現(xiàn)健康檢測(cè)和自動(dòng)恢復(fù) RDB RDB全稱Redis Database Backup file (Redis數(shù)據(jù)備份文件),也被叫做

    2024年02月10日
    瀏覽(24)
  • Redis分布式緩存

    Redis分布式緩存

    -- 基于Redis集群解決單機(jī)Redis存在的問(wèn)題 單機(jī)的Redis存在四大問(wèn)題: Redis有兩種持久化方案: RDB持久化 AOF持久化 ? ? ? ?RDB全稱Redis Database Backup file(Redis數(shù)據(jù)備份文件),也被叫做 Redis數(shù)據(jù)快照 。簡(jiǎn)單來(lái)說(shuō)就是把 內(nèi)存中的所有數(shù)據(jù)都記錄到磁盤 中。當(dāng)Redis實(shí)例故障重啟后,

    2024年02月12日
    瀏覽(19)
  • 分布式緩存

    – 基于Redis集群解決單機(jī)Redis存在的問(wèn)題 Redis有兩種持久化方案: RDB持久化 AOF持久化 RDB全稱Redis Database Backup file(Redis數(shù)據(jù)備份文件),也被叫做Redis數(shù)據(jù)快照。簡(jiǎn)單來(lái)說(shuō)就是 把內(nèi)存中的所有數(shù)據(jù) 都記錄到磁盤中。當(dāng)Redis實(shí)例故障重啟后,從磁盤讀取快照文件,恢復(fù)數(shù)據(jù)???/p>

    2023年04月25日
    瀏覽(20)
  • Redis(分布式緩存詳解)

    Redis(分布式緩存詳解)

    Redis:基于內(nèi)存的鍵值存儲(chǔ)系統(tǒng),通常用作高性能的數(shù)據(jù)庫(kù)、緩存和消息隊(duì)列代理,是互聯(lián)網(wǎng)廣泛應(yīng)用的存儲(chǔ)中間件 特點(diǎn) :基于內(nèi)存存儲(chǔ),讀寫性能高 Redis與MySQL區(qū)別 Redis以鍵值對(duì)形式存儲(chǔ),MySQL以表格形式存儲(chǔ) Redis存儲(chǔ)在 內(nèi)存 ,MySQL存儲(chǔ)在 磁盤 Redis存儲(chǔ) 高效 ,MySQL存儲(chǔ) 安

    2024年02月16日
    瀏覽(30)
  • Redis高級(jí)-分布式緩存

    – 基于Redis集群解決單機(jī)Redis存在的問(wèn)題 單機(jī)的Redis存在四大問(wèn)題: Redis有兩種持久化方案: RDB持久化 AOF持久化 RDB全稱Redis Database Backup file(Redis數(shù)據(jù)備份文件),也被叫做Redis數(shù)據(jù)快照。簡(jiǎn)單來(lái)說(shuō)就是把內(nèi)存中的所有數(shù)據(jù)都記錄到磁盤中。當(dāng)Redis實(shí)例故障重啟后,從磁盤讀取

    2024年04月16日
    瀏覽(26)
  • Redis分布式緩存方案

    Redis分布式緩存方案

    數(shù)據(jù)丟失:數(shù)據(jù)持久化 并發(fā)能力弱:搭建主從集群,實(shí)現(xiàn)讀寫分離 故障恢復(fù)問(wèn)題:哨兵實(shí)現(xiàn)健康檢測(cè),自動(dòng)恢復(fù) 存儲(chǔ)能力:搭建分片集群,利用插槽機(jī)制實(shí)現(xiàn)動(dòng)態(tài)擴(kuò)容 RDB持久化 數(shù)據(jù)庫(kù)備份文件,也叫快照,把內(nèi)存數(shù)據(jù)存到磁盤。使用save進(jìn)行主動(dòng)RDB,會(huì)阻塞所有命令。建議

    2023年04月25日
    瀏覽(17)
  • 緩存和分布式鎖筆記

    緩存 開(kāi)發(fā)中,凡是放入緩存中的數(shù)據(jù)都應(yīng)該指定過(guò)期時(shí)間,使其可以在系統(tǒng)即使沒(méi)有主動(dòng)更新數(shù)據(jù)也能自動(dòng)觸發(fā)數(shù)據(jù)加載進(jìn)緩存的流程。避免業(yè)務(wù)崩潰導(dǎo)致的數(shù)據(jù)永久不一致 問(wèn)題。 redis作為緩存使用redisTemplate操作redis 分布式鎖的原理和使用 分布式加鎖:本地鎖,只能鎖住當(dāng)

    2024年02月09日
    瀏覽(23)

覺(jué)得文章有用就打賞一下文章作者

支付寶掃一掃打賞

博客贊助

微信掃一掃打賞

請(qǐng)作者喝杯咖啡吧~博客贊助

支付寶掃一掃領(lǐng)取紅包,優(yōu)惠每天領(lǐng)

二維碼1

領(lǐng)取紅包

二維碼2

領(lǐng)紅包