中文字幕av专区_日韩电影在线播放_精品国产精品久久一区免费式_av在线免费观看网站

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》
  • 首頁 > 
  • 教程 > 
  • 數據庫 > 
  • 深入淺出百億請求高可用Redis(codis)分布式集群揭秘

深入淺出百億請求高可用Redis(codis)分布式集群揭秘

發布時間:2020-08-11 09:29:24 來源:ITPUB博客 閱讀:225 作者:騰訊技術工程 欄目:數據庫

摘要:作為noSql中的kv數據庫的王者,redis以其高性能,低時延,豐富的數據結構備受開發者青睞,但是由于redis在水平伸縮性上受限,如何做到能夠水平擴容,同時對業務無侵入性是很多使用redis的開發人員都會面臨的問題,而redis分布式解決方案的一個開源產品【codis】較好的彌補了這一弱勢,本文主要講解codis是如何做到對業務無感知,平滑遷移,遷移性能高,遷移異常處理,高可用以及常見的redis的避坑指南,雖然codis目前隨著公司的nosql產品越來越成熟,生命周期也即將結束,不過鑒于還有很多同學對codis的原理比較感興趣,于是將以前的分享的內容重新整理,當然codis在公司外應用目前依舊還是相對比較廣泛。

目錄

一、背景 

二、Redis相關基礎概覽 

      2.1Redis簡介 

      2.2Redis的特點 

      2.3Redis應用場景 

三、Redis分布式解決方案公司內外比較 

四、Codis的架構設計 

      4.1 Codis整體的架構設計 

      4.2Codisproxy的架構設計實現 

五、數據可靠性 &高可用&容災&故障轉移&腦裂處理 

      5.1 數據可靠性

      5.2 高可用&容災&故障轉移 

六、codis水平擴容細節&遷移異常處理 

      6.1 Codis擴容遷移細節

      6.2 遷移異常處理 

七、Codis相關數據 

八、運維手冊及避坑指南 

九、參考資料 


一、背景

隨著直播元年開啟,越來越多的直播產品如春筍般出現,在拉動營收的過程中,產品竭盡全力思考著各種活動來刺激用戶的消費欲望,而這類活動的基礎形式就是榜單,在2016年我們基于cmem及掃描流水表的方式來實現榜單排名,2017開始,我們對原有系統進行重構,使用redis作為我們的榜單基礎存儲,在重構的過程中接到調研redis分布式解決方案的任務之后,比對業內各種開源產品,最后定下Codis,并對其中細節做了一些研究,期間在與Codis作者交流的過程中,有幸知道增值產品部的simotang已經在部門引入codis近2年時間,遂加入到codis的運維工作中,目前在部門內部署運維codis集群15套,2T容量,總日訪問量百億+.支撐了互動視頻產品部基礎存儲,運營活動,榜單類業務2年多,共計100多個活動,榜單上千個。同時在這里非常感謝codis作者spinlock在接入codis過程中給予的指導與幫助。見spinlock github 與 codis地址

二、Redis相關基礎概覽

2.1 Redis簡介

redis是一個基于內存同時具備數據持久化能力的高性能,低時延的KV數據庫,value的數據結構可以是string,hash表,list(列表),set(集合),sortedset(有序集合)。

Redis(RemoteDictionary Server)

Redis is anopen source (BSD licensed), in-memory data structure store, used as adatabase, cache and message broker. It supports data structures suchas strings, hashes, lists, sets, sorted sets with rangequeries,Practice: http://try.redis.io/


2.2 Redis的特點

1. 單線程異步架構(單線程,收包,發包,解析,執行,多路io復用接收文件事件)

2. k-v結構,value支持豐富的數據結構(string,hash,list,set,sortset)

3. 高性能,低時延,基于內存操作,Get/Set10w+,高性能,基于RDB、AOF落地保證數據可靠性

4. 豐富的特性,可用于緩存,消息隊列,TTL過期

5. 支持事務,操作是原子性,要么全部提交,要么全部不提交。

2.3 Redis應用場景

string

計數器,用戶信息(id)映射,唯一性(例如用戶資格判斷),bitmap

hash

常見場景:存儲對象的屬性信息(用戶資料)

list

常見場景:評論存儲,消息隊列

set

常見場景:資格判斷(例如用戶獎勵領取判斷),數據去重等

sorted set

常見場景:排行榜,延時隊列

其他

分布式鎖設計  推薦2篇文章:

基于Redis的分布式鎖到底安全嗎(上)

http://zhangtielei.com/posts/blog-redlock-reasoning.html

基于Redis的分布式鎖到底安全嗎(下)

http://zhangtielei.com/posts/blog-redlock-reasoning-part2.html

深入淺出百億請求高可用Redis(codis)分布式集群揭秘

【圖codis架構圖】

如上圖所示,codis整體屬于二層架構,proxy+存儲,相對于ckv+無proxy的設計來說整體設計會相對簡單,同時對于客戶端連接數據逐漸增大的情況下,也不用去做數據層的副本擴容,而只需要做proxy層的擴容,從這一點上看,成本會低一些,但是對于連接數不大的情況下,還需要單獨去部署proxy,從這一點上看,成本會高一些。

深入淺出百億請求高可用Redis(codis)分布式集群揭秘

其中,開源的codisproxy的服務的注冊發現是通過zk來實現,目前部門是基于l5來做.

從整體的架構設計圖來看,codis整體的架構比較清晰,其中codisproxy是分布式解決方案設計中最核心的部分,存儲路由,分片遷移均與codisproxy分不開,這塊我們來看一下codisproxy的設計實現。

4.2Codisproxy的架構設計實現

codisproxy的架構實現分成2個部分,分別為4.2.1的路由映射的細節與4.2.2的proxy請求處理的細節

4.2.1 路由映射細節

如下圖所示:該部分主要涉及到codis的路由細節,主要涉及到如何將一個key映射到具體的物理結點

深入淺出百億請求高可用Redis(codis)分布式集群揭秘

【圖】路由映射細節


如上圖所示:該部分主要涉及到codis的路由細節

| 相關詞匯說明

slot:分片信息,在redis當中僅僅表示一個數字,代表分片索引。每個分片會歸屬于具體的redis實例

group:主要是虛擬結點,由多臺redis機器組成,形成一主多從的模式,是邏輯意義上的結點

為了幫助大家對proxy路由映射的細節有一個更深入的理解,我整理了幾個常見的路由映射的相關問題來幫忙大家理解

問題一:proxy是如何把請求映射到具體的redis實例中?

Codis基于crc32的算法%1024得到對應的slot,slot就是所謂的邏輯分片,同時codis會將對應的邏輯分片映射到對應的虛擬結點上,每個虛擬結點是由1主多從的物理redis結點組成。至于為啥會用crc32,這個具體也沒有細究,作者也是借鑒于rediscluster中的實現引入的。通過引入邏輯存儲結點group,這樣即使底層的主機機器實例變更,也不映射上層的映射數據,對上層映射透明,便于分片的管理。

問題二,proxy是如何做到讀寫分離

如上圖所示,key映射到具體的虛擬結點時,能夠感知到虛擬結點對應的主與備機實例,此時redisproxy層面能夠識別到具體的redis命令得到對應的命令是讀與寫,再根據集群的配置是否支持讀寫分離的特性,如配置的是支持,則隨機路由到主與從機實例,如配置的是不支持,則路由到主機補全

問題三,proxy目前支持哪些命令,是否支持批量命令,如何保證原子性

命令支持鏈接

不支持命令

半支持命令

深入淺出百億請求高可用Redis(codis)分布式集群揭秘

深入淺出百億請求高可用Redis(codis)分布式集群揭秘

命令支持部分:Prxoy支持的命令分為三種:不支持命令,半支持命令,支持命令,除了上表所示命令外,其他命令proxy均是支持的,其中不支持命令部分主要是因為這些命令參數中沒有key,因此無法識別路由信息,不知道具體路由到哪臺實例上,而半支持命令部分通常是會操作多個key,codis基于一種簡單實現,以第一個key的路由為準,因此需要業務方自己來保持多個key路由到同一個slot,當然業務也是可以不保證,具體后果業務來承擔,是一種弱校驗的模式,而公司級產品ckv+對于多key操作是強校驗,如果多key不在同一slot上,則以錯誤的形式返回。

多key操作&原子性部分:Redis本身對于多key的一些操作例如mset等命令是原子性的,而在分布式操作下,多key會分布到多個redis實例當中,涉及到分布式事務,所以在codis當中進行了簡化處理,多key操作拆成多個單key命令操作,所以codis當中的mset多key操作不具備原子性的語義。

  問題四,如何保證多個key在一個slot當中

有些場景下,我們希望使用到lua或者一些半支持命令來保證我們操作的原子性,因此我們需要在業務層面來去保證多key在一個slot當中,codis采用了和rediscluster一樣的模式,基于hashtag,例如我想讓七天的主播榜單都中路由在同一個slot的話,{anchor_rank}day1,{anchor_rank}day2,{anchor_rank}day3,即可支持,對就是采用大括號的模式,codis會識別大括號,只會取大括號中的字符串進行hash操作。

4.2.2Proxy請求處理細節

如下圖所示:該部分主要涉及到proxy的處理細節,涉及到如何接受一個請求到響應回包的過程

深入淺出百億請求高可用Redis(codis)分布式集群揭秘

【圖】Proxy請求處理細節

如上圖所示:該部分主要涉及到proxy的處理細節

Codisproxy主要基于go語言這種從語言層面天然支持協程的語言來實現的

1)proxy接收客戶端的連接之后,新建一個session,同時啟動session中reader與writer兩個協程,reader主要用于接收客戶端請求數據并解析,對多key的場景下進行命令的拆分,然后將請求通過router進行分發到具體的redis實例,并將redis處理的數據結果寫到通道到中,writer從通道中接收對應的結果,將寫回給客戶端。

loop reader

loop writer

深入淺出百億請求高可用Redis(codis)分布式集群揭秘

深入淺出百億請求高可用Redis(codis)分布式集群揭秘

2)Router層主要是通過crc命令得到key對應的路由信息,從源碼可以看到hashtag的特性,codis其實也是支持的。

hash源碼

深入淺出百億請求高可用Redis(codis)分布式集群揭秘


至此,proxy相關的路由映射與請求處理細節已經結束,整體下來是不是很簡單

五、數據可靠性&高可用&容災&故障轉移&腦裂處理

作為存儲層,數據可靠性與服務高可用是穩定性的核心指標,直接影響到上層核心服務的穩定性,本節將主要針對這兩個指標來做一下闡述。

5.1 數據可靠性

作為codis的實現來講,數據高可靠主要是redis本身的能力,通常存儲層的數據高可靠,主要是單機數據高可靠+遠程數據熱備+定期冷備歸檔實現的

單機數據高可靠主要是借助于redis本身的持久化能力,rdb模式(定期dum)與aof模式(流水日志),這塊可以參考前文所示的2本書來了解,其中aof模式的安全性更高,目前我們線上也是將aof開關打開,在文末也會詳細描述一下。

遠程數據熱備主要是借助于redis自身具備主從同步的特性,全量同步與增量同步的實現,讓redis具體遠程熱備的能力

定期冷備歸檔由于存儲服務在運行的過程中可能存在人員誤操作數據,機房網絡故障,硬件問題導致數據丟失,因此我們需要一些兜底方案,目前主要是單機滾動備份備份最近48小時的數據以及sng的劉備系統來做冷備,以備非預期問題導致數據丟失,能夠快速恢復。

5.2 高可用&容災&故障轉移

codis的架構本身分成proxy集群+redis集群,proxy集群的高可用,可以基于zk或者l5來做故障轉移,而redis集群的高可用是借助于redis開源的哨兵集群來實現,那邊codis作為非redis組件,需要解決的一個問題就是如何集成redis哨兵集群。本節將該問題分成三部分,介紹redis哨兵集群如何保證redis高可用,codisproxy如何感知redis哨兵集群的故障轉移動作,redis集群如何降低“腦裂”的發生概率。

5.2.1 哨兵集群如何保證redis高可用

Sentinel(哨崗,哨兵)是Redis的高可用解決方案:由一個或多個Sentinel實例組成的Sentinel系統,可以監視任意多個主服務器,以及這些主服務器屬下的所有的從服務器,并在被監視的主服務器進入下線狀態時,自動將下線主服務器屬下的某個從服務器升級為新的主服務器,然后由主服務器代替已下線的主服務器繼續處理命令請求。

深入淺出百億請求高可用Redis(codis)分布式集群揭秘

通常來說要達到服務的高可用的效果需要做2個事情:故障探測與故障轉移(即選主并做主從切換)

故障

探測

Sentinel集群故障轉移

1)選出一臺Sentinel-leader,來進行故障轉移操作(raft協議,過半選舉)

if (winner && (max_votes < voters_quorum || max_votes < master->quorum))

2)領頭sentinel在已下線的從服務器里面,挑選一個從服務器,并將其轉換為主服務器

3)讓已下線主服務器屬下的所有從服務器改為復制新的主服務器

4)將已下線主服務器設置為新的主服務器的從服務器,當這個舊的主服務器重新上線時,它就會成為新的主服務器的從服務器

注:從服務器中挑選新的主服務器的步驟

1)選舉列表中剔除所有下線或者斷線狀態的從服務器

剔除所有最近五秒內沒有回復過領頭Sentinel的info命令的從服務器

剔除所有與已下線服務器超過down-after-millisenconds * 10(ms)的從服務器

2)根據從服務器優先級(取最高),復制偏移量(取最大),運行ID(取最小)1)每1秒,向主服務器,從服務器,其他sentinel實例發送ping命令

有效回復:+PONG, -Loading,+MASTERDOWN三種回復一種

無效回復:除以上三種回復之外的回復,或者在指定時限內沒有返回的回復

Sentinel.conf -> Sentinel down-master-millsenconds master 50000

(當連續50秒,sentinel都接收到無效請求或者無回復時,就會將master標記為主觀下線)

2)主觀下線之后,向其他sentinel發送詢問命令,如果達到配置中指定的數量時,則標記master為客觀下線

Sentinel monitor master xx.xx.xx.xx 2

故障

轉移

Sentinel集群故障轉移

1)選出一臺Sentinel-leader,來進行故障轉移操作(raft協議,過半選舉)

if (winner && (max_votes < voters_quorum || max_votes < master->quorum))

2)領頭sentinel在已下線的從服務器里面,挑選一個從服務器,并將其轉換為主服務器

3)讓已下線主服務器屬下的所有從服務器改為復制新的主服務器

4)將已下線主服務器設置為新的主服務器的從服務器,當這個舊的主服務器重新上線時,它就會成為新的主服務器的從服務器

注:從服務器中挑選新的主服務器的步驟

1)選舉列表中剔除所有下線或者斷線狀態的從服務器

剔除所有最近五秒內沒有回復過領頭Sentinel的info命令的從服務器

剔除所有與已下線服務器超過down-after-millisenconds * 10(ms)的從服務器

2)根據從服務器優先級(取最高),復制偏移量(取最大),運行ID(取最小)

5.2.2 codis如何感知哨兵集群的故障轉移動作

codis的架構本身分成proxy集群+redis集群,redis集群的高可用是由哨兵集群來保證的,那么proxy是如何感知redis主機故障,然后切換新主保證服務高可用的呢?

深入淺出百億請求高可用Redis(codis)分布式集群揭秘

如上圖所示,proxy本身會監聽sentinle集群的+switch-master事件,該事件發出,意味著redis集群主機出現問題,sentinel集群開始進行選舉并切換主機,proxy監聽了sentinel的主從切換事件,收到主從切換事件之后,proxy會做一個動作,就是把所有sentinel上的集群所感知的當前認為的主機拉取出來,選取過半sentinel認為的主機當作目前的集群主機。

講到這里,大家可能會忽略一個問題,就是配置存儲,配置中心的存儲還是舊的主機,一旦proxy重起,那拉取的依舊是故障的主機,其實dashboard和proxy也做了一樣的事情,收到主從切換事件之后,就會將新主持久化到storage中(目前為zk)

5.2.3 腦裂處理

腦裂(split-brain)集群的腦裂通常是發生在集群中部分節點之間不可達而引起的。如下述情況發生時,不同分裂的小集群會自主的選擇出master節點,造成原本的集群會同時存在多個master節點。,結果會導致系統混亂,數據損壞。

深入淺出百億請求高可用Redis(codis)分布式集群揭秘

在這個問題上,這里simotang同學已經講解的非常完善了,大規模codis集群的治理與實踐,這里簡單說一下,由于redis集群不能單純的依賴過半選舉的模式,因為redismaster自身沒有做檢測自身健康狀態而降級的動作,所以我們需要一種master健康狀態輔助判斷降級的方式。具體實現為

1)降級雙主出現的概率,讓Quorums判斷更加嚴格,讓主機下線判斷時間更加嚴格,我們部署了5臺sentinel機器覆蓋各大運營商IDC,只有4臺主觀認為主機下線的時候才做下線。

2)被隔離的master降級,基于共享資源判斷的方式,redis服務器上agent會定時持續檢測zk是否通常,若連接不上,則向redis發送降級指令,不可讀寫,犧牲可用性,保證一致性。

六、codis水平擴容細節&遷移異常處理

由于codis是針對redis分布式的解決方案,必然會面臨著redis單點容量不足的情況下水平擴容的問題,本節主要針對codis水平擴容與遷移異常的細節做一下說明,大家先帶著兩個問題來看,問題一,遷移過程中,正在遷移的key的讀寫請求怎么處理,問題二,遷移過程中的異常(例如失敗,超時)怎么處理。

6.1 Codis擴容遷移細節

深入淺出百億請求高可用Redis(codis)分布式集群揭秘

【圖】遷移流程

子進程開銷

監控與優化

不要和其他CPU密集型服務部署在一起,造成CPU過度競爭

如果部署多個Redis實例,盡量保證同一時刻只有一個子進程執行重寫工作

1G內存fork時間約20ms

內存

    背景:子進程通過fork操作產生,占用內存大小等同于父進程,理論上需要兩倍的內存來完成持久化操作,但Linux有寫時復制機制(copy-on-write)。父子進程會共享相同的物理內存頁,當父進程處理寫請求時會把要修改的頁創建副本,而子進程在fork操作過程中共享整個父進程內存快照。

Fork耗費的內存相關日志:AOF rewrite: 53 MB of memory used by copy-on-write,RDB: 5 MB of memory used by copy-on-write

    關閉巨頁,開啟之后,復制頁單位從原來4KB變為2MB,增加fork的負擔,會拖慢寫操作的執行時間,導致大量寫操作慢查詢

“sudo echo never>/sys/kernel/mm/transparent_hugepage/enabled

硬盤

    不要和其他高硬盤負載的服務部署在一起。如:存儲服務、消息隊列

8.6  AOF持久化細節

常用的同步硬盤的策略是everysec,用于平衡性能和數據安全性。對于這種方式,Redis使用另一條線程每秒執行fsync同步硬盤。當系統硬盤資源繁忙時,會造成Redis主線程阻塞。

深入淺出百億請求高可用Redis(codis)分布式集群揭秘

1)主線程負責寫入AOF緩沖區(源碼:flushAppendOnlyFile)

2)AOF線程負責每秒執行一次同步磁盤操作,并記錄最近一次同步時間。

3)主線程負責對比上次AOF同步時間:

如果距上次同步成功時間在2秒內,主線程直接返回。

如果距上次同步成功時間超過2秒,主線程將調用write(2)阻塞,直到同步操作完成

備注:打開AOF持久化功能后,Redis處理完每個事件后會調用write(2)將變化寫入kernel的buffer,如果此時write(2)被阻塞,Redis就不能處理下一個事件。Linux規定執行write(2)時,如果對同一個文件正在執行fdatasync(2)將kernel buffer寫入物理磁盤, write(2)會被Block住,整個Redis被Block住。

通過對AOF阻塞流程可以發現兩個問題:

1)everysec配置最多可能丟失2秒數據,不是1秒。

2)如果系統fsync緩慢,將會導致Redis主線程阻塞影響效率。

Redis提供了一個自救的方式,當發現文件有在執行fdatasync(2)時,就先不調用write(2),只存在cache里,免得被Block。但如果已經超過兩秒都還是這個樣子,則會硬著頭皮執行write(2),即使redis會被Block住。

Asynchronous AOF fsync is taking too long (disk is busy). Writing the AOF buffer,without waiting for fsync to complete, this may slow down Redis

8.7 不小心手抖執行了flushdb

如果配置appendonlyno,迅速調大rdb觸發參數,然后備份rdb文件,若備份失敗,趕緊跑路。配置了appedonlyyes, 辦法調大AOF重寫參數auto-aof-rewrite-percentage和auto-aof-rewrite-minsize,或者直接kill進程,讓Redis不能產生AOF自動重寫。·拒絕手動bgrewriteaof。備份aof文件,同時將備份的aof文件中寫入的flushdb命令干掉,然后還原。若還原不了,則依賴于冷備。

8.8 線上redis想將rdb模式換成aof模式

切不可,直接修改conf,重啟

正確方式:備份rdb文件,configset的方式打開aof,同時configrewrite寫回配置,執行bgrewriteof,內存數據備份至文件

九、參考資料

Redis開發與運維(付磊)

Redis設計與實踐(黃健宏)

大規模codis集群的治理與實踐

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

元氏县| 蒲江县| 观塘区| 普格县| 大渡口区| 沁阳市| 固镇县| 陵川县| 越西县| 淮安市| 贵德县| 新郑市| 西青区| 天台县| 汉中市| 西乌珠穆沁旗| 湘西| 清水河县| 富源县| 房山区| 鸡东县| 汕头市| 隆回县| 大方县| 奎屯市| 汉阴县| 康马县| 茂名市| 长泰县| 新沂市| 墨江| 曲麻莱县| 延津县| 洞头县| 新绛县| 陇南市| 开封市| 乌鲁木齐县| 威信县| 正蓝旗| 梁平县|