您好,登錄后才能下訂單哦!
本文根據徐海峰2018年5月12日在【第九屆中國數據庫技術大會】上的演講內容整理而成。
講師介紹:
徐海峰,花名:大嘴。10年互聯網經驗。現任閱文集團首席架構師、技術專家。主要負責閱文集團內容中心分布式系統的架構與實現、海量數據的分布式存儲與分布式計算。兼負責公司的專利、技術等開源布道。曾任ctrip國際機票計價引擎架構師、5173分布式存儲與計算架構師等工作。多年來一直專心致力于網站的分布式架構、海量數據存儲與計算等中間件的研究與實現,并形成有成型的技術認知與理論體系。對大型網站的架構與分布式系統有豐富的實戰經驗。
內容摘要:
通常的緩存系統(典型如memcahed)普遍都將數據內存化,而不支持持久化。縱使后來的Redis解決了數據無法持久化的“硬傷”,但通常緩存系統的持久化功能是否啟用也一直是一個讓人很糾結的問題。之所以糾結主要是幾個原因:1. 緩存系統啟用持久化后性能明顯下降;2. 數據開啟了持久化,但機器down機恢復后依然無法使用或者數據無法自動更新到最新版本;3. 主存依然是內存,所以數據大小需受制于內存,依然無法存儲比內存大的數據,故持久化僅僅是備份;4. 設計的時候沒考慮持久化,啟用持久化后使用非常別扭;而我們的lest從設計開始就解決了這些問題,并且還帶來了更多很有意思、也很有實用價值的技術,比如私有的通訊與存儲協議、全程無鎖的多線程模型等等。
正文演講:
今天主要是想和大家分享我們現在用的持久化緩存—lest,說是緩存,但因為是持久化,所以我個人認為稱作存儲系統可能更好一些,它確實是KV結構,并且包含了String、List、Map等等。目前已上線使用,支持1PB到2PB的數據。
講到緩存,在大家的印象中緩存像什么?其實緩存和神藥有很多的相同之處,首先它們都是為了解決“行不行”的問題,使用之后99%作用明顯,1%無作用,而且是立馬見效,通常會在幾分鐘或者幾小時失效,而且都是走的“治標治本”的路子,多級緩存,從客戶端到數據庫。
除此之外,二者的出發點都是為了穩定、快速和持久,通常用戶都是不管三七二十一,先用了再說,且還會產生心理依賴,領導對于其效果也會比較滿意,自己也感覺從苦逼碼農晉升到了金光閃閃的框架師。
雖然現實生活中我們很難拒絕使用緩存,但是緩存用多了也會出現很多問題,尤其是當數據量大和機器多了以后,各種問題就會接踵而至。例如現在的緩存基本都是內存式的,一斷電數據就沒有了,恢復起來也是相當困難。
做了主備之后,你會發現備機其實沒有什么用,主機宕了,切到備機上,很多數據都是不同步的,想要同步還需要時間。前兩天,我們還討論,主備好像沒什么用,還是多主比較好用。
最關鍵的問題就是很難管理。緩存用了之后就扔不了,只敢加量,不敢減量。緩存服務器越來越多,可能從一臺變成了兩臺、四臺、八臺……不僅管理成本越來越高,寫代碼也變得很復雜,因為很多緩存系統都會為了速度快而設在客戶端,所以,每增加一個機器,所有的客戶端都要配置,可能有的做得好的團隊會有配置系統自動完成,但要是做的不好的團隊,就需要重新發布一下程序,如果要是個新手,很可能還會給你寫成個死的。
所以,歸根結底還是要強身健體的,為了杜絕這些情況,我們實現了Lest。
首先就是緩存同步,可以做到擴容時無感知;第二,主機宕機了也能很容易的起來,備機可能需要稍微頂一下,但主機必須很快起來,因為我們的訪問基本上一天七、八億次,如果主機宕掉打到數據庫上,緩存穿透的話,那就基本上完了。所以我們采用了上述四個策略來解決了這個問題。
我們的緩存內容是String、List和Map。這其中List和Map的存儲比較難做,因為其包含有結構的數據。例如,如果要在List中查詢從第二個到第十個的數據,Redis很容易就做到了,但如果是全內存,存儲在磁盤上就比較困難。
所以,我們自己做了一些設計來實現,上圖中就是我們的總體架構圖。右上是Tracker,類似于很多大廠都在做的緩存代理層,接下來是存儲機器,操作機器會分段,如256段、128段等等,數據會分配到不同的段上去。通訊和存儲的實現,我們用了自己設計的協議。
負載均衡,其實是老生常談了,緩存的一個最大特點就是key要自定義。業務自定義因為要存儲到磁盤上,因此很難做類似元數據管理的工作。我們選擇的方式是Hash,不過使用Hash比較麻煩的地方是,如果機器增加的話,Hash值也會發生變化。所以,我們在增加機器的時候會有一個小竅門,以2*數字的方式去增加,比如一臺變兩臺,兩條邊四臺,四臺變八臺。這種方式同步量是最少的,50%,假設你是一臺變三臺,那么動的數據就是66.7%。如果大家是使用Hash,我建議大家用2*數字的擴容方式會比較好。
數據存儲下來之后,我們就需要同步,我們有組的標簽,同組之內可以數據同步,相互備份,它是沒有Slave的,全部都是主。這里會牽扯到版本問題,我們后面會講到。
負載均衡的算法就是二次Hash到加權二次Hash的演變,剛開始的時候,我們使用兩次Hash去做,第一次Hash得到段,第二次Hash得到是哪臺機器,但其實ID生成器生成的ID因為業務的關系并不均衡,這導致緩存的存儲量大小很偏,可能出現一臺機器中有20%,另一臺則有80%。
這種情況也很好處理,加權就可以了,相當于一致性Hash,每臺機器都有一個類似7%這樣的素數百分數加權。為什么選擇素數呢?這是因為對素數Hash會比較均勻。
我們在磁盤上做了一個256×256的文件夾,在磁盤層面就把一些文件打亂。我們知道磁盤對小文件其實是比較可怕的,因為使用SSD,我們現在的成本還是比較高,之后我們會考慮使用磁盤,會加類似B樹這樣子的東西。
目前,因為考慮到有很多小文件,選用了SSD,從而避免掉了磁盤會遇到的一些問題。如果是1億KB的數據,經過Hash放到一個文件夾中大概也只有幾百KB,不會超過3000KB,這個壓力還是可以接受的。
上圖是數據存儲的模型,最前面是Head,頭部加了很多元數據。比如整個是一個string,那么黃色的部分是客戶端存下來的真正內容,len表示長度,Version表示版本,我們整套都是用C來寫的,因此性能大概會提升十倍以上。并且我們還做了一個保證單調遞增的ID生成器,它的算法其實就是一個時間向量算法的衍生,解決了版本控制的問題,換句話說就是哪個數字最大,肯定就是最后的版本。Reserved代表類型,這里存的可能是string、list或者是map。同時為了未來的擴展,我們還會有一個預留出來的地方。
其實List和Map與String差不多,大家看圖即可,就不再一一介紹了。
這些存儲如果要手工實現可能有些困難,所以我們去做了一個HMS對象,這是一個支持全部類型的數據協議,包括int、long等等。最經典的使用方式是部署在服務器端,因為服務器端使用C無法像Java那樣反射,這時我們會用一個數學結構來代表整個內容,如果使用Key的話可以達到log N的查詢效率。
以上是我們做的API,幾乎可以支持所有的操作,用法與Redis差不多,Redis能做的操作,lest基本也能做。
同步架構如上圖,如果是同組的話,兩個storage之間到Tracker上拿到數據,然后再做同步,這其中還會涉及到一個高速IP協議。
每次記錄都會產生binlog,將binlog分門別類的記下來,然后去其上讀即可。
同步復制狀態,狀態轉移就是1+1大于等于2,控制簡單,傳輸數據量大,而復制狀態機,控制比較復雜,傳輸的數據其實很少。
上圖是我們第一次對lest做性能測試得到的結果。前段時間,我們申請到了新的機器,我們又重新做了一遍測試。
上圖是處理請求正確響應數,其中紅色的是萬兆服務器+SSD,黃色的是千兆服務器+SATA硬盤。得到的這個性能結果我個人認為還有提升空間,因為我們的客戶端不夠多,只有十臺,至少要有二三十臺才能壓出它的真實性能。現在的性能數據差不多是它真正性能的60%。
從圖中我們可以看到,當數據達到10K以上,性能其實一直在走下坡路,如果大家去對比Redis也會發現,當數據到10K,Redis性能也會下降45%。這說明緩存還是和小數據更合拍。
上圖是最大響應時間,圖中的822我也不知道怎么來的,可能是一個特別異常的值,除去這個值,其它數值的狀態還是比較平穩的,最大響應時間的單位是毫秒,基本上是在一兩百毫秒之間。
上圖是最小響應時間,其值基本分布在一點幾毫秒。
上圖是傳輸量,也就是網卡,很明顯,萬兆占據優勢,這是因為它本來就比千兆要大,其可能只打到了80%,還有10%的增長空間。
上圖是SSD服務器壓力,CPU的狀況差不多,如果十個服務器瘋狂打,那么CPU的壓力在20%左右,隨著數據變大,處理時間變多,CPU壓力就下降了。圖中還有網絡的出和入,其中入會比較小,而出會比較大,如果是Redis,出會更大。
Lest的優劣勢很明顯,它是一個吃磁盤不太吃內存的東西,具體優劣勢可參考上圖。相比來說,如果使用Redis服務器需要二三十臺的場景,lest三臺就可以扛下來。另外,相比其它緩存,lest基本可以做到寫代碼無感知,另外,我比較推薦使用SSD,因為現在SSD還是蠻便宜的,比內存要便宜。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。