Codis作者黃東旭細(xì)說分布式Redis架構(gòu)設(shè)計(jì)和踩過的那些坑
黃東旭,Ping CAP CTO,開源項(xiàng)目Codis的co-author。之前在豌豆莢從事infrastructure相關(guān)的工作,現(xiàn)在在創(chuàng)業(yè)公司PingCAP,方向依然是分布式存儲(chǔ)領(lǐng)域(NewSQL)。
本次分享的內(nèi)容主要包括五個(gè)大部分:
- Redis、RedisCluster和Codis;
- 我們更愛一致性;
- Codis在生產(chǎn)環(huán)境中的使用的經(jīng)驗(yàn)和坑們;
- 對(duì)于分布式數(shù)據(jù)庫(kù)和分布式架構(gòu)的一些看法;
- Q & A環(huán)節(jié)。
Codis是一個(gè)分布式Redis解決方案,與官方的純P2P的模式不同,Codis采用的是Proxy-based的方案。今天我們介紹一下Codis及下一個(gè)大版本RebornDB的設(shè)計(jì),同時(shí)會(huì)介紹一些Codis在實(shí)際應(yīng)用場(chǎng)景中的tips。最后拋磚引玉,會(huì)介紹一下我對(duì)分布式存儲(chǔ)的一些觀點(diǎn)和看法,望各位首席們雅正。
一、 Redis,RedisCluster和Codis
Redis:想必大家的架構(gòu)中,Redis已經(jīng)是一個(gè)必不可少的部件,豐富的數(shù)據(jù)結(jié)構(gòu)和超高的性能以及簡(jiǎn)單的協(xié)議,讓Redis能夠很好的作為數(shù)據(jù)庫(kù)的上游緩存層。但是我們會(huì)比較擔(dān)心Redis的單點(diǎn)問題,單點(diǎn)Redis容量大小總受限于內(nèi)存,在業(yè)務(wù)對(duì)性能要求比較高的情況下,理想情況下我們希望所有的數(shù)據(jù)都能在內(nèi)存里面,不要打到數(shù)據(jù)庫(kù)上,所以很自然的就會(huì)尋求其他方案。 比如,SSD將內(nèi)存換成了磁盤,以換取更大的容量。更自然的想法是將Redis變成一個(gè)可以水平擴(kuò)展的分布式緩存服務(wù),在Codis之前,業(yè)界只有Twemproxy,但是Twemproxy本身是一個(gè)靜態(tài)的分布式Redis方案,進(jìn)行擴(kuò)容/縮容時(shí)候?qū)\(yùn)維要求非常高,而且很難做到平滑的擴(kuò)縮容。Codis的目標(biāo)其實(shí)就是盡量兼容Twemproxy的基礎(chǔ)上,加上數(shù)據(jù)遷移的功能以實(shí)現(xiàn)擴(kuò)容和縮容,最終替換Twemproxy。從豌豆莢最后上線的結(jié)果來(lái)看,最后完全替換了Twem,大概2T左右的內(nèi)存集群。
Redis Cluster :與Codis同期發(fā)布正式版的官方cluster,我認(rèn)為有優(yōu)點(diǎn)也有缺點(diǎn),作為架構(gòu)師,我并不會(huì)在生產(chǎn)環(huán)境中使用,原因有兩個(gè):
cluster的數(shù)據(jù)存儲(chǔ)模塊和分布式的邏輯模塊是耦合在一起的,這個(gè)帶來(lái)的好處是部署異常簡(jiǎn)單,all-in-the-box,沒有像Codis那么多概念,組件和依賴。但是帶來(lái)的缺點(diǎn)是,你很難對(duì)業(yè)務(wù)進(jìn)行無(wú)痛的升級(jí)。比如哪天Redis cluster的分布式邏輯出現(xiàn)了比較嚴(yán)重的bug,你該如何升級(jí)?除了滾動(dòng)重啟整個(gè)集群,沒什么好辦法。這個(gè)比較傷運(yùn)維。
對(duì)協(xié)議進(jìn)行了較大的修改,對(duì)客戶端不太友好,目前很多客戶端已經(jīng)成為事實(shí)標(biāo)準(zhǔn),而且很多程序已經(jīng)寫好了,讓業(yè)務(wù)方去更換Redisclient,是不太現(xiàn)實(shí)的,而且目前很難說有哪個(gè)Rediscluster客戶端經(jīng)過了大規(guī)模生產(chǎn)環(huán)境的驗(yàn)證,從HunanTV開源的Rediscluster proxy上可以看得出這個(gè)影響還是蠻大的,否則就會(huì)支持使用cluster的client了。
Codis:和Redis cluster不同的是,Codis采用一層無(wú)狀態(tài)的proxy層,將分布式邏輯寫在proxy上,底層的存儲(chǔ)引擎還是Redis本身(盡管基于Redis2.8.13上做了一些小patch),數(shù)據(jù)的分布狀態(tài)存儲(chǔ)于zookeeper(etcd)中,底層的數(shù)據(jù)存儲(chǔ)變成了可插拔的部件。這個(gè)事情的好處其實(shí)不用多說,就是各個(gè)部件是可以動(dòng)態(tài)水平擴(kuò)展的,尤其無(wú)狀態(tài)的proxy對(duì)于動(dòng)態(tài)的負(fù)載均衡,還是意義很大的,而且還可以做一些有意思的事情,比如發(fā)現(xiàn)一些slot的數(shù)據(jù)比較冷,可以專門用一個(gè)支持持久化存儲(chǔ)的server group來(lái)負(fù)責(zé)這部分slot,以節(jié)省內(nèi)存,當(dāng)這部分?jǐn)?shù)據(jù)變熱起來(lái)時(shí),可以再動(dòng)態(tài)的遷移到內(nèi)存的server group上,一切對(duì)業(yè)務(wù)透明。比較有意思的是,在Twitter內(nèi)部棄用Twmeproxy后,t家自己開發(fā)了一個(gè)新的分布式Redis解決方案,仍然走的是proxy-based路線。不過沒有開源出來(lái)。可插拔存儲(chǔ)引擎這個(gè)事情也是Codis的下一代產(chǎn)品RebornDB在做的一件事情。btw,RebornDB和它的持久化引擎都是完全開源的,見https://github.com/reborndb/reborn和https://github.com/reborndb/qdb。當(dāng)然這樣的設(shè)計(jì)的壞處是,經(jīng)過了proxy,多了一次網(wǎng)絡(luò)交互,看上去性能下降了一些,但是記住,我們的proxy是可以動(dòng)態(tài)擴(kuò)展的,整個(gè)服務(wù)的QPS并不由單個(gè)proxy的性能決定(所以生產(chǎn)環(huán)境中我建議使用LVS/HA Proxy或者Jodis),每個(gè)proxy其實(shí)都是一樣的。
二、我們更愛一致性
很多朋友問我,為什么不支持讀寫分離,其實(shí)這個(gè)事情的原因很簡(jiǎn)單,因?yàn)槲覀儺?dāng)時(shí)的業(yè)務(wù)場(chǎng)景不能容忍數(shù)據(jù)不一致,由于Redis本身的replication模型是主從異步復(fù)制,在master上寫成功后,在slave上是否能讀到這個(gè)數(shù)據(jù)是沒有保證的,而讓業(yè)務(wù)方處理一致性的問題還是蠻麻煩的。而且Redis單點(diǎn)的性能還是蠻高的,不像mysql之類的真正的數(shù)據(jù)庫(kù),沒有必要為了提升一點(diǎn)點(diǎn)讀QPS而讓業(yè)務(wù)方困惑。這和數(shù)據(jù)庫(kù)的角色不太一樣。所以,你可能看出來(lái)了,其實(shí)Codis的HA,并不能保證數(shù)據(jù)完全不丟失,因?yàn)槭钱惒綇?fù)制,所以master掛掉后,如果有沒有同步到slave上的數(shù)據(jù),此時(shí)將slave提升成master后,剛剛寫入的還沒來(lái)得及同步的數(shù)據(jù)就會(huì)丟失。不過在RebornDB中我們會(huì)嘗試對(duì)持久化存儲(chǔ)引擎(qdb)可能會(huì)支持同步復(fù)制(syncreplication),讓一些對(duì)數(shù)據(jù)一致性和安全性有更強(qiáng)要求的服務(wù)可以使用。
說到一致性,這也是Codis支持的MGET/MSET無(wú)法保證原本單點(diǎn)時(shí)的原子語(yǔ)義的原因。 因?yàn)镸SET所參與的key可能分不在不同的機(jī)器上,如果需要保證原來(lái)的語(yǔ)義,也就是要么一起成功,要么一起失敗,這樣就是一個(gè)分布式事務(wù)的問題,對(duì)于Redis來(lái)說,并沒有WAL或者回滾這么一說,所以即使是一個(gè)最簡(jiǎn)單的二階段提交的策略都很難實(shí)現(xiàn),而且即使實(shí)現(xiàn)了,性能也沒有保證。所以在Codis中使用MSET/MGET其實(shí)和你本地開個(gè)多線程SET/GET效果一樣,只不過是由服務(wù)端打包返回罷了,我們加上這個(gè)命令的支持只是為了更好的支持以前用Twemproxy的業(yè)務(wù)。
在實(shí)際場(chǎng)景中,很多朋友使用了lua腳本以擴(kuò)展Redis的功能,其實(shí)Codis這邊是支持的,但記住,Codis在涉及這種場(chǎng)景的時(shí)候,僅僅是轉(zhuǎn)發(fā)而已,它并不保證你的腳本操作的數(shù)據(jù)是否在正確的節(jié)點(diǎn)上。比如,你的腳本里涉及操作多個(gè)key,Codis能做的就是將這個(gè)腳本分配到參數(shù)列表中的第一個(gè)key的機(jī)器上執(zhí)行。所以這種場(chǎng)景下,你需要自己保證你的腳本所用到的key分布在同一個(gè)機(jī)器上,這里可以采用hashtag的方式。
比如你有一個(gè)腳本是操作某個(gè)用戶的多個(gè)信息,如uid1age,uid1sex,uid1name形如此類的key,如果你不用hashtag的話,這些key可能會(huì)分散在不同的機(jī)器上,如果使用了hashtag(用花括號(hào)擴(kuò)住計(jì)算hash的區(qū)域):{uid1}age,{uid1}sex,{uid1}name,這樣就保證這些key分布在同一個(gè)機(jī)器上。這個(gè)是twemproxy引入的一個(gè)語(yǔ)法,我們這邊也支持了。
在開源Codis后,我們收到了很多社區(qū)的反饋,大多數(shù)的意見是集中在Zookeeper的依賴,Redis的修改,還有為啥需要Proxy上面,我們也在思考,這幾個(gè)東西是不是必須的。當(dāng)然這幾個(gè)部件帶來(lái)的好處毋庸置疑,上面也闡述過了,但是有沒有辦法能做得更漂亮。于是,我們?cè)谙乱浑A段會(huì)再往前走一步,實(shí)現(xiàn)以下幾個(gè)設(shè)計(jì):
使用proxy內(nèi)置的Raft來(lái)代替外部的Zookeeper,zk對(duì)于我們來(lái)說,其實(shí)只是一個(gè)強(qiáng)一致性存儲(chǔ)而已,我們其實(shí)可以使用Raft來(lái)做到同樣的事情。將raft嵌入proxy,來(lái)同步路由信息。達(dá)到減少依賴的效果。
抽象存儲(chǔ)引擎層,由proxy或者第三方的agent來(lái)負(fù)責(zé)啟動(dòng)和管理存儲(chǔ)引擎的生命周期。具體來(lái)說,就是現(xiàn)在codis還需要手動(dòng)的去部署底層的Redis或者qdb,自己配置主從關(guān)系什么的,但是未來(lái)我們會(huì)把這個(gè)事情交給一個(gè)自動(dòng)化的agent或者甚至在proxy內(nèi)部集成存儲(chǔ)引擎。這樣的好處是我們可以最大程度上的減小Proxy轉(zhuǎn)發(fā)的損耗(比如proxy會(huì)在本地啟動(dòng)Redis instance)和人工誤操作,提升了整個(gè)系統(tǒng)的自動(dòng)化程度。
還有replication based migration。眾所周知,現(xiàn)在Codis的數(shù)據(jù)遷移方式是通過修改底層Redis,加入單key的原子遷移命令實(shí)現(xiàn)的。這樣的好處是實(shí)現(xiàn)簡(jiǎn)單、遷移過程對(duì)業(yè)務(wù)無(wú)感知。但是壞處也是很明顯,首先就是速度比較慢,而且對(duì)Redis有侵入性,還有維護(hù)slot信息給Redis帶來(lái)額外的內(nèi)存開銷。大概對(duì)于小key-value為主業(yè)務(wù)和原生Redis是1:1.5的比例,所以還是比較費(fèi)內(nèi)存的。
在RebornDB中我們會(huì)嘗試提供基于復(fù)制的遷移方式,也就是開始遷移時(shí),記錄某slot的操作,然后在后臺(tái)開始同步到slave,當(dāng)slave同步完后,開始將記錄的操作回放,回放差不多后,將master的寫入停止,追平后修改路由表,將需要遷移的slot切換成新的master,主從(半)同步復(fù)制,這個(gè)之前提到過。
#p#
三、Codis在生產(chǎn)環(huán)境中的使用的經(jīng)驗(yàn)和坑們
來(lái)說一些 tips,作為開發(fā)工程師,一線的操作經(jīng)驗(yàn)肯定沒有運(yùn)維的同學(xué)多,大家一會(huì)可以一起再深度討論。
關(guān)于多產(chǎn)品線部署:很多朋友問我們?nèi)绻卸鄠€(gè)項(xiàng)目時(shí),codis如何部署比較好,我們當(dāng)時(shí)在豌豆莢的時(shí)候,一個(gè)產(chǎn)品線會(huì)部署一整套codis,但是zk共用一個(gè),不同的codis集群擁有不同的product name來(lái)區(qū)分,codis本身的設(shè)計(jì)沒有命名空間那么一說,一個(gè)codis只能對(duì)應(yīng)一個(gè)product name。不同product name的codis集群在同一個(gè)zk上不會(huì)相互干擾。
關(guān)于zk:由于Codis是一個(gè)強(qiáng)依賴的zk的項(xiàng)目,而且在proxy和zk的連接發(fā)生抖動(dòng)造成sessionexpired的時(shí)候,proxy是不能對(duì)外提供服務(wù)的,所以盡量保證proxy和zk部署在同一個(gè)機(jī)房。生產(chǎn)環(huán)境中zk一定要是>=3臺(tái)的奇數(shù)臺(tái)機(jī)器,建議5臺(tái)物理機(jī)。
關(guān)于HA:這里的HA分成兩部分,一個(gè)是proxy層的HA,還有底層Redis的HA。先說proxy層的HA。之前提到過proxy本身是無(wú)狀態(tài)的,所以proxy本身的HA是比較好做的,因?yàn)檫B接到任何一個(gè)活著的proxy上都是一樣的,在生產(chǎn)環(huán)境中,我們使用的是jodis,這個(gè)是我們開發(fā)的一個(gè)jedis連接池,很簡(jiǎn)單,就是監(jiān)聽zk上面的存活proxy列表,挨個(gè)返回jedis對(duì)象,達(dá)到負(fù)載均衡和HA的效果。也有朋友在生產(chǎn)環(huán)境中使用LVS和HA Proxy來(lái)做負(fù)載均衡,這也是可以的。 Redis本身的HA,這里的Redis指的是codis底層的各個(gè)server group的master,在一開始的時(shí)候codis本來(lái)就沒有將這部分的HA設(shè)計(jì)進(jìn)去,因?yàn)镽edis在掛掉后,如果直接將slave提升上來(lái)的話,可能會(huì)造成數(shù)據(jù)不一致的情況,因?yàn)橛行碌男薷目赡茉趍aster中還沒有同步到slave上,這種情況下需要管理員手動(dòng)的操作修復(fù)數(shù)據(jù)。后來(lái)我們發(fā)現(xiàn)這個(gè)需求確實(shí)比較多的朋友反映,于是我們開發(fā)了一個(gè)簡(jiǎn)單的ha工具:codis-ha,用于監(jiān)控各個(gè)server group的master的存活情況,如果某個(gè)master掛掉了,會(huì)直接提升該group的一個(gè)slave成為新的master。 項(xiàng)目的地址是:https://github.com/ngaut/codis-ha。
關(guān)于dashboard:dashboard在codis中是一個(gè)很重要的角色,所有的集群信息變更操作都是通過dashboard發(fā)起的(這個(gè)設(shè)計(jì)有點(diǎn)像docker),dashboard對(duì)外暴露了一系列RESTfulAPI接口,不管是web管理工具,還是命令行工具都是通過訪問這些httpapi來(lái)進(jìn)行操作的,所以請(qǐng)保證dashboard和其他各個(gè)組件的網(wǎng)絡(luò)連通性。比如,經(jīng)常發(fā)現(xiàn)有用戶的dashboard中集群的ops為0,就是因?yàn)閐ashboard無(wú)法連接到proxy的機(jī)器的緣故。
關(guān)于go環(huán)境:在生產(chǎn)環(huán)境中盡量使用go1.3.x的版本,go的1.4的性能很差,更像是一個(gè)中間版本,還沒有達(dá)到production ready的狀態(tài)就發(fā)布了。很多朋友對(duì)go的gc頗有微詞,這里我們不討論哲學(xué)問題,選擇go是多方面因素權(quán)衡后的結(jié)果,而且codis是一個(gè)中間件類型的產(chǎn)品,并不會(huì)有太多小對(duì)象常駐內(nèi)存,所以對(duì)于gc來(lái)說基本毫無(wú)壓力,所以不用考慮gc的問題。
關(guān)于隊(duì)列的設(shè)計(jì):其實(shí)簡(jiǎn)單來(lái)說,就是「不要把雞蛋放在一個(gè)籃子」的道理,盡量不要把數(shù)據(jù)都往一個(gè)key里放,因?yàn)閏odis是一個(gè)分布式的集群,如果你永遠(yuǎn)只操作一個(gè)key,就相當(dāng)于退化成單個(gè)Redis實(shí)例了。很多朋友將Redis用來(lái)做隊(duì)列,但是Codis并沒有提供BLPOP/BLPUSH的接口,這沒問題,可以將列表在邏輯上拆成多個(gè)LIST的key,在業(yè)務(wù)端通過定時(shí)輪詢來(lái)實(shí)現(xiàn)(除非你的隊(duì)列需要嚴(yán)格的時(shí)序要求),這樣就可以讓不同的Redis來(lái)分擔(dān)這個(gè)同一個(gè)列表的訪問壓力。而且單key過大可能會(huì)造成遷移時(shí)的阻塞,由于Redis是一個(gè)單線程的程序,所以遷移的時(shí)候會(huì)阻塞正常的訪問。
關(guān)于主從和bgsave:codis本身并不負(fù)責(zé)維護(hù)Redis的主從關(guān)系,在codis里面的master和slave只是概念上的:proxy會(huì)將請(qǐng)求打到「master」上,master掛了codis-ha會(huì)將某一個(gè)「slave」提升成master。而真正的主從復(fù)制,需要在啟動(dòng)底層的Redis時(shí)手動(dòng)的配置。在生產(chǎn)環(huán)境中,我建議master的機(jī)器不要開bgsave,也不要輕易的執(zhí)行save命令,數(shù)據(jù)的備份盡量放在slave上操作。
關(guān)于跨機(jī)房/多活:想都別想。。。codis沒有多副本的概念,而且codis多用于緩存的業(yè)務(wù)場(chǎng)景,業(yè)務(wù)的壓力是直接打到緩存上的,在這層做跨機(jī)房架構(gòu)的話,性能和一致性是很難得到保證的
關(guān)于proxy的部署:其實(shí)可以將proxy部署在client很近的地方,比如同一個(gè)物理機(jī)上,這樣有利于減少延遲,但是需要注意的是,目前jodis并不會(huì)根據(jù)proxy的位置來(lái)選擇位置最佳的實(shí)例,需要修改。
四、對(duì)于分布式數(shù)據(jù)庫(kù)和分布式架構(gòu)的一些看法(one more Thing)
Codis相關(guān)的內(nèi)容告一段落。接下來(lái)我想聊聊我對(duì)于分布式數(shù)據(jù)庫(kù)和分布式架構(gòu)的一些看法。 架構(gòu)師們是如此貪心,有單點(diǎn)就一定要變成分布式,同時(shí)還希望盡可能的透明:P。就MySQL來(lái)看,從最早的單點(diǎn)到主從讀寫分離,再到后來(lái)阿里的類似Cobar和TDDL,分布式和可擴(kuò)展性是達(dá)到了,但是犧牲了事務(wù)支持,于是有了后來(lái)的OceanBase。Redis從單點(diǎn)到Twemproxy,再到Codis,再到Reborn。到最后的存儲(chǔ)早已和最初的面目全非,但協(xié)議和接口永存,比如SQL和Redis Protocol。
NoSQL來(lái)了一茬又一茬,從HBase到Cassandra到MongoDB,解決的是數(shù)據(jù)的擴(kuò)展性問題,通過裁剪業(yè)務(wù)的存儲(chǔ)和查詢的模型來(lái)在CAP上平衡。但是幾乎還是都丟掉了跨行事務(wù)(插一句,小米上在HBase上加入了跨行事務(wù),不錯(cuò)的工作)。
我認(rèn)為,拋開底層存儲(chǔ)的細(xì)節(jié),對(duì)于業(yè)務(wù)來(lái)說,KV,SQL查詢(關(guān)系型數(shù)據(jù)庫(kù)支持)和事務(wù),可以說是構(gòu)成業(yè)務(wù)系統(tǒng)的存儲(chǔ)原語(yǔ)。為什么memcached/Redis+mysql的組合如此的受歡迎,正是因?yàn)檫@個(gè)組合,幾個(gè)原語(yǔ)都能用上,對(duì)于業(yè)務(wù)來(lái)說,可以很方便的實(shí)現(xiàn)各種業(yè)務(wù)的存儲(chǔ)需求,能輕易的寫出「正確」的程序。但是,現(xiàn)在的問題是數(shù)據(jù)大到一定程度上時(shí),從單機(jī)向分布式進(jìn)化的過程中,最難搞定的就是事務(wù),SQL支持什么的還可以通過各種mysqlproxy搞定,KV就不用說了,天生對(duì)分布式友好。
于是這樣,我們就默認(rèn)進(jìn)入了一個(gè)沒有(跨行)事務(wù)支持的世界里,很多業(yè)務(wù)場(chǎng)景我們只能犧牲業(yè)務(wù)的正確性來(lái)在實(shí)現(xiàn)的復(fù)雜度上平衡。比如一個(gè)很簡(jiǎn)單的需求:微博關(guān)注數(shù)的變化,最直白,最正常的寫法應(yīng)該是,將被關(guān)注者的被關(guān)注數(shù)的修改和關(guān)注者的關(guān)注數(shù)修改放到同一個(gè)事務(wù)里,一起提交,要么一起成功,要么一起失敗。但是現(xiàn)在為了考慮性能,為了考慮實(shí)現(xiàn)復(fù)雜度,一般來(lái)說的做法可能是隊(duì)列輔助異步的修改,或者通過cache先暫存等等方式繞開事務(wù)。
但是在一些需要強(qiáng)事務(wù)支持的場(chǎng)景就沒有那么好繞過去了(目前我們只討論開源的架構(gòu)方案),比如支付/積分變更業(yè)務(wù),常見的搞法是關(guān)鍵路徑根據(jù)用戶特征sharding到單點(diǎn)MySQL,或者M(jìn)ySQLXA,但是性能下降得太厲害。
后來(lái)Google在他們的廣告業(yè)務(wù)中遇到這個(gè)問題,既需要高性能,又需要分布式事務(wù),還必須保證一致性:),Google在此之前是通過一個(gè)大規(guī)模的MySQL集群通過sharding苦苦支撐,這個(gè)架構(gòu)的可運(yùn)維/擴(kuò)展性實(shí)在太差。這要是在一般公司,估計(jì)也就忍了,但是Google可不是一般公司,用原子鐘搞定Spanner,然后再Spanner上構(gòu)建了SQL查詢層F1。我在第一次看到這個(gè)系統(tǒng)的時(shí)候,感覺簡(jiǎn)直驚艷,應(yīng)該是第一個(gè)可以真正稱為NewSQL的公開設(shè)計(jì)的系統(tǒng)。所以,BigTable(KV)+F1(SQL)+Spanner(高性能分布式事務(wù)支持),同時(shí)Spanner還有一個(gè)非常重要的特性是跨數(shù)據(jù)中心的復(fù)制和一致性保證(通過Paxos實(shí)現(xiàn)),多數(shù)據(jù)中心,剛好補(bǔ)全了整個(gè)Google的基礎(chǔ)設(shè)施的數(shù)據(jù)庫(kù)棧,使得Google對(duì)于幾乎任何類型的業(yè)務(wù)系統(tǒng)開發(fā)都非常方便。我想,這就是未來(lái)的方向吧,一個(gè)可擴(kuò)展的KV數(shù)據(jù)庫(kù)(作為緩存和簡(jiǎn)單對(duì)象存儲(chǔ)),一個(gè)高性能支持分布式事務(wù)和SQL查詢接口的分布式關(guān)系型數(shù)據(jù)庫(kù),提供表支持。
#p#
五、Q & A
Q1:我沒看過Codis,您說Codis沒有多副本概念,請(qǐng)問是什么意思?
A1:Codis是一個(gè)分布式Redis解決方案,是通過presharding把數(shù)據(jù)在概念上分成1024個(gè)slot,然后通過proxy將不同的key的請(qǐng)求轉(zhuǎn)發(fā)到不同的機(jī)器上,數(shù)據(jù)的副本還是通過Redis本身保證
Q2:Codis的信息在一個(gè)zk里面存儲(chǔ)著,zk在Codis中還有別的作用嗎?主從切換為何不用sentinel
A2:Codis的特點(diǎn)是動(dòng)態(tài)的擴(kuò)容縮容,對(duì)業(yè)務(wù)透明;zk除了存儲(chǔ)路由信息,同時(shí)還作為一個(gè)事件同步的媒介服務(wù),比如變更master或者數(shù)據(jù)遷移這樣的事情,需要所有的proxy通過監(jiān)聽特定zk事件來(lái)實(shí)現(xiàn) 可以說zk被我們當(dāng)做了一個(gè)可靠的rpc的信道來(lái)使用。因?yàn)橹挥屑鹤兏腶dmin時(shí)候會(huì)往zk上發(fā)事件,proxy監(jiān)聽到以后,回復(fù)在zk上,admin收到各個(gè)proxy的回復(fù)后才繼續(xù)。本身集群變更的事情不會(huì)經(jīng)常發(fā)生,所以數(shù)據(jù)量不大。Redis的主從切換是通過codis-ha在zk上遍歷各個(gè)server group的master判斷存活情況,來(lái)決定是否發(fā)起提升新master的命令。
Q3:數(shù)據(jù)分片,是用的一致性hash嗎?請(qǐng)具體介紹下,謝謝。
A3:不是,是通過presharding,hash算法是crc32(key)%1024
Q4:怎么進(jìn)行權(quán)限管理?
A4:Codis中沒有鑒權(quán)相關(guān)的命令,在reborndb中加入了auth指令。
Q5:怎么禁止普通用戶鏈接Redis破壞數(shù)據(jù)?
A5:同上,目前Codis沒有auth,接下來(lái)的版本會(huì)加入。
Q6:Redis跨機(jī)房有什么方案?
A6:目前沒有好的辦法,我們的Codis定位是同一個(gè)機(jī)房?jī)?nèi)部的緩存服務(wù),跨機(jī)房復(fù)制對(duì)于Redis這樣的服務(wù)來(lái)說,一是延遲較大,二是一致性難以保證,對(duì)于性能要求比較高的緩存服務(wù),我覺得跨機(jī)房不是好的選擇。
Q7:集群的主從怎么做(比如集群S是集群M的從,S和M的節(jié)點(diǎn)數(shù)可能不一樣,S和M可能不在一個(gè)機(jī)房)?
A7:Codis只是一個(gè)proxy-based的中間件,并不負(fù)責(zé)數(shù)據(jù)副本相關(guān)的工作。也就是數(shù)據(jù)只有一份,在Redis內(nèi)部。
Q8:根據(jù)你介紹了這么多,我可以下一個(gè)結(jié)論,你們沒有多租戶的概念,也沒有做到高可用??梢赃@么說吧?你們更多的是把Redis當(dāng)做一個(gè)cache來(lái)設(shè)計(jì)。
A8:對(duì),其實(shí)我們內(nèi)部多租戶是通過多Codis集群解決的,Codis更多的是為了替換twemproxy的一個(gè)項(xiàng)目。高可用是通過第三方工具實(shí)現(xiàn)。Redis是cache,Codis主要解決的是Redis單點(diǎn)、水平擴(kuò)展的問題。把codis的介紹貼一下: Auto rebalance Extremely simple to use Support both Redis or rocksdb transparently. GUI dashboard & admin tools Supports most of Redis commands. Fully compatible with twemproxy(https://github.com/twitter/twemproxy). Native Redis clients are supported Safe and transparent data migration, Easily add or remove nodes on-demand.解決的問題是這些。業(yè)務(wù)不停的情況下,怎么動(dòng)態(tài)的擴(kuò)展緩存層,這個(gè)是codis關(guān)注的。
Q9:對(duì)于Redis冷備的數(shù)據(jù)庫(kù)的遷移,您有啥經(jīng)驗(yàn)沒有?對(duì)于Redis熱數(shù)據(jù),可以通過migrate命令實(shí)現(xiàn)兩個(gè)Redis進(jìn)程間的數(shù)據(jù)轉(zhuǎn)移,當(dāng)然如果對(duì)端有密碼,migrate就玩完了(這個(gè)我已經(jīng)給Redis官方提交了patch)。
A9:冷數(shù)據(jù)我們現(xiàn)在是實(shí)現(xiàn)了完整的Redissync協(xié)議,同時(shí)實(shí)現(xiàn)了一個(gè)基于rocksdb的磁盤存儲(chǔ)引擎,備機(jī)的冷數(shù)據(jù),全部是存在磁盤上的,直接作為一個(gè)從掛在master上的。實(shí)際使用時(shí),3個(gè)group,keys數(shù)量一致,但其中一個(gè)的ops是另外兩個(gè)的兩倍,有可能是什么原因造成的?key的數(shù)量一致并不代表實(shí)際請(qǐng)求是均勻分布的,不如你可能某幾個(gè)key特別熱,它一定是會(huì)落在實(shí)際存儲(chǔ)這個(gè)key的機(jī)器上的。剛才說的rocksdb的存儲(chǔ)引擎:https://github.com/reborndb/qdb,其實(shí)啟動(dòng)后就是個(gè)Redis-server,支持了PSYNC協(xié)議,所以可以直接當(dāng)成Redis從來(lái)用。是一個(gè)節(jié)省從庫(kù)內(nèi)存的好方法。
Q10:Redis實(shí)例內(nèi)存占比超過50%,此時(shí)執(zhí)行bgsave,開了虛擬內(nèi)存支持的會(huì)阻塞,不開虛擬內(nèi)存支持的會(huì)直接返回err,對(duì)嗎?
A10:不一定,這個(gè)要看寫數(shù)據(jù)(開啟bgsave后修改的數(shù)據(jù))的頻繁程度,在Redis內(nèi)部執(zhí)行bgsave,其實(shí)是通過操作系統(tǒng)COW機(jī)制來(lái)實(shí)現(xiàn)復(fù)制,如果你這段時(shí)間的把幾乎所有的數(shù)據(jù)都修改了,這樣操作系統(tǒng)只能全部完整的復(fù)制出來(lái),這樣就爆了。
Q11:剛讀完,贊一個(gè)??煞窠榻B下codis的autorebalance實(shí)現(xiàn)。
A11:算法比較簡(jiǎn)單,https://github.com/wandoulabs/codis/blob/master/cmd/cconfig/rebalancer.go#L104。代碼比較清楚,code talks:)。其實(shí)就是根據(jù)各個(gè)實(shí)例的內(nèi)存比例,分配slot好的。
Q12:主要想了解對(duì)降低數(shù)據(jù)遷移對(duì)線上服務(wù)的影響,有沒有什么經(jīng)驗(yàn)介紹?
A12:其實(shí)現(xiàn)在codis數(shù)據(jù)遷移的方式已經(jīng)很溫和了,是一個(gè)個(gè)key的原子遷移,如果怕抖動(dòng)甚至可以加上每個(gè)key的延遲時(shí)間。這個(gè)好處就是對(duì)業(yè)務(wù)基本沒感知,但是缺點(diǎn)就是慢。