99999久久久久久亚洲,欧美人与禽猛交狂配,高清日韩av在线影院,一个人在线高清免费观看,啦啦啦在线视频免费观看www

熱線電話:13121318867

登錄
首頁精彩閱讀如何面對PB級別數(shù)據(jù)的架構(gòu)變遷?
如何面對PB級別數(shù)據(jù)的架構(gòu)變遷?
2016-04-05
收藏

如何面對PB級別數(shù)據(jù)的架構(gòu)變遷?

面對PB級別數(shù)據(jù)存儲,我們一路走來也踩過很多坑,這里就直接進(jìn)入主題了,給大家分享一下監(jiān)控寶系統(tǒng)架構(gòu)變遷的兩個比較重要的點(diǎn)。

一、Redis的擴(kuò)展

我們面臨的第一個的問題是redis的擴(kuò)展,redis進(jìn)程無法使用多核,我們當(dāng)時的redis進(jìn)程并發(fā)1.5W,單 core CPU占用率95%,偶發(fā)會達(dá)到100%,監(jiān)控寶的任務(wù)調(diào)度會出現(xiàn)延遲現(xiàn)象。我們想到的方案如下:

方案1:改程序邏輯,基于任務(wù)ID的一致性hash支持redis多實(shí)例。 但是研發(fā)很忙(好吧,好像從來沒有閑過)。此方案只能放棄。

方案2:redis cluster. 當(dāng)時看到官方的架構(gòu)圖,我就直接將此方案放棄了。我們有大量的寫操作,如果每個點(diǎn)都同步寫操作,理論上瓶頸無法解決,不太適合我們的使用場景,而且大家的生產(chǎn)環(huán)境用這個的好像也不多。

redis cluster 的邏輯圖如下:

方案3:codis, Twemproxy.

最終我們選擇了codis。借此機(jī)會感謝一下豌豆莢的劉奇同學(xué),當(dāng)時遇到了一些問題,請教了劉奇同學(xué),總是能很快的得到解答,非常贊。目前codis 在線上穩(wěn)定運(yùn)行一年多,從未出現(xiàn)任何問題。QPS 已經(jīng)達(dá)到15萬次每秒。

架構(gòu)概覽

其架構(gòu)圖如下,整個架構(gòu)每一層都支持?jǐn)U展,并且無單點(diǎn)。

Codis有很多優(yōu)點(diǎn),讓我們選擇它的理由如下:

1、平滑遷移。Codis 提供了遷移工具,比較容易操作。我們生產(chǎn)環(huán)境已經(jīng)驗(yàn)證,從redis遷到codis 對業(yè)務(wù)影響為0,不過redis有些命令是不支持的,遷移之前還是要仔細(xì)讀下codis的文檔,看是否適合自己的生產(chǎn)環(huán)境。

2、擴(kuò)展容易。Codis將數(shù)據(jù)默認(rèn)分了1024個slot, 看到這個當(dāng)時就很開心, 以后基本不用擔(dān)心數(shù)據(jù)量的問題了。理論上是可以擴(kuò)展到1024個redis實(shí)例,擴(kuò)展的時候,先把新的redis實(shí)例加入到集群中,再將部分slot遷移 至新的實(shí)例中就可以了,包括后面將要提到的Mycat 2.0 也會采用這種設(shè)計(jì)。

3、友好的管理頁面,擴(kuò)展的操作直接就可以通過管理頁面做了。下面是遷移管理的圖:

而上面這幾點(diǎn)Twemproxy是不具備的。唯一的缺點(diǎn)就是稍稍復(fù)雜一些,入門的時候稍需要多花些時間。但相比其優(yōu)點(diǎn)這些都微不足道了。

二、使用Mysql處理PB級別的數(shù)據(jù)存儲

我們面臨的第二個問題是PB級別的數(shù)據(jù)存儲。

現(xiàn)實(shí)場景:以我們的網(wǎng)站監(jiān)控為例簡單的介紹下數(shù)據(jù)的情況。我們在全球分布有200+個監(jiān)測點(diǎn),這些監(jiān)測點(diǎn)按用戶設(shè)置的頻率訪問指定的網(wǎng)站頁面,監(jiān)測數(shù)據(jù)傳到我們的數(shù)據(jù)中心。這個服務(wù)目前有30多萬用戶在使用,平均數(shù)據(jù)日增量在1T以上。

這部分?jǐn)?shù)據(jù)類似于日志數(shù)據(jù),使用Mysql來存這些數(shù)據(jù)并不是什么高大上的做法,如果大家使用過ELK的話,會推薦我們使用elasticsearch來存儲這些日志數(shù)據(jù)。

好吧,的確是這樣,我們的新產(chǎn)品:透視寶、壓測寶、數(shù)據(jù)寶是有用到elasticsearch,也用到了hadoop、spark、suro、kafka,druid等大數(shù)據(jù)相關(guān)的框架應(yīng)用?;蛘咧苯邮褂梦募泶鎯σ彩强梢缘?。

數(shù)據(jù)庫的分庫分表問題

但老系統(tǒng)的問題還是要解決的。使用Mysql做大量數(shù)據(jù)存儲很容易就想到分庫分表,提到分庫分表自然就會想到Mysql的中間件。

大家在網(wǎng)站可以查到一些常用的分庫分表的中間件,包括大家比較熟悉的Atlas、Mycat(cobar)、TDDL、HEISENBERG、OCEANUS、VITESS、ONEPORXY、DRDS等,先不談這些中間件之間的區(qū)別,他們共有一個特性,只能在一個維度上對數(shù)據(jù)進(jìn)行拆分或者說只能對數(shù)據(jù)進(jìn)行一次拆分。

切分?jǐn)?shù)據(jù)庫分為垂直切分和水平切分,先介紹一個我們使用到的比較簡單的垂直切分場景:

垂直切分
幾個數(shù)據(jù)庫在同一個Mysql實(shí)例中,但因數(shù)據(jù)庫A 的IO相對較高,希望將其單獨(dú)拉到另外一臺服務(wù)器上。又不想讓研發(fā)改動代碼。

以前一直以為Mycat只能做水平切分,其實(shí)也可以垂直切分的,很實(shí)用,配置也很簡單,因各種原因希望將原來一個Mysql實(shí)例中的多個庫分布到多個實(shí)例中,直接使用Mycat就可以做到,對應(yīng)用程序來看還是同一個實(shí)例,在拆分過程可以通過主從同步實(shí)現(xiàn)平滑遷移。

水平切分

接下來介紹水平切分,水平切分是指將某個表按照某個字段按某種規(guī)則來分散到多個表之中,每個表中包含一部分?jǐn)?shù)據(jù)。

常用的根據(jù)主鍵或非主鍵的分片規(guī)則:


  1. 枚舉法:
    比如數(shù)據(jù)是按照地區(qū)省份來保存的,用戶通過多級別劃分的,本規(guī)則適用于這些特定的場景。
  2. 求模:
    如果分片字段為數(shù)字,對分片字段進(jìn)行十進(jìn)制/百進(jìn)制求模運(yùn)算,數(shù)據(jù)可以均勻落在各分片內(nèi)。也見過網(wǎng)友分享的對字符串hash取模,支持存在符號字母的字段的分片。
  3. 范圍約定
    對分片字段約定一個范圍,比如ID 100001~200000為一個分片,ID 200001~300000為一個分片
  4. 按日期
    可以按月,按天,按小時分片
  5. 一致性hash


一致性hash算法有效解決了分布式數(shù)據(jù)的擴(kuò)容問題。這個大家可以查下具體的算法實(shí)現(xiàn)。

以上是常用的幾種方式,也有一些分片方式是根據(jù)上面5種變化得來,比如對日期字段hash再分片的。

單獨(dú)使用一種分片規(guī)則是很難支撐大量數(shù)據(jù)的存儲,哪怕使用一致性hash在生產(chǎn)環(huán)境中也是很麻煩的事情,光是數(shù)據(jù)遷移就是一件讓運(yùn)維頭疼的事了,這時候我們需同時采用垂直分片和水平分片,甚至多次水平分片,下面聊一下我們在實(shí)際生產(chǎn)中如何使用這些分片規(guī)則的。

實(shí)例:

以我們API監(jiān)控為例, 看下應(yīng)用場景。

現(xiàn)在我們手機(jī)里安裝的是各種APP,其架構(gòu)中必然存在大量的API,我們的用戶不但要監(jiān)控單個API請求,還要監(jiān)控連續(xù)請求構(gòu)成的事務(wù),監(jiān)控寶API監(jiān)控的正確性是以斷言來判斷的。每個監(jiān)測點(diǎn)都會對用戶的API做出請求,請求數(shù)據(jù)及斷言的結(jié)果都將被存儲到數(shù)據(jù)中心。

我們借助于cobar, 對數(shù)據(jù)做了兩次分片。分片邏輯圖如下:

首先我們是通過cobar 采用枚舉法按監(jiān)測點(diǎn)ID對DB這層進(jìn)行了數(shù)據(jù)分片,這樣做的話物理上同一個監(jiān)測點(diǎn)的數(shù)據(jù)在一起,業(yè)務(wù)上也是好理解的。

其次,在程序邏輯中按天對表進(jìn)行了分片。每天都會有腳本將一月之內(nèi)的表都創(chuàng)建好。

擴(kuò)展衍生問題

從上圖中大家可以看到,這里擴(kuò)展上是存在問題的!


  1. 我們一共有200多個監(jiān)測點(diǎn)。在第一階段,數(shù)據(jù)量沒有那么大的時候,為了節(jié)約成本,我們僅使用了10臺機(jī)器做存儲,一臺機(jī)器存有多個監(jiān)測點(diǎn)的數(shù)據(jù)
  2. 隨著數(shù)據(jù)量增大,發(fā)展到第二階段,當(dāng)某臺機(jī)器硬盤快存滿的時候,我需要將一些監(jiān)測點(diǎn)的數(shù)據(jù)遷移至新增進(jìn)集群的機(jī)器中,按這個架構(gòu),最多我們可以擴(kuò)展到200+臺機(jī)器。
  3. 在生產(chǎn)環(huán)境中用戶對北上廣的監(jiān)測點(diǎn)是最有興趣的,所以這些監(jiān)測點(diǎn)的數(shù)據(jù)量是最大的。 這樣就會發(fā)展到第三階段,部分監(jiān)測點(diǎn)的數(shù)據(jù)量大到單臺機(jī)器的硬盤存不下了。


這時候如何解決問題呢,我們來分析一下數(shù)據(jù),單個數(shù)據(jù)庫中是按日期來分表的,而大于3個月的歷史數(shù)據(jù)較少有人查詢,用戶也可以接受歷史數(shù)據(jù)查詢時間稍長一些,于是我們選用了TokuDB來壓縮歷史數(shù)據(jù),基本上1T的數(shù)據(jù)壓縮之后在100G左右。

1:10的壓縮例,即使這樣,理論上最多也只能存儲4P左右的數(shù)據(jù)(數(shù)據(jù)放在UCLOUD上,云主機(jī)支持的最大硬盤為2T)。

解決方案

我們在網(wǎng)站監(jiān)控的數(shù)據(jù)分庫中解決了這個問題,邏輯圖如下,我們從4個維度對數(shù)據(jù)進(jìn)行了分片。

1、按日期為第一維度對數(shù)據(jù)庫分片,必須按日期做第一次分片,并且分片時間點(diǎn)可以在配置文件中自定義。

2、按監(jiān)測點(diǎn)ID為第二維度對數(shù)據(jù)庫分片

3、按實(shí)際分片數(shù)量對任務(wù)ID動態(tài)取模為第三維度對數(shù)據(jù)庫分片。

4、對任務(wù)ID 100取模為第四維度對數(shù)據(jù)表分片。

創(chuàng)建后的數(shù)據(jù)庫類名似于db-201501-monitor01-01、 db-201501-monitor01-02 ……  每個庫是有100張表。這樣可以的好處:

1、冷熱數(shù)據(jù)自然分離。

2、可以根據(jù)日期無限次分片。

3、在同一個時間段里實(shí)際分片數(shù)可以自定義。

注意:理論上可以無限次分片。每次分片服務(wù)器的數(shù)量是可控的,并且下次分片的時間也變的可預(yù)期??梢栽谧畲蟪潭仁枪?jié)約成本。

4、數(shù)據(jù)無需遷移

細(xì)心的同學(xué)會發(fā)現(xiàn)這樣對數(shù)據(jù)分片造成一個小問題,我們對任務(wù)ID做了兩次取模。會造成部分實(shí)例中的某些表中數(shù)據(jù)是空的,不過并不影響應(yīng)用。

互動問答

Q1:這么多次分片,都是mycat分的吧?請問總共有多少記錄量?

并不都通過mycat分片,有些是程序支持的,特別是第一次分片,我們需要能在指定的時間點(diǎn)分片。 總記錄量我們沒有統(tǒng)計(jì),我們知道目前每秒的寫入為2萬QPS。

Q2:一致性hash中 rehash對業(yè)務(wù)影響?

我們的業(yè)務(wù)中并沒有用到一致性hash,主要是評估遷移量的時候,覺得數(shù)據(jù)量太大,不太適合我們的應(yīng)用場景。

Q3:為了提高IO并發(fā),每個DB都是用單獨(dú)的磁盤嗎?RAID5?RAID10?

我們使用的是UCLOUD的云盤,他們的IOPS可以支持到 隨機(jī)寫1600QPS,據(jù)我了解,他們的底層是通過8塊硬盤作raid10.

Q4:codis group中master slave的作用是什么?為什么這么用?

作用是為了保障高可用性,當(dāng)某個redis實(shí)例節(jié)點(diǎn)出現(xiàn)故障可以實(shí)現(xiàn)master-slave的角色切換,我們當(dāng)時用到的版本中切換是需要手動觸發(fā)的。他們也提供了一個腳本來實(shí)現(xiàn)。

Q5:所有的數(shù)據(jù)庫全存放在一個物理機(jī)房嗎?機(jī)房出故障的情況怎么應(yīng)對?

對于跨機(jī)房容災(zāi)的問題,我們是借助于云平臺來做的,目前的知名云平臺都有多個機(jī)房,并且之間都是光纖打通的。PING值在10ms以內(nèi),基本可以當(dāng)內(nèi)網(wǎng)使用。另外,我們從年前開始啟用了多云布署的方案,多云布署,聊起來有點(diǎn)長了。

Q6:后期codis會實(shí)現(xiàn)如果group中一個redis實(shí)例掛掉,會不會自動踢出該實(shí)例啊

這個問題在問題4中已經(jīng)回答了。我使用的版本還不會自動切換和踢出集群。但當(dāng)時劉奇說過要在后面的版本實(shí)現(xiàn),因?yàn)槲覀冇凶约鹤稣{(diào)用他們提供的切換腳本了,就沒在關(guān)注后面的版本。大家關(guān)心的話,可以看下codis 的官方文檔。

Q7:codis中的master和slave并不是實(shí)質(zhì)上的主從吧,只是切換的一種狀態(tài)而已,底層redis實(shí)例是需要進(jìn)行主從設(shè)置的?

codis本身是通過redis的一個版本二次開發(fā)的,本質(zhì)上這個主從關(guān)系就是通過redis的主從功能實(shí)現(xiàn)的。

Q8:master slave切換能說的稍微詳細(xì)嗎?高可用?怎么做的?

這個問題還是接上圖吧。

這個是操作界面上的可以手動操作。另外還有一個腳本可以實(shí)現(xiàn)。

數(shù)據(jù)分析咨詢請掃描二維碼

若不方便掃碼,搜微信號:CDAshujufenxi

數(shù)據(jù)分析師資訊
更多

OK
客服在線
立即咨詢
客服在線
立即咨詢
') } function initGt() { var handler = function (captchaObj) { captchaObj.appendTo('#captcha'); captchaObj.onReady(function () { $("#wait").hide(); }).onSuccess(function(){ $('.getcheckcode').removeClass('dis'); $('.getcheckcode').trigger('click'); }); window.captchaObj = captchaObj; }; $('#captcha').show(); $.ajax({ url: "/login/gtstart?t=" + (new Date()).getTime(), // 加隨機(jī)數(shù)防止緩存 type: "get", dataType: "json", success: function (data) { $('#text').hide(); $('#wait').show(); // 調(diào)用 initGeetest 進(jìn)行初始化 // 參數(shù)1:配置參數(shù) // 參數(shù)2:回調(diào),回調(diào)的第一個參數(shù)驗(yàn)證碼對象,之后可以使用它調(diào)用相應(yīng)的接口 initGeetest({ // 以下 4 個配置參數(shù)為必須,不能缺少 gt: data.gt, challenge: data.challenge, offline: !data.success, // 表示用戶后臺檢測極驗(yàn)服務(wù)器是否宕機(jī) new_captcha: data.new_captcha, // 用于宕機(jī)時表示是新驗(yàn)證碼的宕機(jī) product: "float", // 產(chǎn)品形式,包括:float,popup width: "280px", https: true // 更多配置參數(shù)說明請參見:http://docs.geetest.com/install/client/web-front/ }, handler); } }); } function codeCutdown() { if(_wait == 0){ //倒計(jì)時完成 $(".getcheckcode").removeClass('dis').html("重新獲取"); }else{ $(".getcheckcode").addClass('dis').html("重新獲取("+_wait+"s)"); _wait--; setTimeout(function () { codeCutdown(); },1000); } } function inputValidate(ele,telInput) { var oInput = ele; var inputVal = oInput.val(); var oType = ele.attr('data-type'); var oEtag = $('#etag').val(); var oErr = oInput.closest('.form_box').next('.err_txt'); var empTxt = '請輸入'+oInput.attr('placeholder')+'!'; var errTxt = '請輸入正確的'+oInput.attr('placeholder')+'!'; var pattern; if(inputVal==""){ if(!telInput){ errFun(oErr,empTxt); } return false; }else { switch (oType){ case 'login_mobile': pattern = /^1[3456789]\d{9}$/; if(inputVal.length==11) { $.ajax({ url: '/login/checkmobile', type: "post", dataType: "json", data: { mobile: inputVal, etag: oEtag, page_ur: window.location.href, page_referer: document.referrer }, success: function (data) { } }); } break; case 'login_yzm': pattern = /^\d{6}$/; break; } if(oType=='login_mobile'){ } if(!!validateFun(pattern,inputVal)){ errFun(oErr,'') if(telInput){ $('.getcheckcode').removeClass('dis'); } }else { if(!telInput) { errFun(oErr, errTxt); }else { $('.getcheckcode').addClass('dis'); } return false; } } return true; } function errFun(obj,msg) { obj.html(msg); if(msg==''){ $('.login_submit').removeClass('dis'); }else { $('.login_submit').addClass('dis'); } } function validateFun(pat,val) { return pat.test(val); }