redis面試必問(下)

接上一章redis面試必問(上)

7.redis 集群模式的工作原理能說一下么丸边?在集群模式下吕漂,redis 的 key 是如何尋址的背镇?分布式尋址都有哪些算法殿较?了解一致性 hash 算法嗎规婆?

考點(diǎn)分析

在前幾年澜躺,redis 如果要搞幾個節(jié)點(diǎn),每個節(jié)點(diǎn)存儲一部分的數(shù)據(jù)抒蚜,得借助一些中間件來實(shí)現(xiàn)掘鄙,比如說有 codis,或者 twemproxy嗡髓,都有操漠。有一些 redis 中間件,你讀寫 redis 中間件饿这,redis 中間件負(fù)責(zé)將你的數(shù)據(jù)分布式存儲在多臺機(jī)器上的 redis 實(shí)例中浊伙。

這兩年,redis 不斷在發(fā)展长捧,redis 也不斷的有新的版本嚣鄙,現(xiàn)在的 redis 集群模式,可以做到在多臺機(jī)器上串结,部署多個 redis 實(shí)例哑子,每個實(shí)例存儲一部分的數(shù)據(jù),同時每個 redis 實(shí)例可以掛 redis 從實(shí)例肌割,自動確保說卧蜓,如果 redis 主實(shí)例掛了,會自動切換到 redis 從實(shí)例頂上來声功。

現(xiàn)在 redis 的新版本烦却,大家都是用 redis cluster 的,也就是 redis 原生支持的 redis 集群模式先巴,那么面試官肯定會就 redis cluster 對你來個幾連炮其爵。要是你沒用過 redis cluster冒冬,正常,以前很多人用 codis 之類的客戶端來支持集群摩渺,但是起碼你得研究一下 redis cluster 吧简烤。

如果你的數(shù)據(jù)量很少,主要是承載高并發(fā)高性能的場景摇幻,比如你的緩存一般就幾個 G横侦,單機(jī)就足夠了,可以使用 replication绰姻,一個 master 多個 slaves枉侧,要幾個 slave 跟你要求的讀吞吐量有關(guān),然后自己搭建一個 sentinel 集群去保證 redis 主從架構(gòu)的高可用性狂芋。

redis cluster榨馁,主要是針對海量數(shù)據(jù)+高并發(fā)+高可用的場景。redis cluster 支撐 N 個 redis master node帜矾,每個 master node 都可以掛載多個 slave node翼虫。這樣整個 redis 就可以橫向擴(kuò)容了。如果你要支撐更大數(shù)據(jù)量的緩存屡萤,那就橫向擴(kuò)容更多的 master 節(jié)點(diǎn)珍剑,每個 master 節(jié)點(diǎn)就能存放更多的數(shù)據(jù)了。

面試題剖析

redis cluster 介紹

  • 自動將數(shù)據(jù)進(jìn)行分片死陆,每個 master 上放一部分?jǐn)?shù)據(jù)
  • 提供內(nèi)置的高可用支持招拙,部分 master 不可用時,還是可以繼續(xù)工作的

在 redis cluster 架構(gòu)下翔曲,每個 redis 要放開兩個端口號迫像,比如一個是 6379劈愚,另外一個就是 加1w 的端口號瞳遍,比如 16379。

16379 端口號是用來進(jìn)行節(jié)點(diǎn)間通信的菌羽,也就是 cluster bus 的東西掠械,cluster bus 的通信,用來進(jìn)行故障檢測注祖、配置更新猾蒂、故障轉(zhuǎn)移授權(quán)。cluster bus 用了另外一種二進(jìn)制的協(xié)議是晨,gossip 協(xié)議肚菠,用于節(jié)點(diǎn)間進(jìn)行高效的數(shù)據(jù)交換,占用更少的網(wǎng)絡(luò)帶寬和處理時間罩缴。

節(jié)點(diǎn)間的內(nèi)部通信機(jī)制

基本通信原理

  • redis cluster 節(jié)點(diǎn)間采用 gossip 協(xié)議進(jìn)行通信
    集中式是將集群元數(shù)據(jù)(節(jié)點(diǎn)信息蚊逢、故障等等)幾種存儲在某個節(jié)點(diǎn)上雌隅。集中式元數(shù)據(jù)集中存儲的一個典型代表也榄,就是大數(shù)據(jù)領(lǐng)域的 storm。它是分布式的大數(shù)據(jù)實(shí)時計算引擎,是集中式的元數(shù)據(jù)存儲的結(jié)構(gòu)蹦渣,底層基于 zookeeper(分布式協(xié)調(diào)的中間件)對所有元數(shù)據(jù)進(jìn)行存儲維護(hù)。
zookeeper-centralized-storage

redis 維護(hù)集群元數(shù)據(jù)采用另一個方式纪他, gossip 協(xié)議蛛倦,所有節(jié)點(diǎn)都持有一份元數(shù)據(jù),不同的節(jié)點(diǎn)如果出現(xiàn)了元數(shù)據(jù)的變更昼伴,就不斷將元數(shù)據(jù)發(fā)送給其它的節(jié)點(diǎn)匾旭,讓其它節(jié)點(diǎn)也進(jìn)行元數(shù)據(jù)的變更。

redis-gossip

集中式好處在于圃郊,元數(shù)據(jù)的讀取和更新季率,時效性非常好,一旦元數(shù)據(jù)出現(xiàn)了變更描沟,就立即更新到集中式的存儲中飒泻,其它節(jié)點(diǎn)讀取的時候就可以感知到;不好在于吏廉,所有的元數(shù)據(jù)的更新壓力全部集中在一個地方泞遗,可能會導(dǎo)致元數(shù)據(jù)的存儲有壓力。

gossip 好處在于席覆,元數(shù)據(jù)的更新比較分散史辙,不是集中在一個地方,更新請求會陸陸續(xù)續(xù)佩伤,打到所有節(jié)點(diǎn)上去更新聊倔,降低了壓力;不好在于生巡,元數(shù)據(jù)的更新有延時耙蔑,可能導(dǎo)致集群中的一些操作會有一些滯后。

  • 10000 端口
    每個節(jié)點(diǎn)都有一個專門用于節(jié)點(diǎn)間通信的端口孤荣,就是自己提供服務(wù)的端口號+10000甸陌,比如 7001,那么用于節(jié)點(diǎn)間通信的就是 17001 端口盐股。每個節(jié)點(diǎn)每隔一段時間都會往另外幾個節(jié)點(diǎn)發(fā)送 ping 消息钱豁,同時其它幾個節(jié)點(diǎn)接收到 ping 之后返回 pong

  • 交換的信息
    信息包括故障信息疯汁,節(jié)點(diǎn)的增加和刪除牲尺,hash slot 信息 等等。

gossip 協(xié)議

gossip 協(xié)議包含多種消息幌蚊,包含 ping,pong,meet,fail 等等谤碳。

  • meet:某個節(jié)點(diǎn)發(fā)送 meet 給新加入的節(jié)點(diǎn)凛澎,讓新節(jié)點(diǎn)加入集群中,然后新節(jié)點(diǎn)就會開始與其它節(jié)點(diǎn)進(jìn)行通信估蹄。
redis-trib.rb add-node

其實(shí)內(nèi)部就是發(fā)送了一個 gossip meet 消息給新加入的節(jié)點(diǎn)塑煎,通知那個節(jié)點(diǎn)去加入我們的集群。

  • ping:每個節(jié)點(diǎn)都會頻繁給其它節(jié)點(diǎn)發(fā)送 ping臭蚁,其中包含自己的狀態(tài)還有自己維護(hù)的集群元數(shù)據(jù)最铁,互相通過 ping 交換元數(shù)據(jù)。
  • pong:返回 ping 和 meeet垮兑,包含自己的狀態(tài)和其它信息冷尉,也用于信息廣播和更新。
  • fail:某個節(jié)點(diǎn)判斷另一個節(jié)點(diǎn) fail 之后系枪,就發(fā)送 fail 給其它節(jié)點(diǎn)雀哨,通知其它節(jié)點(diǎn)說,某個節(jié)點(diǎn)宕機(jī)啦私爷。

ping 消息深入

ping 時要攜帶一些元數(shù)據(jù)雾棺,如果很頻繁,可能會加重網(wǎng)絡(luò)負(fù)擔(dān)衬浑。

每個節(jié)點(diǎn)每秒會執(zhí)行 10 次 ping捌浩,每次會選擇 5 個最久沒有通信的其它節(jié)點(diǎn)。當(dāng)然如果發(fā)現(xiàn)某個節(jié)點(diǎn)通信延時達(dá)到了 cluster_node_timeout / 2工秩,那么立即發(fā)送 ping尸饺,避免數(shù)據(jù)交換延時過長,落后的時間太長了助币。比如說浪听,兩個節(jié)點(diǎn)之間都 10 分鐘沒有交換數(shù)據(jù)了,那么整個集群處于嚴(yán)重的元數(shù)據(jù)不一致的情況眉菱,就會有問題迹栓。所以 cluster_node_timeout 可以調(diào)節(jié),如果調(diào)得比較大倍谜,那么會降低 ping 的頻率迈螟。

每次 ping,會帶上自己節(jié)點(diǎn)的信息尔崔,還有就是帶上 1/10 其它節(jié)點(diǎn)的信息,發(fā)送出去褥民,進(jìn)行交換季春。至少包含 3 個其它節(jié)點(diǎn)的信息,最多包含總結(jié)點(diǎn)-2 個其它節(jié)點(diǎn)的信息消返。

分布式尋址算法

  • hash 算法(大量緩存重建)
  • 一致性 hash 算法(自動緩存遷移)+ 虛擬節(jié)點(diǎn)(自動負(fù)載均衡)
  • redis cluster 的 hash slot 算法

hash 算法

來了一個 key载弄,首先計算 hash 值耘拇,然后對節(jié)點(diǎn)數(shù)取模。然后打在不同的 master 節(jié)點(diǎn)上宇攻。一旦某一個 master 節(jié)點(diǎn)宕機(jī)惫叛,所有請求過來,都會基于最新的剩余 master 節(jié)點(diǎn)數(shù)去取模逞刷,嘗試去取數(shù)據(jù)嘉涌。這會導(dǎo)致大部分的請求過來,全部無法拿到有效的緩存夸浅,導(dǎo)致大量的流量涌入數(shù)據(jù)庫仑最。

hash

一致性 hash 算法

一致性 hash 算法將整個 hash 值空間組織成一個虛擬的圓環(huán),整個空間按順時針方向組織帆喇,下一步將各個 master 節(jié)點(diǎn)(使用服務(wù)器的 ip 或主機(jī)名)進(jìn)行 hash警医。這樣就能確定每個節(jié)點(diǎn)在其哈希環(huán)上的位置。

來了一個 key坯钦,首先計算 hash 值预皇,并確定此數(shù)據(jù)在環(huán)上的位置,從此位置沿環(huán)順時針“行走”婉刀,遇到的第一個 master 節(jié)點(diǎn)就是 key 所在位置深啤。

在一致性哈希算法中,如果一個節(jié)點(diǎn)掛了路星,受影響的數(shù)據(jù)僅僅是此節(jié)點(diǎn)到環(huán)空間前一個節(jié)點(diǎn)(沿著逆時針方向行走遇到的第一個節(jié)點(diǎn))之間的數(shù)據(jù)溯街,其它不受影響。增加一個節(jié)點(diǎn)也同理洋丐。

燃鵝呈昔,一致性哈希算法在節(jié)點(diǎn)太少時,容易因?yàn)楣?jié)點(diǎn)分布不均勻而造成緩存熱點(diǎn)的問題友绝。為了解決這種熱點(diǎn)問題堤尾,一致性 hash 算法引入了虛擬節(jié)點(diǎn)機(jī)制,即對每一個節(jié)點(diǎn)計算多個 hash迁客,每個計算結(jié)果位置都放置一個虛擬節(jié)點(diǎn)郭宝。這樣就實(shí)現(xiàn)了數(shù)據(jù)的均勻分布,負(fù)載均衡掷漱。

consistent-hashing-algorithm

redis cluster 的 hash slot 算法

redis cluster 有固定的 16384 個 hash slot粘室,對每個 key 計算 CRC16 值,然后對 16384 取模卜范,可以獲取 key 對應(yīng)的 hash slot衔统。

redis cluster 中每個 master 都會持有部分 slot,比如有 3 個 master,那么可能每個 master 持有 5000 多個 hash slot锦爵。hash slot 讓 node 的增加和移除很簡單舱殿,增加一個 master,就將其他 master 的 hash slot 移動部分過去险掀,減少一個 master沪袭,就將它的 hash slot 移動到其他 master 上去。移動 hash slot 的成本是非常低的樟氢「园恚客戶端的 api,可以對指定的數(shù)據(jù)嗡害,讓他們走同一個 hash slot焚碌,通過 hash tag 來實(shí)現(xiàn)。

任何一臺機(jī)器宕機(jī)霸妹,另外兩個節(jié)點(diǎn)十电,不影響的。因?yàn)?key 找的是 hash slot叹螟,不是機(jī)器鹃骂。

hash-slot

redis cluster 的高可用與主備切換原理

redis cluster 的高可用的原理,幾乎跟哨兵是類似的

判斷節(jié)點(diǎn)宕機(jī)

如果一個節(jié)點(diǎn)認(rèn)為另外一個節(jié)點(diǎn)宕機(jī)罢绽,那么就是 pfail畏线,主觀宕機(jī)。如果多個節(jié)點(diǎn)都認(rèn)為另外一個節(jié)點(diǎn)宕機(jī)了良价,那么就是 fail寝殴,客觀宕機(jī),跟哨兵的原理幾乎一樣明垢,sdown蚣常,odown。

cluster-node-timeout 內(nèi)痊银,某個節(jié)點(diǎn)一直沒有返回 pong抵蚊,那么就被認(rèn)為 pfail

如果一個節(jié)點(diǎn)認(rèn)為某個節(jié)點(diǎn) pfail 了溯革,那么會在 gossip ping 消息中贞绳,ping 給其他節(jié)點(diǎn),如果超過半數(shù)的節(jié)點(diǎn)都認(rèn)為 pfail 了致稀,那么就會變成 fail冈闭。

從節(jié)點(diǎn)過濾

對宕機(jī)的 master node,從其所有的 slave node 中豺裆,選擇一個切換成 master node拒秘。

檢查每個 slave node 與 master node 斷開連接的時間号显,如果超過了 cluster-node-timeout * cluster-slave-validity-factor臭猜,那么就沒有資格切換成 master躺酒。

從節(jié)點(diǎn)選舉

每個從節(jié)點(diǎn),都根據(jù)自己對 master 復(fù)制數(shù)據(jù)的 offset蔑歌,來設(shè)置一個選舉時間羹应,offset 越大(復(fù)制數(shù)據(jù)越多)的從節(jié)點(diǎn),選舉時間越靠前次屠,優(yōu)先進(jìn)行選舉园匹。

所有的 master node 開始 slave 選舉投票,給要進(jìn)行選舉的 slave 進(jìn)行投票劫灶,如果大部分 master node(N/2 + 1)都投票給了某個從節(jié)點(diǎn)裸违,那么選舉通過,那個從節(jié)點(diǎn)可以切換成 master本昏。

從節(jié)點(diǎn)執(zhí)行主備切換供汛,從節(jié)點(diǎn)切換為主節(jié)點(diǎn)。

與哨兵比較

整個流程跟哨兵相比涌穆,非常類似怔昨,所以說,redis cluster 功能強(qiáng)大宿稀,直接集成了 replication 和 sentinel 的功能趁舀。

8.了解什么是 redis 的雪崩和穿透?redis 崩潰之后會怎么樣祝沸?系統(tǒng)該如何應(yīng)對這種情況矮烹?如何處理 redis 的穿透?

面試題剖析

緩存雪崩

對于系統(tǒng) A罩锐,假設(shè)每天高峰期每秒 5000 個請求奉狈,本來緩存在高峰期可以扛住每秒 4000 個請求,但是緩存機(jī)器意外發(fā)生了全盤宕機(jī)唯欣。緩存掛了嘹吨,此時 1 秒 5000 個請求全部落數(shù)據(jù)庫,數(shù)據(jù)庫必然扛不住境氢,它會報一下警蟀拷,然后就掛了。此時萍聊,如果沒用什么特別的方案來處理這個故障问芬,DBA 很著急,重啟數(shù)據(jù)庫寿桨,但是數(shù)據(jù)庫立馬又被新的流量給打死了此衅。

這就是緩存雪崩强戴。

redis-caching-avalanche

大約在 3 年前,國內(nèi)比較知名的一個互聯(lián)網(wǎng)公司挡鞍,曾因?yàn)榫彺媸鹿势锎酰瑢?dǎo)致雪崩,后臺系統(tǒng)全部崩潰墨微,事故從當(dāng)天下午持續(xù)到晚上凌晨 3~4 點(diǎn)道媚,公司損失了幾千萬。

緩存雪崩的事前事中事后的解決方案如下翘县。

  • 事前:redis 高可用最域,主從+哨兵,redis cluster锈麸,避免全盤崩潰镀脂。
  • 事中:本地 ehcache 緩存 + hystrix 限流&降級,避免 MySQL 被打死忘伞。
  • 事后:redis 持久化薄翅,一旦重啟,自動從磁盤上加載數(shù)據(jù)虑省,快速恢復(fù)緩存數(shù)據(jù)匿刮。
redis-caching-avalanche-solution

用戶發(fā)送一個請求,系統(tǒng) A 收到請求后探颈,先查本地 ehcache 緩存熟丸,如果沒查到再查 redis。如果 ehcache 和 redis 都沒有伪节,再查數(shù)據(jù)庫光羞,將數(shù)據(jù)庫中的結(jié)果,寫入 ehcache 和 redis 中怀大。

限流組件纱兑,可以設(shè)置每秒的請求,有多少能通過組件化借,剩余的未通過的請求潜慎,怎么辦?走降級蓖康!可以返回一些默認(rèn)的值铐炫,或者友情提示,或者空白的值蒜焊。

好處:

  • 數(shù)據(jù)庫絕對不會死倒信,限流組件確保了每秒只有多少個請求能通過。
  • 只要數(shù)據(jù)庫不死泳梆,就是說鳖悠,對用戶來說榜掌,2/5 的請求都是可以被處理的。
  • 只要有 2/5 的請求可以被處理乘综,就意味著你的系統(tǒng)沒死憎账,對用戶來說,可能就是點(diǎn)擊幾次刷不出來頁面瘾带,但是多點(diǎn)幾次鼠哥,就可以刷出來一次熟菲。

緩存穿透

對于系統(tǒng)A看政,假設(shè)一秒 5000 個請求,結(jié)果其中 4000 個請求是黑客發(fā)出的惡意攻擊抄罕。

黑客發(fā)出的那 4000 個攻擊允蚣,緩存中查不到,每次你去數(shù)據(jù)庫里查呆贿,也查不到嚷兔。

舉個栗子。數(shù)據(jù)庫 id 是從 1 開始的做入,結(jié)果黑客發(fā)過來的請求 id 全部都是負(fù)數(shù)冒晰。這樣的話,緩存中不會有竟块,請求每次都“視緩存于無物”壶运,直接查詢數(shù)據(jù)庫。這種惡意攻擊場景的緩存穿透就會直接把數(shù)據(jù)庫給打死浪秘。

redis-caching-penetration

解決方式很簡單蒋情,每次系統(tǒng) A 從數(shù)據(jù)庫中只要沒查到,就寫一個空值到緩存里去耸携,比如 set -999 UNKNOWN棵癣。這樣的話,下次便能走緩存了夺衍。

9.如何保證緩存與數(shù)據(jù)庫的雙寫一致性狈谊?

面試題剖析

一般來說,如果允許緩存可以稍微的跟數(shù)據(jù)庫偶爾有不一致的情況沟沙,也就是說如果你的系統(tǒng)不是嚴(yán)格要求 “緩存+數(shù)據(jù)庫” 必須保持一致性的話河劝,最好不要做這個方案,即:讀請求和寫請求串行化尝胆,串到一個內(nèi)存隊(duì)列里去丧裁。

串行化可以保證一定不會出現(xiàn)不一致的情況,但是它也會導(dǎo)致系統(tǒng)的吞吐量大幅度降低含衔,用比正常情況下多幾倍的機(jī)器去支撐線上的一個請求煎娇。

Cache Aside Pattern

最經(jīng)典的緩存+數(shù)據(jù)庫讀寫的模式二庵,就是 Cache Aside Pattern。

  • 讀的時候缓呛,先讀緩存催享,緩存沒有的話,就讀數(shù)據(jù)庫哟绊,然后取出數(shù)據(jù)后放入緩存因妙,同時返回響應(yīng)。
  • 更新的時候票髓,先更新數(shù)據(jù)庫攀涵,然后再刪除緩存

為什么是刪除緩存洽沟,而不是更新緩存以故?

原因很簡單,很多時候裆操,在復(fù)雜點(diǎn)的緩存場景怒详,緩存不單單是數(shù)據(jù)庫中直接取出來的值。

比如可能更新了某個表的一個字段踪区,然后其對應(yīng)的緩存昆烁,是需要查詢另外兩個表的數(shù)據(jù)并進(jìn)行運(yùn)算,才能計算出緩存最新的值的缎岗。

另外更新緩存的代價有時候是很高的静尼。是不是說,每次修改數(shù)據(jù)庫的時候密强,都一定要將其對應(yīng)的緩存更新一份茅郎?也許有的場景是這樣,但是對于比較復(fù)雜的緩存數(shù)據(jù)計算的場景或渤,就不是這樣了系冗。如果你頻繁修改一個緩存涉及的多個表,緩存也頻繁更新薪鹦。但是問題在于掌敬,這個緩存到底會不會被頻繁訪問到?

舉個栗子池磁,一個緩存涉及的表的字段奔害,在 1 分鐘內(nèi)就修改了 20 次,或者是 100 次地熄,那么緩存更新 20 次华临、100 次;但是這個緩存在 1 分鐘內(nèi)只被讀取了 1 次端考,有大量的冷數(shù)據(jù)雅潭。實(shí)際上揭厚,如果你只是刪除緩存的話,那么在 1 分鐘內(nèi)扶供,這個緩存不過就重新計算一次而已筛圆,開銷大幅度降低。用到緩存才去算緩存椿浓。

其實(shí)刪除緩存太援,而不是更新緩存,就是一個 lazy 計算的思想扳碍,不要每次都重新做復(fù)雜的計算提岔,不管它會不會用到,而是讓它到需要被使用的時候再重新計算左腔。像 mybatis唧垦,hibernate,都有懶加載思想液样。查詢一個部門,部門帶了一個員工的 list巧还,沒有必要說每次查詢部門鞭莽,都里面的 1000 個員工的數(shù)據(jù)也同時查出來啊。80% 的情況麸祷,查這個部門澎怒,就只是要訪問這個部門的信息就可以了。先查部門阶牍,同時要訪問里面的員工喷面,那么這個時候只有在你要訪問里面的員工的時候,才會去數(shù)據(jù)庫里面查詢 1000 個員工走孽。

最初級的緩存不一致問題及解決方案

問題:先修改數(shù)據(jù)庫惧辈,再刪除緩存。如果刪除緩存失敗了磕瓷,那么會導(dǎo)致數(shù)據(jù)庫中是新數(shù)據(jù)盒齿,緩存中是舊數(shù)據(jù),數(shù)據(jù)就出現(xiàn)了不一致困食。

redis-junior-inconsistent

解決思路:先刪除緩存边翁,再修改數(shù)據(jù)庫。如果數(shù)據(jù)庫修改失敗了硕盹,那么數(shù)據(jù)庫中是舊數(shù)據(jù)符匾,緩存中是空的,那么數(shù)據(jù)不會不一致瘩例。因?yàn)樽x的時候緩存沒有啊胶,則讀數(shù)據(jù)庫中舊數(shù)據(jù)芒澜,然后更新到緩存中。

比較復(fù)雜的數(shù)據(jù)不一致問題分析

數(shù)據(jù)發(fā)生了變更创淡,先刪除了緩存痴晦,然后要去修改數(shù)據(jù)庫,此時還沒修改琳彩。一個請求過來誊酌,去讀緩存,發(fā)現(xiàn)緩存空了露乏,去查詢數(shù)據(jù)庫碧浊,查到了修改前的舊數(shù)據(jù),放到了緩存中瘟仿。隨后數(shù)據(jù)變更的程序完成了數(shù)據(jù)庫的修改箱锐。完了,數(shù)據(jù)庫和緩存中的數(shù)據(jù)不一樣了...

為什么上億流量高并發(fā)場景下劳较,緩存會出現(xiàn)這個問題驹止?

只有在對一個數(shù)據(jù)在并發(fā)的進(jìn)行讀寫的時候,才可能會出現(xiàn)這種問題观蜗。其實(shí)如果說你的并發(fā)量很低的話臊恋,特別是讀并發(fā)很低,每天訪問量就 1 萬次墓捻,那么很少的情況下抖仅,會出現(xiàn)剛才描述的那種不一致的場景。但是問題是砖第,如果每天的是上億的流量撤卢,每秒并發(fā)讀是幾萬,每秒只要有數(shù)據(jù)更新的請求梧兼,就可能會出現(xiàn)上述的數(shù)據(jù)庫+緩存不一致的情況放吩。

解決方案如下:

更新數(shù)據(jù)的時候,根據(jù)數(shù)據(jù)的唯一標(biāo)識袱院,將操作路由之后屎慢,發(fā)送到一個 jvm 內(nèi)部隊(duì)列中。讀取數(shù)據(jù)的時候忽洛,如果發(fā)現(xiàn)數(shù)據(jù)不在緩存中腻惠,那么將重新讀取數(shù)據(jù)+更新緩存的操作,根據(jù)唯一標(biāo)識路由之后欲虚,也發(fā)送同一個 jvm 內(nèi)部隊(duì)列中集灌。

一個隊(duì)列對應(yīng)一個工作線程,每個工作線程串行拿到對應(yīng)的操作,然后一條一條的執(zhí)行欣喧。這樣的話腌零,一個數(shù)據(jù)變更的操作,先刪除緩存唆阿,然后再去更新數(shù)據(jù)庫益涧,但是還沒完成更新。此時如果一個讀請求過來驯鳖,讀到了空的緩存闲询,那么可以先將緩存更新的請求發(fā)送到隊(duì)列中,此時會在隊(duì)列中積壓浅辙,然后同步等待緩存更新完成扭弧。

這里有一個優(yōu)化點(diǎn),一個隊(duì)列中记舆,其實(shí)多個更新緩存請求串在一起是沒意義的鸽捻,因此可以做過濾,如果發(fā)現(xiàn)隊(duì)列中已經(jīng)有一個更新緩存的請求了泽腮,那么就不用再放個更新請求操作進(jìn)去了御蒲,直接等待前面的更新操作請求完成即可。

待那個隊(duì)列對應(yīng)的工作線程完成了上一個操作的數(shù)據(jù)庫的修改之后盛正,才會去執(zhí)行下一個操作删咱,也就是緩存更新的操作,此時會從數(shù)據(jù)庫中讀取最新的值豪筝,然后寫入緩存中。

如果請求還在等待時間范圍內(nèi)摘能,不斷輪詢發(fā)現(xiàn)可以取到值了续崖,那么就直接返回;如果請求等待的時間超過一定時長团搞,那么這一次直接從數(shù)據(jù)庫中讀取當(dāng)前的舊值严望。

高并發(fā)的場景下,該解決方案要注意的問題:

  • 讀請求長時阻塞

由于讀請求進(jìn)行了非常輕度的異步化逻恐,所以一定要注意讀超時的問題像吻,每個讀請求必須在超時時間范圍內(nèi)返回。

該解決方案复隆,最大的風(fēng)險點(diǎn)在于說拨匆,可能數(shù)據(jù)更新很頻繁,導(dǎo)致隊(duì)列中積壓了大量更新操作在里面挽拂,然后讀請求會發(fā)生大量的超時惭每,最后導(dǎo)致大量的請求直接走數(shù)據(jù)庫。務(wù)必通過一些模擬真實(shí)的測試亏栈,看看更新數(shù)據(jù)的頻率是怎樣的台腥。

另外一點(diǎn)宏赘,因?yàn)橐粋€隊(duì)列中,可能會積壓針對多個數(shù)據(jù)項(xiàng)的更新操作黎侈,因此需要根據(jù)自己的業(yè)務(wù)情況進(jìn)行測試察署,可能需要部署多個服務(wù),每個服務(wù)分?jǐn)傄恍?shù)據(jù)的更新操作峻汉。如果一個內(nèi)存隊(duì)列里居然會擠壓 100 個商品的庫存修改操作贴汪,每隔庫存修改操作要耗費(fèi) 10ms 去完成,那么最后一個商品的讀請求俱济,可能等待 10 * 100 = 1000ms = 1s 后嘶是,才能得到數(shù)據(jù),這個時候就導(dǎo)致讀請求的長時阻塞蛛碌。

一定要做根據(jù)實(shí)際業(yè)務(wù)系統(tǒng)的運(yùn)行情況聂喇,去進(jìn)行一些壓力測試,和模擬線上環(huán)境蔚携,去看看最繁忙的時候希太,內(nèi)存隊(duì)列可能會擠壓多少更新操作,可能會導(dǎo)致最后一個更新操作對應(yīng)的讀請求酝蜒,會 hang 多少時間誊辉,如果讀請求在 200ms 返回,如果你計算過后亡脑,哪怕是最繁忙的時候堕澄,積壓 10 個更新操作,最多等待 200ms霉咨,那還可以的蛙紫。

如果一個內(nèi)存隊(duì)列中可能積壓的更新操作特別多,那么你就要加機(jī)器途戒,讓每個機(jī)器上部署的服務(wù)實(shí)例處理更少的數(shù)據(jù)坑傅,那么每個內(nèi)存隊(duì)列中積壓的更新操作就會越少。

其實(shí)根據(jù)之前的項(xiàng)目經(jīng)驗(yàn)喷斋,一般來說唁毒,數(shù)據(jù)的寫頻率是很低的,因此實(shí)際上正常來說星爪,在隊(duì)列中積壓的更新操作應(yīng)該是很少的浆西。像這種針對讀高并發(fā)、讀緩存架構(gòu)的項(xiàng)目移必,一般來說寫請求是非常少的室谚,每秒的 QPS 能到幾百就不錯了。

我們來實(shí)際粗略測算一下

如果一秒有 500 的寫操作秒赤,如果分成 5 個時間片猪瞬,每 200ms 就 100 個寫操作,放到 20 個內(nèi)存隊(duì)列中入篮,每個內(nèi)存隊(duì)列陈瘦,可能就積壓 5 個寫操作。每個寫操作性能測試后潮售,一般是在 20ms 左右就完成痊项,那么針對每個內(nèi)存隊(duì)列的數(shù)據(jù)的讀請求,也就最多 hang 一會兒酥诽,200ms 以內(nèi)肯定能返回了鞍泉。

經(jīng)過剛才簡單的測算,我們知道肮帐,單機(jī)支撐的寫 QPS 在幾百是沒問題的咖驮,如果寫 QPS 擴(kuò)大了 10 倍,那么就擴(kuò)容機(jī)器训枢,擴(kuò)容 10 倍的機(jī)器托修,每個機(jī)器 20 個隊(duì)列。

  • 讀請求并發(fā)量過高

這里還必須做好壓力測試恒界,確保恰巧碰上上述情況的時候睦刃,還有一個風(fēng)險,就是突然間大量讀請求會在幾十毫秒的延時 hang 在服務(wù)上十酣,看服務(wù)能不能扛的住涩拙,需要多少機(jī)器才能扛住最大的極限情況的峰值。

但是因?yàn)椴⒉皇撬械臄?shù)據(jù)都在同一時間更新耸采,緩存也不會同一時間失效吃环,所以每次可能也就是少數(shù)數(shù)據(jù)的緩存失效了,然后那些數(shù)據(jù)對應(yīng)的讀請求過來洋幻,并發(fā)量應(yīng)該也不會特別大。

  • 多服務(wù)實(shí)例部署的請求路由

可能這個服務(wù)部署了多個實(shí)例翅娶,那么必須保證說文留,執(zhí)行數(shù)據(jù)更新操作,以及執(zhí)行緩存更新操作的請求竭沫,都通過 Nginx 服務(wù)器路由到相同的服務(wù)實(shí)例上燥翅。

比如說,對同一個商品的讀寫請求蜕提,全部路由到同一臺機(jī)器上森书。可以自己去做服務(wù)間的按照某個請求參數(shù)的 hash 路由,也可以用 Nginx 的 hash 路由功能等等凛膏。

  • 熱點(diǎn)商品的路由問題杨名,導(dǎo)致請求的傾斜

萬一某個商品的讀寫請求特別高,全部打到相同的機(jī)器的相同的隊(duì)列里面去了猖毫,可能會造成某臺機(jī)器的壓力過大台谍。就是說,因?yàn)橹挥性谏唐窋?shù)據(jù)更新的時候才會清空緩存吁断,然后才會導(dǎo)致讀寫并發(fā)趁蕊,所以其實(shí)要根據(jù)業(yè)務(wù)系統(tǒng)去看,如果更新頻率不是太高的話仔役,這個問題的影響并不是特別大掷伙,但是的確可能某些機(jī)器的負(fù)載會高一些。

10.redis 的并發(fā)競爭問題是什么又兵?如何解決這個問題任柜?了解 redis 事務(wù)的 CAS 方案嗎?

考點(diǎn)分析

這個也是線上非常常見的一個問題寒波,就是多客戶端同時并發(fā)寫一個 key乘盼,可能本來應(yīng)該先到的數(shù)據(jù)后到了,導(dǎo)致數(shù)據(jù)版本錯了俄烁;或者是多客戶端同時獲取一個 key绸栅,修改值之后再寫回去,只要順序錯了页屠,數(shù)據(jù)就錯了粹胯。

而且 redis 自己就有天然解決這個問題的 CAS 類的樂觀鎖方案。

面試題剖析

某個時刻辰企,多個系統(tǒng)實(shí)例都去更新某個 key风纠。可以基于 zookeeper 實(shí)現(xiàn)分布式鎖牢贸。每個系統(tǒng)通過 zookeeper 獲取分布式鎖竹观,確保同一時間,只能有一個系統(tǒng)實(shí)例在操作某個 key潜索,別人都不允許讀和寫臭增。

zookeeper-distributed-lock

你要寫入緩存的數(shù)據(jù),都是從 mysql 里查出來的竹习,都得寫入 mysql 中誊抛,寫入 mysql 中的時候必須保存一個時間戳,從 mysql 查出來的時候整陌,時間戳也查出來拗窃。

每次要寫之前瞎领,先判斷一下當(dāng)前這個 value 的時間戳是否比緩存里的 value 的時間戳要新。如果是的話随夸,那么可以寫九默,否則,就不能用舊的數(shù)據(jù)覆蓋新的數(shù)據(jù)逃魄。

11.生產(chǎn)環(huán)境中的 redis 是怎么部署的荤西?

面試題剖析

redis cluster,10 臺機(jī)器伍俘,5 臺機(jī)器部署了 redis 主實(shí)例邪锌,另外 5 臺機(jī)器部署了 redis 的從實(shí)例霍掺,每個主實(shí)例掛了一個從實(shí)例吩谦,5 個節(jié)點(diǎn)對外提供讀寫服務(wù),每個節(jié)點(diǎn)的讀寫高峰qps可能可以達(dá)到每秒 5 萬钝域,5 臺機(jī)器最多是 25 萬讀寫請求/s妨退。

機(jī)器是什么配置妇萄?32G 內(nèi)存+ 8 核 CPU + 1T 磁盤,但是分配給 redis 進(jìn)程的是10g內(nèi)存咬荷,一般線上生產(chǎn)環(huán)境冠句,redis 的內(nèi)存盡量不要超過 10g,超過 10g 可能會有問題幸乒。

5 臺機(jī)器對外提供讀寫懦底,一共有 50g 內(nèi)存。

因?yàn)槊總€主實(shí)例都掛了一個從實(shí)例罕扎,所以是高可用的聚唐,任何一個主實(shí)例宕機(jī),都會自動故障遷移腔召,redis 從實(shí)例會自動變成主實(shí)例繼續(xù)提供讀寫服務(wù)杆查。

你往內(nèi)存里寫的是什么數(shù)據(jù)?每條數(shù)據(jù)的大小是多少臀蛛?商品數(shù)據(jù)亲桦,每條數(shù)據(jù)是 10kb。100 條數(shù)據(jù)是 1mb浊仆,10 萬條數(shù)據(jù)是 1g烙肺。常駐內(nèi)存的是 200 萬條商品數(shù)據(jù),占用內(nèi)存是 20g氧卧,僅僅不到總內(nèi)存的 50%。目前高峰期每秒就是 3500 左右的請求量氏堤。

其實(shí)大型的公司沙绝,會有基礎(chǔ)架構(gòu)的 team 負(fù)責(zé)緩存集群的運(yùn)維搏明。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市闪檬,隨后出現(xiàn)的幾起案子星著,更是在濱河造成了極大的恐慌,老刑警劉巖粗悯,帶你破解...
    沈念sama閱讀 216,372評論 6 498
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件虚循,死亡現(xiàn)場離奇詭異,居然都是意外死亡样傍,警方通過查閱死者的電腦和手機(jī)横缔,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,368評論 3 392
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來衫哥,“玉大人茎刚,你說我怎么就攤上這事〕贩辏” “怎么了膛锭?”我有些...
    開封第一講書人閱讀 162,415評論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長蚊荣。 經(jīng)常有香客問我初狰,道長,這世上最難降的妖魔是什么互例? 我笑而不...
    開封第一講書人閱讀 58,157評論 1 292
  • 正文 為了忘掉前任奢入,我火速辦了婚禮,結(jié)果婚禮上敲霍,老公的妹妹穿的比我還像新娘俊马。我一直安慰自己,他們只是感情好肩杈,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,171評論 6 388
  • 文/花漫 我一把揭開白布柴我。 她就那樣靜靜地躺著,像睡著了一般扩然。 火紅的嫁衣襯著肌膚如雪艘儒。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,125評論 1 297
  • 那天夫偶,我揣著相機(jī)與錄音界睁,去河邊找鬼。 笑死兵拢,一個胖子當(dāng)著我的面吹牛翻斟,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播说铃,決...
    沈念sama閱讀 40,028評論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼访惜,長吁一口氣:“原來是場噩夢啊……” “哼嘹履!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起债热,我...
    開封第一講書人閱讀 38,887評論 0 274
  • 序言:老撾萬榮一對情侶失蹤砾嫉,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后窒篱,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體焕刮,經(jīng)...
    沈念sama閱讀 45,310評論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,533評論 2 332
  • 正文 我和宋清朗相戀三年墙杯,在試婚紗的時候發(fā)現(xiàn)自己被綠了配并。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,690評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡霍转,死狀恐怖荐绝,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情避消,我是刑警寧澤低滩,帶...
    沈念sama閱讀 35,411評論 5 343
  • 正文 年R本政府宣布,位于F島的核電站岩喷,受9級特大地震影響恕沫,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜纱意,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,004評論 3 325
  • 文/蒙蒙 一婶溯、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧偷霉,春花似錦迄委、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,659評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至硫狞,卻和暖如春信轿,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背残吩。 一陣腳步聲響...
    開封第一講書人閱讀 32,812評論 1 268
  • 我被黑心中介騙來泰國打工财忽, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人泣侮。 一個月前我還...
    沈念sama閱讀 47,693評論 2 368
  • 正文 我出身青樓即彪,卻偏偏與公主長得像,于是被迫代替她去往敵國和親活尊。 傳聞我的和親對象是個殘疾皇子祖凫,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,577評論 2 353

推薦閱讀更多精彩內(nèi)容