海量ES數(shù)據(jù)存儲與平滑遷移方案

1.背景

本人所在的部門是主要負(fù)責(zé)的職責(zé)就是根據(jù)活動的玩法來發(fā)相關(guān)的禮品杀狡,我們常見的禮品有:xxx券抒抬,券碼,以及一些其他合作方的券

對于這些禮品的發(fā)放記錄我這邊數(shù)據(jù)量是比較大的挤安,一天預(yù)估的是500w的量挡篓,那么一年下來就會達(dá)到18億的數(shù)據(jù)量,這些記錄保留一年恬砂,所以說峰值最高會有18億的數(shù)據(jù)量咧纠,這些數(shù)據(jù)存放可以用Mysql分庫分表。

但是現(xiàn)在有個問題是僅僅存放是完全可以的泻骤,但是如果要查詢呢漆羔?

對于簡單的查詢根據(jù)分片id能查,但是對于一些復(fù)雜的多維度查詢瞪讼,mysql分庫分表是不方便的钧椰,實(shí)現(xiàn)了性能也會很差。所以我們這邊就打算把數(shù)據(jù)存放在ES中符欠。下面是目前對于禮品記錄寫和查的方案

采用雙寫方案先寫DB嫡霞,寫成功后再異步發(fā)kafka,寫ES

1703227554875.png

查詢

要求實(shí)時性高希柿、查詢簡單的就走DB诊沪,復(fù)雜多維度的就走ES

1703227595498.png

2.現(xiàn)狀問題

在方案的設(shè)計之初,對這個方案的考慮不夠全面曾撤,導(dǎo)致出現(xiàn)下面的問題:

  1. 單ES索引數(shù)據(jù)量巨大:單個索引占用110G端姚,不符合es索引推薦20-50G的范圍

  2. mapping設(shè)計不合理:存在不需要分詞但是仍然分詞的、以及多余的字段

  3. 沒有清理數(shù)據(jù)任務(wù):歷史廢數(shù)據(jù)一致存在挤悉,隨著時間推移會越來越大渐裸,影響性能

針對上面的問題,這次打算用合理的方案把它優(yōu)化掉

3.新架構(gòu)

1703227754387.png

1.架構(gòu)描述

舊的整體架構(gòu)是在左邊装悲,這個架構(gòu)也就會存在上述的數(shù)據(jù)量大等因?yàn)?strong>數(shù)據(jù)都存在一個索引的問題昏鹃。

那么經(jīng)過架構(gòu)的設(shè)計升級,最新的禮品發(fā)放記錄讀寫架構(gòu)變成了右圖诀诊,主要改動點(diǎn):

  1. mapping設(shè)計:重新設(shè)計mapping

    • 減少不必要的字段

    • 針對不需要分詞的字段改成keyword類型

    • 重新設(shè)置shard數(shù)量

  2. 讀:es用別名的方式洞渤,多個分片索引對應(yīng)同一個別名(user_award_index_all),這樣查的時候都統(tǒng)一查user_award_index_all的索引即可

  3. 寫:根據(jù)索引月份分片后属瓣,每次寫入當(dāng)前月對應(yīng)的的索引分片载迄,例如現(xiàn)在是2023年12月,那么就會寫入到user_award_index_202312索引

  4. 通過定時任務(wù)抡蛙,定期清理過期的分片索引

2.創(chuàng)建索引和刪除過期索引定時任務(wù)

這個定時任務(wù)每周跑一次护昧,會創(chuàng)建這個月和下個月的索引,以及刪除上一年本月-1過期索引溜畅。當(dāng)然如果當(dāng)前需要創(chuàng)建的索引已經(jīng)存在就不會再創(chuàng)建捏卓,這樣每周跑一次,保證了創(chuàng)建當(dāng)前月和下個月的索引,以及刪除舊索引能重試3次怠晴。

例如:當(dāng)前是23年12月遥金,那么就會創(chuàng)建user_awrad_index_2312,和user_awrad_index_2401的索引蒜田,

同時會刪除上一年本月-1過期索引(user_awrad_index_2211)稿械,也就是保留一年的數(shù)據(jù)。

3.ES Alias(別名)介紹

作為本篇文章中最重要的知識點(diǎn)之一冲粤,對于es的別名機(jī)制這里簡單說明一下:

別名可以看作是索引的一個指向美莫,它提供了一種靈活而可靠的方式來管理和操作索引。下面是ES別名機(jī)制的幾個優(yōu)勢:

  1. 簡化索引操作:通過使用別名梯捕,您可以將復(fù)雜的索引操作(如創(chuàng)建厢呵、刪除、重命名等)進(jìn)行簡化傀顾。例如襟铭,當(dāng)需要更換底層索引時,只需更新別名指向新索引即可短曾,而不需要更改應(yīng)用程序代碼或配置文件寒砖。

  2. 實(shí)現(xiàn)無縫切換:由于別名提供了指向具體索引的邏輯名稱,因此可以輕松地在不影響應(yīng)用程序和用戶訪問的情況下切換底層索引嫉拐。這對于數(shù)據(jù)遷移哩都、版本升級或故障恢復(fù)等場景非常有用。

  3. 支持滾動升級:當(dāng)您需要對大型索引進(jìn)行結(jié)構(gòu)變更或調(diào)整時婉徘,別名機(jī)制使得滾動升級成為可能漠嵌。您可以先創(chuàng)建一個新版本的索引,并將別名指向該新版本盖呼。然后逐步將舊版本中的數(shù)據(jù)導(dǎo)入到新版本中献雅,在此過程中保持服務(wù)連續(xù)可用。

  4. 多租戶支持:通過使用別名塌计,您可以為每個租戶或用戶創(chuàng)建獨(dú)立的視圖,并將其映射到特定的索引侯谁。這樣可以實(shí)現(xiàn)安全性和隔離性锌仅,并方便地控制每個租戶或用戶所能訪問和操作的數(shù)據(jù)范圍。

3.優(yōu)勢

  1. 每個分片索引的大小能控制在20-50G內(nèi)墙贱,符合ES的規(guī)范標(biāo)準(zhǔn)热芹,提高讀寫性能

  2. 刪除數(shù)據(jù)的時候,只需要將別名移除惨撇,再把過期索引整個刪除即可伊脓,不需要在同一個大索引操作,減少引起ES性能問題的可能

由于之前的禮品發(fā)放記錄采用的是舊的方案魁衙,那么需要轉(zhuǎn)變成新的方案报腔,就涉及到數(shù)據(jù)的遷移株搔,那么怎么遷移呢?

4.遷移方案

1.方案評估

在做數(shù)據(jù)遷移方案的時候纯蛾,有以下這些考慮的點(diǎn):

1.是否停機(jī)遷移纤房?

2.如何把舊遷移遷移到新數(shù)據(jù)?

3.數(shù)據(jù)的新增翻诉、更新如何應(yīng)對炮姨?

4.出問題了如何回滾?

5.如何校對數(shù)據(jù)遷移的準(zhǔn)確性碰煌?

上面這些考慮點(diǎn)也是做數(shù)據(jù)遷移繞不過的考慮的點(diǎn)舒岸,那么針對本人的業(yè)務(wù)場景一一分析:

1.是否停機(jī)?

我們的業(yè)務(wù)是不允許停機(jī)來做數(shù)據(jù)遷移的芦圾,目前發(fā)獎峰值QPS在500蛾派,并且發(fā)獎的這個業(yè)務(wù)如果停機(jī)對于C端的業(yè)務(wù)是不能接受的

2.如何遷移舊數(shù)據(jù)到新數(shù)據(jù)?

遷移數(shù)據(jù)就是需要從老的user_award_index遷移到分片的user_award_index_2023xx 堕扶,那么遷移方式有2種:

1.采用定時任務(wù)碍脏,深度分頁查詢es數(shù)據(jù),通過scroll分頁的查詢稍算,將數(shù)據(jù)同步過去

2.使用es的自帶遷移命令:reindex典尾,這個原理是通過異步任務(wù),也是和方式1的scroll深度分頁查詢一樣糊探,深 度分頁查詢出來后钾埂,同步到目標(biāo)索引

這里使用es的reindex會更加方便,支持的功能也比較豐富科平,同時它是異步分頁處理任務(wù)褥紫,不影響先上的讀寫。而且es有getTask命令瞪慧,用于查詢reindex相關(guān)的任務(wù)髓考,這種可視化命令的方式也提供了不少便利。

3.數(shù)據(jù)的新增弃酌、更新如何應(yīng)對?

我們的場景都是寫操作氨菇,有記錄了就往ES里面寫,沒有更新的數(shù)據(jù)妓湘,所以這里遷移數(shù)據(jù)處理起來會更簡單查蓉,直接覆蓋即可。

4.出問題了如何回滾榜贴?

我們的方案采用雙寫豌研,然后切讀,再關(guān)舊索引的寫,最后刪除老的索引鹃共,去掉用于切換雙寫等開關(guān)代碼

所以在切讀以后如果出問題了鬼佣,那么可以立馬把讀切回讀老的索引。

5.如何校對數(shù)據(jù)遷移的準(zhǔn)確性及汉?

  1. 條數(shù)對比:使用es的命令查詢新舊索引的條數(shù)沮趣,然后對比,這里需要注意不能直接比對實(shí)時全量的數(shù)據(jù)坷随,因?yàn)樾吕纤饕龑慐S的速度不一樣房铭,執(zhí)行統(tǒng)計索引數(shù)量的時間也不一致,完全實(shí)時的數(shù)量是不一定一致的温眉。所以我們這邊是對比某個時間點(diǎn)之前的全量數(shù)據(jù)條數(shù)缸匪。

  2. 數(shù)據(jù)性對比:禮品發(fā)放記錄的索引數(shù)據(jù)是相對簡潔,沒有太復(fù)雜的邏輯类溢,只需要抽樣一些數(shù)據(jù)對比是否有差異

2.遷移流程

1.遷移流程圖

1703227848670.png

2.上線雙寫

這一步上線寫新老索引的代碼凌蔬,同時加上寫新老索引的開關(guān),方便后續(xù)出問題后切換

 // 寫老的es
          String oldUserAwardEsWriteSwitch = sysConfigHelper.getByKeyWithDefaultVal(CommDefConstants.OLD_USER_AWARD_ES_WRITE_SWITCH, "1");
          if (Objects.equals("1", oldUserAwardEsWriteSwitch)) {
              esUtil.batchAddListData(CommDefConstants.EsIndex.USER_AWARD_RECORD_INDEX_OLD, esBatchList);
          }
  
          // 寫新的es
          try {
              String newUserAwardEsWriteSwitch = sysConfigHelper.getByKeyWithDefaultVal(CommDefConstants.NEW_USER_AWARD_ES_WRITE_SWITCH, "0");
              if (Objects.equals("1", newUserAwardEsWriteSwitch)) {
                  esUtil.batchAddListData(getSliceUserAwardIndexName(YearMonth.now()), esBatchList);
              }
          } catch (Exception e) {
              log.error(SfJsonUtil.toJsonStr(esBatchList), e);
          }

OLD_USER_AWARD_ES_WRITE_SWITCH為寫老es索引的開關(guān)

NEW_USER_AWARD_ES_WRITE_SWITCH為寫新es索引的開關(guān)闯冷,這里try-catch住了砂心,防止寫新索引報錯影響正常的業(yè)務(wù)

3.遷移數(shù)據(jù):reindex

下面是遷移的命令:

curl -u xxx:xxx -X POST 'xxxxxx:9200/_reindex' \
  -H 'Content-Type: application/json' \
  --d '{
      "source":{
          "index":"user_award_record_index",
          "size":5000,
          "_source":[
              "amount",
              "channel",
              "createTm",
              "detail",
              "userId",
              "errorMsg",
              "errorCode"
          ],
            "query": {
        "range": {
          "createTm": {
            "gte": "2022-12-07T18:00:00.000",
            "lte": "2023-12-08T00:00:00.000"
          }
        }    
      }
      },
      "dest":{
          "index":"slice_of_user_award_index_202312"
      },
  }'

souce: 表示源索引,也就是數(shù)據(jù)的來源

dest: 表示目標(biāo)索引蛇耀,表示需要遷移到的目標(biāo)索引

size:表示每次批量處理的數(shù)據(jù)量大小

query: 查詢需要遷移的數(shù)據(jù)

es的reindex還有其他很多參數(shù)辩诞,例如下面:

當(dāng)涉及到 Elasticsearch 的 reindex API 參數(shù)時,下面是詳細(xì)介紹:

  1. "script":允許在重新索引期間對文檔進(jìn)行轉(zhuǎn)換或修改的腳本纺涤∫朐荩可以使用不同的編程語言如 Painless、Groovy 等編寫自定義腳本來處理文檔內(nèi)容撩炊。

  2. "size":用于分批處理數(shù)據(jù)時指定每個批處理操作中要復(fù)制的文檔數(shù)量外永。默認(rèn)為 1000。

  3. "refresh":控制在每次重新索引操作后是否自動刷新目標(biāo)索引以使更改立即生效拧咳〔ィ可以設(shè)置為 true 表示自動刷新,或者 false 表示禁用刷新骆膝。

  4. "wait_for_completion":決定是否等待重新索引過程完成后再返回響應(yīng)砾淌。默認(rèn)為 true,表示等待完成谭网;而設(shè)置為 false 會立即返回響應(yīng)并在后臺執(zhí)行重建操作。

  5. "requests_per_second":限制重新索引操作每秒允許執(zhí)行的請求數(shù)量赃春。這有助于平衡性能和資源消耗愉择。例如,"requests_per_second": 1000 將限制每秒不超過 1000 個請求。

  6. "conflicts":定義如何解決可能出現(xiàn)的沖突情況(當(dāng)源和目標(biāo)之間存在相同 ID 的文檔)锥涕≈愿辏可以選擇忽略沖突、替換目標(biāo)文檔或更新目標(biāo)文檔层坠。

我目前的場景對于沖突用的是默認(rèn)更新殖妇,其他的參數(shù)就暫時用不少了,對于復(fù)雜的遷移可能會用上破花。

這里也提供一下查詢reindex的任務(wù)命令

這樣就能看出當(dāng)前的reindex任務(wù)執(zhí)行進(jìn)度谦趣,會包含需要遷移的數(shù)據(jù)量、當(dāng)前遷移的數(shù)量座每、創(chuàng)建時間等前鹅。

4.驗(yàn)證數(shù)據(jù)

驗(yàn)證數(shù)據(jù)就是查詢新老索引的數(shù)量進(jìn)行對比,然后抽查一下數(shù)據(jù)的情況峭梳。

  -H 'Content-Type: application/json' \
  -d '{
    "query": {
        "range": {
          "createTm": {
            "gte": "2022-12-09T18:00:00.000",
            "lte": "2023-12-11T10:40:00.000"
          }
        }    
      }
  }'

5.切讀

String readFlag = sysConfigHelper.getByKeyWithDefaultVal(CommDefConstants.USER_AWARD_ES_READ_FLAG, "old");
BoolQueryBuilder boolBuilder;
boolean old = "old".equals(readFlag);
if (old) {
    boolBuilder = genBoolQueryBuilder(req);
 } else {
    boolBuilder = boolQueryBuilder(req);
 }

切讀就是通過USER_AWARD_ES_READ_FLAG開關(guān)來控制讀新索引還是老索引舰绘,這里比較簡單

6.停止舊索引寫入

觀察幾天,當(dāng)線上流量讀的流量驗(yàn)證讀無問題后葱椭,那么就說明新的索引運(yùn)行穩(wěn)定了捂寿,就可以把寫舊索引關(guān)了。這里直接把OLD_USER_AWARD_ES_WRITE_SWITCH開關(guān)改成0即可孵运,同時把老的索引刪除秦陋。

5.成果

  1. 平滑遷移0故障、0BUG: 此次遷移未造成任何生產(chǎn)影響或者問題

  2. 存儲占用減少:由于重新設(shè)計過了mapping掐松,不需要分詞的數(shù)據(jù)去掉了分詞踱侣,也減少了不必要字段,整體的索引容量減少了一倍大磺。2億2千條數(shù)據(jù)從之前的110G到了56G

  3. 性能提升:查詢性能相較之前提升了30%抡句,平均耗時從125ms下降到93ms,這里是由于es集群本身沒優(yōu)化好杠愧,導(dǎo)致新老索引的整體耗時偏高

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
禁止轉(zhuǎn)載待榔,如需轉(zhuǎn)載請通過簡信或評論聯(lián)系作者。
  • 序言:七十年代末流济,一起剝皮案震驚了整個濱河市锐锣,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌绳瘟,老刑警劉巖雕憔,帶你破解...
    沈念sama閱讀 212,599評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異糖声,居然都是意外死亡斤彼,警方通過查閱死者的電腦和手機(jī)分瘦,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,629評論 3 385
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來琉苇,“玉大人嘲玫,你說我怎么就攤上這事〔⑸龋” “怎么了去团?”我有些...
    開封第一講書人閱讀 158,084評論 0 348
  • 文/不壞的土叔 我叫張陵,是天一觀的道長穷蛹。 經(jīng)常有香客問我土陪,道長,這世上最難降的妖魔是什么俩莽? 我笑而不...
    開封第一講書人閱讀 56,708評論 1 284
  • 正文 為了忘掉前任旺坠,我火速辦了婚禮,結(jié)果婚禮上扮超,老公的妹妹穿的比我還像新娘取刃。我一直安慰自己,他們只是感情好出刷,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,813評論 6 386
  • 文/花漫 我一把揭開白布璧疗。 她就那樣靜靜地躺著,像睡著了一般馁龟。 火紅的嫁衣襯著肌膚如雪崩侠。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 50,021評論 1 291
  • 那天坷檩,我揣著相機(jī)與錄音却音,去河邊找鬼。 笑死矢炼,一個胖子當(dāng)著我的面吹牛系瓢,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播句灌,決...
    沈念sama閱讀 39,120評論 3 410
  • 文/蒼蘭香墨 我猛地睜開眼夷陋,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了胰锌?” 一聲冷哼從身側(cè)響起骗绕,我...
    開封第一講書人閱讀 37,866評論 0 268
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎资昧,沒想到半個月后酬土,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,308評論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡格带,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,633評論 2 327
  • 正文 我和宋清朗相戀三年撤缴,在試婚紗的時候發(fā)現(xiàn)自己被綠了东揣。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,768評論 1 341
  • 序言:一個原本活蹦亂跳的男人離奇死亡腹泌,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出尔觉,到底是詐尸還是另有隱情凉袱,我是刑警寧澤,帶...
    沈念sama閱讀 34,461評論 4 333
  • 正文 年R本政府宣布侦铜,位于F島的核電站专甩,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏钉稍。R本人自食惡果不足惜涤躲,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 40,094評論 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望贡未。 院中可真熱鬧种樱,春花似錦、人聲如沸俊卤。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,850評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽消恍。三九已至岂昭,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間狠怨,已是汗流浹背约啊。 一陣腳步聲響...
    開封第一講書人閱讀 32,082評論 1 267
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留佣赖,地道東北人恰矩。 一個月前我還...
    沈念sama閱讀 46,571評論 2 362
  • 正文 我出身青樓,卻偏偏與公主長得像茵汰,于是被迫代替她去往敵國和親枢里。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,666評論 2 350

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