亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

go zero微服務(wù)高在請(qǐng)求量下如何優(yōu)化

 更新時(shí)間:2022年07月05日 16:51:58   作者:kevinwan  
這篇文章主要為大家介紹了go zero微服務(wù)高在請(qǐng)求量下的優(yōu)化處理,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪

引言

前兩篇文章我們介紹了緩存使用的各種最佳實(shí)踐,首先介紹了緩存使用的基本姿勢(shì),分別是如何利用go-zero自動(dòng)生成的緩存和邏輯代碼中緩存代碼如何寫(xiě),接著講解了在面對(duì)緩存的穿透、擊穿、雪崩等常見(jiàn)問(wèn)題時(shí)的解決方案,最后還重點(diǎn)講解了如何保證緩存的一致性。

因?yàn)榫彺鎸?duì)于高并發(fā)服務(wù)來(lái)說(shuō)實(shí)在是太重要了,所以這篇文章我們還會(huì)繼續(xù)一起學(xué)習(xí)下緩存相關(guān)的知識(shí)。

本地緩存

當(dāng)我們遇到極端熱點(diǎn)數(shù)據(jù)查詢(xún)的時(shí)候,這個(gè)時(shí)候就要考慮本地緩存了。熱點(diǎn)本地緩存主要部署在應(yīng)用服務(wù)器的代碼中,用于阻擋熱點(diǎn)查詢(xún)對(duì)于Redis等分布式緩存或者數(shù)據(jù)庫(kù)的壓力。

在我們的商城中,首頁(yè)Banner中會(huì)放一些廣告商品或者推薦商品,這些商品的信息由運(yùn)營(yíng)在管理后臺(tái)錄入和變更。這些商品的請(qǐng)求量非常大,即使是Redis也很難扛住,所以這里我們可以使用本地緩存來(lái)進(jìn)行優(yōu)化。

在product庫(kù)中先建一張商品運(yùn)營(yíng)表product_operation,為了簡(jiǎn)化只保留必要字段,product_id為推廣運(yùn)營(yíng)的商品id,status為運(yùn)營(yíng)商品的狀態(tài),status為1的時(shí)候會(huì)在首頁(yè)Banner中展示該商品。

CREATE TABLE `product_operation` (
  `id` bigint unsigned NOT NULL AUTO_INCREMENT,
  `product_id` bigint unsigned NOT NULL DEFAULT 0 COMMENT '商品id',
  `status` int NOT NULL DEFAULT '1' COMMENT '運(yùn)營(yíng)商品狀態(tài) 0-下線(xiàn) 1-上線(xiàn)',
  `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '創(chuàng)建時(shí)間',
  `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新時(shí)間',
  PRIMARY KEY (`id`),
  KEY `ix_update_time` (`update_time`)
) ENGINE=InnoDB  DEFAULT CHARSET=utf8mb4 COMMENT='商品運(yùn)營(yíng)表';

本地緩存的實(shí)現(xiàn)比較簡(jiǎn)單,我們可以使用map來(lái)自己實(shí)現(xiàn),在go-zero的collection中提供了Cache來(lái)實(shí)現(xiàn)本地緩存的功能,我們直接拿來(lái)用,重復(fù)造輪子從來(lái)不是一個(gè)明智的選擇,localCacheExpire為本地緩存過(guò)期時(shí)間,Cache提供了Get和Set方法,使用非常簡(jiǎn)單

localCache, err := collection.NewCache(localCacheExpire)

先從本地緩存中查找,如果命中緩存則直接返回。沒(méi)有命中緩存的話(huà)需要先從數(shù)據(jù)庫(kù)中查詢(xún)運(yùn)營(yíng)位商品id,然后再聚合商品信息,最后回塞到本地緩存中。詳細(xì)代碼邏輯如下:

func (l *OperationProductsLogic) OperationProducts(in *product.OperationProductsRequest) (*product.OperationProductsResponse, error) {
  opProducts, ok := l.svcCtx.LocalCache.Get(operationProductsKey)
  if ok {
    return &product.OperationProductsResponse{Products: opProducts.([]*product.ProductItem)}, nil
  }
  pos, err := l.svcCtx.OperationModel.OperationProducts(l.ctx, validStatus)
  if err != nil {
    return nil, err
  }
  var pids []int64
  for _, p := range pos {
    pids = append(pids, p.ProductId)
  }
  products, err := l.productListLogic.productsByIds(l.ctx, pids)
  if err != nil {
    return nil, err
  }
  var pItems []*product.ProductItem
  for _, p := range products {
    pItems = append(pItems, &product.ProductItem{
      ProductId: p.Id,
      Name:      p.Name,
    })
  }
  l.svcCtx.LocalCache.Set(operationProductsKey, pItems)
  return &product.OperationProductsResponse{Products: pItems}, nil
}

使用grpurl調(diào)試工具請(qǐng)求接口,第一次請(qǐng)求cache miss后,后面的請(qǐng)求都會(huì)命中本地緩存,等到本地緩存過(guò)期后又會(huì)重新回源db加載數(shù)據(jù)到本地緩存中

~ grpcurl -plaintext -d '{}' 127.0.0.1:8081 product.Product.OperationProducts
{
  "products": [
    {
      "productId": "32",
      "name": "電風(fēng)扇6"
    },
    {
      "productId": "31",
      "name": "電風(fēng)扇5"
    },
    {
      "productId": "33",
      "name": "電風(fēng)扇7"
    }
  ]
}

注意,并不是所有信息都適用于本地緩存,本地緩存的特點(diǎn)是請(qǐng)求量超高,同時(shí)業(yè)務(wù)上能夠允許一定的不一致,因?yàn)楸镜鼐彺嬉话悴粫?huì)主動(dòng)做更新操作,需要等到過(guò)期后重新回源db后再更新。所以在業(yè)務(wù)中要視情況而定看是否需要使用本地緩存。

自動(dòng)識(shí)別熱點(diǎn)數(shù)據(jù)

首頁(yè)Banner場(chǎng)景是由運(yùn)營(yíng)人員來(lái)配置的,也就是我們能提前知道可能產(chǎn)生的熱點(diǎn)數(shù)據(jù),但有些情況我們是不能提前預(yù)知數(shù)據(jù)會(huì)成為熱點(diǎn)的。

所以就需要我們能自適應(yīng)地自動(dòng)的識(shí)別這些熱點(diǎn)數(shù)據(jù),然后把這些數(shù)據(jù)提升為本地緩存。

我們維護(hù)一個(gè)滑動(dòng)窗口,比如滑動(dòng)窗口設(shè)置為10s,就是要統(tǒng)計(jì)這10s內(nèi)有哪些key被高頻訪問(wèn),一個(gè)滑動(dòng)窗口中對(duì)應(yīng)多個(gè)Bucket,每個(gè)Bucket中對(duì)應(yīng)一個(gè)map,map的key為商品的id,value為商品對(duì)應(yīng)的請(qǐng)求次數(shù)。

接著我們可以定時(shí)的(比如10s)去統(tǒng)計(jì)當(dāng)前所有Buckets中的key的數(shù)據(jù),然后把這些數(shù)據(jù)導(dǎo)入到大頂堆中,輕而易舉的可以從大頂堆中獲取topK的key,我們可以設(shè)置一個(gè)閾值,比如在一個(gè)滑動(dòng)窗口時(shí)間內(nèi)某一個(gè)key訪問(wèn)頻次超過(guò)500次,就認(rèn)為該key為熱點(diǎn)key,從而自動(dòng)地把該key升級(jí)為本地緩存。

緩存使用技巧

下面介紹一些緩存使用的小技巧

  • key的命名要盡量易讀,即見(jiàn)名知意,在易讀的前提下長(zhǎng)度要盡可能的小,以減少資源的占用,對(duì)于value來(lái)說(shuō)可以用int就盡量不要用string,對(duì)于小于N的value,redis內(nèi)部有shared_object緩存。
  • 在redis使用hash的情況下進(jìn)行key的拆分,同一個(gè)hash key會(huì)落到同一個(gè)redis節(jié)點(diǎn),hash過(guò)大的情況下會(huì)導(dǎo)致內(nèi)存以及請(qǐng)求分布的不均勻,考慮對(duì)hash進(jìn)行拆分為小的hash,使得節(jié)點(diǎn)內(nèi)存均勻避免單節(jié)點(diǎn)請(qǐng)求熱點(diǎn)。
  • 為了避免不存在的數(shù)據(jù)請(qǐng)求,導(dǎo)致每次請(qǐng)求都緩存miss直接打到數(shù)據(jù)庫(kù)中,進(jìn)行空緩存的設(shè)置。
  • 緩存中需要存對(duì)象的時(shí)候,序列化盡量使用protobuf,盡可能減少數(shù)據(jù)大小。
  • 新增數(shù)據(jù)的時(shí)候要保證緩存務(wù)必存在的情況下再去操作新增,使用Expire來(lái)判斷緩存是否存在。
  • 對(duì)于存儲(chǔ)每日登錄場(chǎng)景的需求,可以使用BITSET,為了避免單個(gè)BITSET過(guò)大或者熱點(diǎn),可以進(jìn)行sharding。
  • 在使用sorted set的時(shí)候,避免使用zrange或者zrevrange返回過(guò)大的集合,復(fù)雜度較高。
  • 在進(jìn)行緩存操作的時(shí)候盡量使用PIPELINE,但也要注意避免集合過(guò)大。
  • 避免超大的value。
  • 緩存盡量要設(shè)置過(guò)期時(shí)間。
  • 慎用全量操作命令,比如Hash類(lèi)型的HGETALL、Set類(lèi)型的SMEMBERS等,這些操作會(huì)對(duì)Hash和Set的底層數(shù)據(jù)結(jié)構(gòu)進(jìn)行全量掃描,如果數(shù)據(jù)量較多的話(huà),會(huì)阻塞Redis主線(xiàn)程。
  • 獲取集合類(lèi)型的全量數(shù)據(jù)可以使用SSCAN、HSCAN等命令分批返回集合中的數(shù)據(jù),減少對(duì)主線(xiàn)程的阻塞。
  • 慎用MONITOR命令,MONITOR命令會(huì)把監(jiān)控到的內(nèi)容持續(xù)寫(xiě)入輸出緩沖區(qū),如果線(xiàn)上命令操作很多,輸出緩沖區(qū)很快就會(huì)溢出,會(huì)對(duì)Redis性能造成影響。
  • 生產(chǎn)環(huán)境禁用KEYS、FLUSHALL、FLUSHDB等命令。

結(jié)束語(yǔ)

已知的熱點(diǎn)緩存比較簡(jiǎn)單,從數(shù)據(jù)庫(kù)中提前加載到內(nèi)存中即可,未知的熱點(diǎn)緩存我們需要自適應(yīng)的識(shí)別出熱點(diǎn)的數(shù)據(jù),然后把這些熱點(diǎn)的數(shù)據(jù)升級(jí)為本地緩存。最后介紹了一些實(shí)際生產(chǎn)中緩存使用的一些小技巧,在生產(chǎn)環(huán)境中要活靈活用盡量避免問(wèn)題的產(chǎn)生。

代碼倉(cāng)庫(kù): https://github.com/zhoushuguang/lebron

項(xiàng)目地址: https://github.com/zeromicro/go-zero

本篇文章介紹了如何使用本地?zé)狳c(diǎn)緩存應(yīng)對(duì)超高的請(qǐng)求,熱點(diǎn)緩存又分為已知的熱點(diǎn)緩存和未知的熱點(diǎn)緩存,希望大家以后多多支持腳本之家!

相關(guān)文章

  • Go?編程復(fù)雜數(shù)據(jù)類(lèi)型?Map

    Go?編程復(fù)雜數(shù)據(jù)類(lèi)型?Map

    這篇文章主要介紹了Go編程復(fù)雜數(shù)據(jù)類(lèi)型Map,Go中的Map是一組無(wú)需的K-V類(lèi)型的數(shù)據(jù),與Python中的字典Dict和Java中的HashMap結(jié)構(gòu)類(lèi)似。未被初始化的Map為nil
    2022-08-08
  • Go語(yǔ)言入門(mén)教程之基礎(chǔ)語(yǔ)法快速入門(mén)

    Go語(yǔ)言入門(mén)教程之基礎(chǔ)語(yǔ)法快速入門(mén)

    這篇文章主要介紹了Go語(yǔ)言入門(mén)教程之基礎(chǔ)語(yǔ)法快速入門(mén),本文講解了值類(lèi)型、變量、常量、循環(huán)、條件語(yǔ)句、條件枚舉等內(nèi)容,需要的朋友可以參考下
    2014-11-11
  • Go語(yǔ)言關(guān)于幾種深度拷貝(deepcopy)方法的性能對(duì)比

    Go語(yǔ)言關(guān)于幾種深度拷貝(deepcopy)方法的性能對(duì)比

    這篇文章主要介紹了Go語(yǔ)言關(guān)于幾種深度拷貝(deepcopy)方法的性能對(duì)比,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2024-01-01
  • Golang?Gin解析JSON請(qǐng)求數(shù)據(jù)避免出現(xiàn)EOF錯(cuò)誤

    Golang?Gin解析JSON請(qǐng)求數(shù)據(jù)避免出現(xiàn)EOF錯(cuò)誤

    這篇文章主要為大家介紹了Golang?Gin?優(yōu)雅地解析JSON請(qǐng)求數(shù)據(jù),避免ShouldBindBodyWith出現(xiàn)EOF錯(cuò)誤的源碼分析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-04-04
  • 深入理解Golang之http server的實(shí)現(xiàn)

    深入理解Golang之http server的實(shí)現(xiàn)

    這篇文章主要介紹了深入理解Golang之http server的實(shí)現(xiàn),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2019-11-11
  • 源碼分析Go語(yǔ)言中g(shù)ofmt實(shí)現(xiàn)原理

    源碼分析Go語(yǔ)言中g(shù)ofmt實(shí)現(xiàn)原理

    gofmt?是?Go?語(yǔ)言官方提供的一個(gè)工具,用于自動(dòng)格式化?Go?源代碼,使其符合?Go?語(yǔ)言的官方編碼風(fēng)格,本文給大家源碼詳細(xì)分析了Go語(yǔ)言中g(shù)ofmt實(shí)現(xiàn)原理,并通過(guò)圖文和代碼講解的非常詳細(xì),需要的朋友可以參考下
    2024-03-03
  • golang并發(fā)下載多個(gè)文件的方法

    golang并發(fā)下載多個(gè)文件的方法

    今天小編就為大家分享一篇golang并發(fā)下載多個(gè)文件的方法,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧
    2019-07-07
  • Golang實(shí)現(xiàn)對(duì)map的并發(fā)讀寫(xiě)的方法示例

    Golang實(shí)現(xiàn)對(duì)map的并發(fā)讀寫(xiě)的方法示例

    這篇文章主要介紹了Golang實(shí)現(xiàn)對(duì)map的并發(fā)讀寫(xiě)的方法示例,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    2019-03-03
  • go如何利用orm簡(jiǎn)單實(shí)現(xiàn)接口分布式鎖

    go如何利用orm簡(jiǎn)單實(shí)現(xiàn)接口分布式鎖

    本篇文章主要介紹了go如何利用orm簡(jiǎn)單實(shí)現(xiàn)接口分布式鎖,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    2018-07-07
  • Go學(xué)習(xí)筆記之map的聲明和初始化

    Go學(xué)習(xí)筆記之map的聲明和初始化

    map底層是由哈希表實(shí)現(xiàn)的,Go使用鏈地址法來(lái)解決鍵沖突,下面這篇文章主要給大家介紹了關(guān)于Go學(xué)習(xí)筆記之map的聲明和初始化的相關(guān)資料,需要的朋友可以參考下
    2022-11-11

最新評(píng)論