自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

攜程持久化KV存儲挑戰(zhàn)Redis,狂省90%成本……

開發(fā) 前端 Redis
過去幾年,攜程技術(shù)保障部門在Redis治理方面做了很多工作,解決了運營上的問題,在私有云上也積累了豐富的經(jīng)驗。后又通過引入Kvrocks,在公有云上實現(xiàn)降本增效的目的,從而支撐了公司的國際化戰(zhàn)略。

一、背景

過去幾年,攜程技術(shù)保障部門在Redis治理方面做了很多工作,解決了運營上的問題,在私有云上也積累了豐富的經(jīng)驗。后又通過引入Kvrocks,在公有云上實現(xiàn)降本增效的目的,從而支撐了公司的國際化戰(zhàn)略。

與此同時,國內(nèi)業(yè)務(wù)部門存在降低基礎(chǔ)建設(shè)成本的客觀需要,有些業(yè)務(wù)方期望提供一種非傳統(tǒng)關(guān)系數(shù)據(jù)庫來解決某些高性能海量空間的業(yè)務(wù)需求,并在此基礎(chǔ)上支持特殊定制化以面對后疫情時代的挑戰(zhàn)。這些變化使我們開始思考,是不是可以參考公有云上的思路,在私有云上構(gòu)建一種持久化數(shù)據(jù)庫,來滿足業(yè)務(wù)方對高性能、低成本、海量、持久化的需求。

二、面對的問題

回顧之前在公有云上的方案,目的明確。因為公有云的內(nèi)存較貴,我們將Redis的數(shù)據(jù)存在SSD上來降低成本,選型了Kvrocks,并自研實現(xiàn)支持Redis的復(fù)制協(xié)議,將公有云上的成本降低了60%(圖1)。 

圖1 

隨著業(yè)務(wù)發(fā)展和Redis集群的日益增長,需求更加多樣化,需要在私有云上同樣能有一種持久化的KV存儲系統(tǒng)來提供服務(wù),包括:

  1. KV存儲和讀寫的場景,Redis能提供的存儲上限過低,需要有大容量的KV存儲系統(tǒng);
  2. 數(shù)據(jù)持久化,而不是像Redis那樣重啟數(shù)據(jù)即丟失;
  3. 節(jié)約Redis的使用成本,畢竟私有云上的Redis集群非常龐大;
  4. 提供類似selectforudpate的語義來實現(xiàn)庫存之類字段的扣減,而不是依賴外部的一些組件,比如分布式鎖;
  5. 數(shù)據(jù)能提供相比Redis更高的一致性,比如支持同步復(fù)制。

我們仔細分析業(yè)務(wù)需求和業(yè)界可選的方案,以期望找到一種持久化的KV數(shù)據(jù)庫,能兼容Redis滿足大容量和成本降低的需求,而又不局限于Redis,能提供更多樣化的能力來支撐業(yè)務(wù)的訴求。

三、調(diào)研和選擇

我們調(diào)研了業(yè)界大部分的NoSQL/NewSQL數(shù)據(jù)庫,主要考慮以下幾個方面。

是否為業(yè)界主流

主流有兩層含義:第一,是否流行,比如github上的star數(shù),是否是頂級開源基金的項目,或是否有大廠背書;第二,其理念是否主流,如現(xiàn)在使用最廣的關(guān)系型數(shù)據(jù)庫MySQL,以及NewSQL TiDB,其相關(guān)概念如半同步復(fù)制,GTID,raft,計算存儲分離等概念都比較深入人心。

是否有成熟的中間件

中間件成熟是非常重要的一種能力,一旦選擇了一種不合適的數(shù)據(jù)庫,中間件相關(guān)的路由,打點,監(jiān)控,降級,熔斷,DR切換等每一項都需要投入大量的人力物力來做,此外穩(wěn)定的中間件也是需要長時間打磨才能被業(yè)務(wù)方信賴,如果能復(fù)用現(xiàn)有中間件的大部分能力,能節(jié)約大量人力物力。

集群運維治理配套是否完善

選擇一種KV數(shù)據(jù)庫,除了中間件外,治理相關(guān)的如集群擴容,縮容,實例的遷移,資源利用率等一樣要考慮進來。無論哪種數(shù)據(jù)庫,部署后的運維治理相關(guān),能復(fù)用現(xiàn)有的能力最好,如果不能復(fù)用,需要考慮:擴容到10倍需要多久時間,是否可以縮容?是否好遷移,對業(yè)務(wù)透明?大規(guī)模部署后,資源利用率是否可以提升?

性能是否滿足要求,是否支持10X的擴展

上面說的這幾點,如果都滿足,但性能不滿足或者不支持10X擴展,那也將一票否決。性能也是重要考量的一塊,希望找到一種性能優(yōu)異的KV數(shù)據(jù)庫。

是否可以二次開發(fā),獨立演進

對于攜程這樣體量或相似體量的公司來說,持久化KV的數(shù)據(jù)庫大多有自研的或基于開源二次開發(fā)的數(shù)據(jù)庫,比如美團的Cellar,餓了么的Tidis,360的pika等,我們同樣需要選擇一種易于二次開發(fā)或方便擴展的數(shù)據(jù)庫,來開發(fā)自定義的特性支撐業(yè)務(wù)。

調(diào)研的過程受制于篇幅限制,不再一一展開,最終我們繼續(xù)選擇了Kvrocks來作為治理演進的對象,其他的NoSQL/NewSQL有各種不足,而Kvrocks受益于Redis運維治理的成熟,可以復(fù)用現(xiàn)有的大部分Redis中間件和運維治理的能力,在攜程與Redis幾乎無差異的部署/使用方式,當(dāng)下無疑是最適合的一種持久化KV數(shù)據(jù)庫。

四、從Kvrocks到TRocks

經(jīng)過不斷的開發(fā)迭代和使用,最終我們將新系統(tǒng)命名為TRocks(Trip+Kvrocks),作為攜程自己的持久化KV數(shù)據(jù)庫。相比于原來的Kvrocks,除了與Redis可以互通協(xié)議互為主從外,主要是基于以下幾個方面的改進。

1、功能增強

獨占鎖

一些業(yè)務(wù)方存在著流程協(xié)調(diào),執(zhí)行順序的限制,往往會需要使用分布式鎖,比如扣減庫存的邏輯。常見的方式是引入一個第三方的分布式系統(tǒng),將鎖標(biāo)識存儲在那里用于共享訪問,以達到鎖的目的。

這樣做雖然常見,但也有一些問題,首先需要引入額外的系統(tǒng),并單獨考慮各種異常情況的處理,增加了整個應(yīng)用的復(fù)雜度。其次標(biāo)識位往往有一定含義或者能與當(dāng)前業(yè)務(wù)數(shù)據(jù)做關(guān)聯(lián),這就相當(dāng)于額外存儲了一份業(yè)務(wù)數(shù)據(jù),存在一定的安全隱患。同時多個應(yīng)用可能共用一套外部分布式系統(tǒng)來處理鎖,這就無形中增加了系統(tǒng)的訪問壓力,一旦出現(xiàn)問題將影響多個依賴方,缺乏隔離性。

為了解決此類問題,TRocks在內(nèi)部實現(xiàn)了基于Key力度的鎖功能,將其分布式部署并作為應(yīng)用的業(yè)務(wù)數(shù)據(jù)庫時,其本身就擁有了分布式鎖的能力(圖2)。對鎖的處理和業(yè)務(wù)數(shù)據(jù)在一起,無需引入多余的系統(tǒng),降低復(fù)雜度,幫助業(yè)務(wù)方專注于業(yè)務(wù)代碼的開發(fā)。

 


圖2 

為了保證請求的唯一性和類似raft那樣支持冪等重試的功能,每個請求需要帶上標(biāo)識唯一性的clientid和自增seq,這些metadata和本身的data會被當(dāng)成一個writebatch寫入到rocksdb中,后續(xù)還會同步到slave上,從而保證整條鏈路上請求的原子性。

復(fù)合命令

由于Redis命令本身的限制,有些業(yè)務(wù)方反饋實現(xiàn)一個功能,比如對hash key進行超時處理需要進行2次操作,一次設(shè)置值,一次設(shè)置超時。雖然中間件將這層邏輯封裝之后對外只提供一個api,但內(nèi)部執(zhí)行仍然是2個命令,可能存在原子性問題。TRocks針對這種情況增加了一些復(fù)合功能的命令,調(diào)用這些命令可以實現(xiàn)相同的效果并保證原子性,同時這些功能對用戶是透明的,直接調(diào)用客戶端相應(yīng)api即可使用。

2、可用性增強

可調(diào)一致性

Kvrocks本身的主從復(fù)制邏輯與Redis相似,都是通過異步方式進行的。在這種方式下,如果出現(xiàn)網(wǎng)絡(luò)斷開或者master宕機,數(shù)據(jù)還未來得及同步,就會出現(xiàn)數(shù)據(jù)丟失的情況。為了避免此類問題,TRocks加入了類似Mysql的半同步復(fù)制來提高數(shù)據(jù)的一致性。我們可以通過打開半同步方式并指定至少需要參與的半同步slave的數(shù)量來啟用該功能,提高災(zāi)備能力。

例如一個1主4從的集群,設(shè)定需要等待任意2臺Slave響應(yīng)。

如圖3所示,當(dāng)滿足響應(yīng)的slave為2的時候,半同步即可認為完成,即使此時另外兩臺slave可能還未完成同步工作。 

圖3 

但這種方式在多機房部署的情況仍然可能存在問題。因為距離的關(guān)系,相同機房的數(shù)據(jù)傳輸速率會更高,所以master復(fù)制到和其在同一個機房的slave通常情況會更快(圖4)。 

圖4 

這樣就很容易發(fā)生同機房的slave數(shù)據(jù)復(fù)制的進度要快于異地機房的slave。如果發(fā)生機房級的故障,導(dǎo)致master所在集群的服務(wù)全都無法正常工作,這個時候就可能發(fā)生數(shù)據(jù)丟失。

為此我們在半同步復(fù)制的基礎(chǔ)上增加了IDC模式,使得即使初始條件已經(jīng)滿足,也需要至少存在相關(guān)IDC的slave反饋才能完成整個復(fù)制流程。IDC模式有兩種,本地復(fù)制和異地復(fù)制。

以異地模式為例,如果返回slave的數(shù)量滿足條件,且包含至少一臺來自于master所在機房不同的slave,則半同步復(fù)制完成。如果當(dāng)前響應(yīng)中未包含非master集群的slave,則繼續(xù)等待,直到master接收到一臺來自異地的slave的反饋,半同步才能完成(圖5)。 

圖5 

盡管異地模式數(shù)據(jù)的安全性更高,但也會影響整個系統(tǒng)的性能,這個性能差正常情況下取決于不同機房之間的網(wǎng)絡(luò)延遲?;趯π阅芎蛿?shù)據(jù)可用性的不同要求,使用方可以酌情選擇全異步復(fù)制(即關(guān)閉半同步),半同步 & 半同步(本地)復(fù)制或者半同步(異地)復(fù)制。

全量同步復(fù)制抑制

上面說到異步復(fù)制在異常情況下可能存在數(shù)據(jù)缺失的情況,如果再加上運維系統(tǒng)對主從關(guān)系的調(diào)整,就會發(fā)生數(shù)據(jù)沖突。而我們目前TRocks的版本還在快速迭代中,希望每次升級版本能夠?qū)τ脩敉该?,然而事實并非如此?/p>

假設(shè)存在master A和slave B,正常情況下A和B的數(shù)據(jù)是保持一致的(綠色部分),但當(dāng)A發(fā)生宕機的時候,B可能還未同步到A的最新數(shù)據(jù),這時B的數(shù)據(jù)不再增加。但隨后哨兵發(fā)現(xiàn)master無法訪問,就把B提升為master并開始處理寫入數(shù)據(jù)(藍色部分)。當(dāng)一段時間后,A系統(tǒng)恢復(fù),重新加入進集群,此時A會變?yōu)閙asterB的slave,并嘗試從B中同步數(shù)據(jù),這里就可能存在沖突區(qū)(圖6)。 

圖6 

按照Kvrocks初始的復(fù)制邏輯,A會認為自身數(shù)據(jù)存在問題,并放棄全部數(shù)據(jù)然后從頭開始進行全量同步B的數(shù)據(jù)。這個行為本身沒有問題。然而實際生產(chǎn)環(huán)境下,如果數(shù)據(jù)量很大的話,全量同步的耗時會比較長,而硬盤相比內(nèi)存的帶寬至少小兩個數(shù)量級,因為我們的實例都是容器化部署,這有可能導(dǎo)致災(zāi)難性的后果,A在同步數(shù)據(jù)的時候會產(chǎn)生大量的IO,從而可能會影響A/B所在的宿主機上的所有的實例。

在數(shù)據(jù)一致性要求沒有那么高的場景中,僅僅因為可能的幾條數(shù)據(jù)不一致就重新同全量同步,代價非常昂貴。所以我們希望在非強一致性條件下,系統(tǒng)可以容忍極少量的數(shù)據(jù)差異,盡可能避免全量同步以便充分利用資源。

我們的方案是當(dāng)檢測到數(shù)據(jù)不一致的時候,主從之間會進行交互協(xié)調(diào),計算出沖突區(qū)的范圍,并從沖突區(qū)之后第一條數(shù)據(jù)開始進行同步。為什么不是直接從沖突區(qū)后面開始同步?這里需要有個概念,TRocks/Kvrocks的數(shù)據(jù)都是追加形式的,增刪改都會在log文件中追加一條記錄,并提供起始位置(Sequence),對應(yīng)不同的Redis類型的記錄會有不同的長度(Count),比如一條SET指令對應(yīng)的Sequence會累加1,而HSET指令會累加2。

從Sequence到Sequence+Count就是一條記錄的數(shù)據(jù)范圍。當(dāng)重新同步的時候,沖突區(qū)的結(jié)束位置如果處于正常數(shù)據(jù)的中間,這樣是沒有辦法取得完整數(shù)據(jù)的,所以需要從沖突區(qū)后第一條數(shù)據(jù)開始。 

圖7 

而沖突區(qū)與同步開始之間的區(qū)域是補足區(qū)(圖7),我們通過插入空白數(shù)據(jù)來進行填補,所以對于A和B來說,他們之間不一致區(qū)域是沖突區(qū)和補足區(qū)的總和。而對于沖突的部分,我們會記錄下兩邊的差異,真有差異發(fā)生時,參考git解決沖突的思路,將數(shù)據(jù)的選擇權(quán)交給用戶。

上線該feature后,版本的升級就變得比較輕松,大部分情況下版本升級只是一次實例的拉出重啟拉入,實例也是秒級up,升級過程也基本上對業(yè)務(wù)做到了透明。

在解決此問題的同時,我們也注意到master/slave數(shù)據(jù)是對齊的某些情況下也會發(fā)生全量同步,檢查下來發(fā)現(xiàn)是pub/sub命令的問題。

這個命令是哨兵用于訂閱服務(wù)消息的,但Kvrocks的pub/sub是一個寫操作,這樣就會造成持續(xù)性的數(shù)據(jù)寫入從而累加rocksdb的Sequence,這樣如果一個slave宕機后恢復(fù),還沒來得及與master同步卻被哨兵寫入了一條無關(guān)緊要的pub消息,累加了Seq從而觸發(fā)了不必要的全量同步,但實際上該功能并非必須,所以我們修改Kvrocks處理哨兵pubsub消息的規(guī)則,不去寫之后這個命令只工作在內(nèi)存中,自然不會累加rocksdb的Sequence,杜絕這種情況全量同步的可能性。

3、運維治理能力增強

水平擴縮容 

圖8 

在之前的Redis治理演進之路文章中,我們介紹了一種新的擴縮容方案來解決Redis集群版本升級和擴縮容的問題(圖8),參考同樣的思路,我們繼續(xù)改造BinlogServer來實現(xiàn)TRocks的集群的水平擴縮容,這套方案實際上不僅解決了擴縮容的問題,同時也解決了Redis到Redis的數(shù)據(jù)遷移,TRocks到TRocks的數(shù)據(jù)遷移,Redis與TRocks之間的互相遷移,也可以幫助用戶平滑的從Redis的訪問過渡到TRocks的訪問。

然而相比Redis擴縮容基本不需要考慮內(nèi)存帶寬,硬盤帶寬太窄,而數(shù)據(jù)遷移的時候流量太大。由于所有數(shù)據(jù)最終都需要在新集群上刷盤,導(dǎo)致遷移過程中目標(biāo)集群的磁盤讀寫會非常大,又由于我們都是容器化部署,大量的磁盤讀寫也可能會影響到統(tǒng)一宿主機上的其他無關(guān)的應(yīng)用,所以我們調(diào)整了TRocks的寫入限流設(shè)置,以避免大量寫入影響磁盤性能,同時修改了BinlogServer加入了限流功能,平緩數(shù)據(jù)傳輸?shù)乃俾省?/p>

哨兵多機房部署

為了保證TRocks集群可以跨機房容災(zāi),哨兵需要部署在多個機房中,目前我們是三機房部署。如下圖(圖9): 

圖9 

在部署的時候,遇到了一個問題,我們發(fā)現(xiàn)哨兵之間經(jīng)常無法選出leader,需要等下一個選舉周期(6分鐘)才能重新選出,導(dǎo)致長時間無法確定TRocks master。這個問題本身跟TRocks沒有太大關(guān)系,只是實際使用中對我們故障處理帶來了不小麻煩。

出現(xiàn)無法選出leader的原因是多個哨兵同時發(fā)起選舉希望成為leader,導(dǎo)致最終每個哨兵都選擇了自己,無法達成共識。查看源碼發(fā)現(xiàn)官方已經(jīng)為發(fā)起選舉前設(shè)置了隨機的間隔時間(50~100ms),但實際操作中發(fā)現(xiàn)這個隨機間隔反而增加了發(fā)生選角失敗的可能,考慮應(yīng)該是隨機時間太短導(dǎo)致,所以我們將隨件間隔修改為100~200ms,同時在哨兵發(fā)現(xiàn)master宕機之后就立即發(fā)起選舉來盡可能規(guī)避無法選主的問題。

五、一些數(shù)據(jù)

1、性能數(shù)據(jù)

TRocks在內(nèi)網(wǎng)上線后,在各個業(yè)務(wù)線都得到了廣泛的使用,排除公有云的部分,私有云上已經(jīng)有將近2K的實例,10T+的數(shù)據(jù)量,下圖(圖10,圖11)可以看到同樣的數(shù)據(jù)寫TRocks和Redis的性能對比。平均響應(yīng)時間,99.9%在同一個水平,并且我們還可以看到,得益于自定義的命令,同樣的功能相比Redis更加簡潔。 

圖10 

圖11 

根據(jù)我們跟業(yè)務(wù)方壓測,一臺40C和2塊RAID0的SATA SSD在保證良好響應(yīng)的前提下(99.9%<10ms)約能提供讀寫的QPS為8-10W,其中value<1k。而如果換成NVME SSD這個QPS可以提升3-5倍。

2、成本數(shù)據(jù)

假設(shè)TRocks都是容器化部署,并且一臺40C的宿主機上可以部署20個實例,每個實例大小為40G,因為TRocks相比Redis有不小的壓縮功能(約3-7倍的壓縮率),如果將Redis的數(shù)據(jù)導(dǎo)過來可以平穩(wěn)運行,那么TRocks相比Redis約可以節(jié)約90%的成本。

既然能省這么多成本,是否所有的Redis都可以用TRocks來代替,我們是否需要將私有云上所有的Redis都替換成為TRocks?

答案都是否定的,也不是我們推廣TRocks的初衷,原因有以下兩點:

  • 如上文所提到的,我們希望TRocks能擁有Redis的大部分能力,而又不僅僅局限于Redis,希望它更是一個通用的KV數(shù)據(jù)庫,能提供更多樣化的能力來支撐業(yè)務(wù)的訴求。
  • 硬盤的帶寬與內(nèi)存有2個數(shù)量級的差距,而這些先天不足也無法滿足某些Redis場景的需求。比如大Key(>100K)響應(yīng)和Redis還是有一定的差距,此外某些數(shù)據(jù)量小并且單個實例訪問QPS較高的實例,用TRocks來替換也并不合適,因為規(guī)?;\維治理,我們需要考慮整個宿主機和每個實例是否能平穩(wěn)運行,一般來說單個實例>10G,QPS<5K 是比較適合的。當(dāng)然NVME SSD可以極大縮短大Key的響應(yīng)時間和提升單個實例QPS的上限。

六、未來規(guī)劃

1、復(fù)合命令增強

我們調(diào)研發(fā)現(xiàn),業(yè)務(wù)經(jīng)常為了獲取一條數(shù)據(jù),需要多次查詢TRocks,類似二度人脈的取數(shù)據(jù)邏輯,多次的網(wǎng)絡(luò)IO會導(dǎo)致耗時增加,而設(shè)計通用的命令來支持業(yè)務(wù)需求,減少網(wǎng)絡(luò)IO變得非常重要,此外還有些用戶詢問TRocks的hash類型中的subkey是否也可以實現(xiàn)過期。由于hash功能目前仍然是遵照Redis的規(guī)則,所以現(xiàn)在是按照整個hash key一起過期而不能實現(xiàn)內(nèi)部數(shù)據(jù)項的過期。這個需求是有一定價值的,未來我們會通過提供一個特殊的hash結(jié)構(gòu)來實現(xiàn)此類功能。

2、引入checkpoint

Kvrocks1.X在進行全量復(fù)制時,master會生成硬的backup,會拷貝文件產(chǎn)生大量的IO,而官方2.0版本已經(jīng)用Rocksdbcheckpoint解決了這個問題,我們也已經(jīng)將2.0版本merge過來測試,準(zhǔn)備適時升級上線。

3、使用NVME SSD

目前攜程的大量TRocks還是跑在SATA接口的SSD上,而據(jù)我們的測試下來兩塊SATA raid0的帶寬大約為800MB/S,導(dǎo)致硬盤非常容易跑滿,相比之下,NVME SSD的帶寬基本都是幾G起步,并且我們測試下來NVME SSD在小的壓力下,對于SATA SSD性能有3-5倍的提升,而對于大Key的情況(超過100K)和大的壓力下,NVME SSD的性能提升可以高達10-100倍。因此我們已經(jīng)計劃將SATA SSD全換成NVME SSD,進一步提升TRocks的性能。

 

責(zé)任編輯:未麗燕 來源: 攜程技術(shù)
相關(guān)推薦

2023-11-17 10:03:45

攜程開源

2022-06-17 09:42:20

開源MMKV攜程機票

2023-06-13 14:55:04

2022-08-20 07:46:03

Dynamo攜程數(shù)據(jù)庫

2014-12-25 17:51:07

2025-03-18 12:23:25

2023-08-18 10:49:14

開發(fā)攜程

2014-03-24 10:12:22

2023-10-12 13:01:29

Redis數(shù)據(jù)庫

2020-03-03 14:15:49

Redis持久化數(shù)據(jù)庫

2022-11-29 08:05:48

KubernetesPVCSI

2020-07-28 18:03:10

戴爾

2024-03-26 00:03:08

Redis數(shù)據(jù)RDB

2024-12-20 12:15:06

RedisRDB持久化

2020-02-18 16:14:33

RedisRDBAOF

2021-10-04 21:11:18

Redis混合持久化

2023-05-11 09:12:35

RedisRDB日志

2023-07-07 12:19:43

攜程技術(shù)

2022-07-15 12:58:02

鴻蒙攜程華為
點贊
收藏

51CTO技術(shù)棧公眾號