天天看點

HBase實戰 | 從MySQL到HBase:資料存儲方案轉型的演進

作者介紹

楊宏志,知乎首頁架構負責人,主要負責首頁工程化建設、工程架構優化、性能提升等工作。知乎專欄:

https://zhuanlan.zhihu.com/c_195355141

本文轉載自dbaplus社群(ID:dbaplus)

本文大緻會從以下幾個方面入手,談談筆者對資料存儲方案選型的看法:

  • 從MySQL到HBase叢集化方案的演化
  • MySQL與HBase的性能取舍
  • 不同方案的優化思路
  • 總結

一.叢集化方案

1.MySQL應用的演化

MySQL與HBase說到最核心的點,是一種資料存儲方案。方案本身沒有對錯、沒有好壞,隻有合适與否。相信多數公司都與MySQL有着不解之緣,部分學校的課程甚至直接以SQL語言作為資料庫講解。我想借自身經曆,先來談談MySQL應用的演化。

隻有MySQL

筆者之前曾在一家O2O創業公司工作,公司所有資料都存儲在同一個MySQL裡,而且沒有任何主備方案。相信這是很多初創公司會用到的一個典型解決辦法,當時這台MySQL為使用者、訂單、物流服務,同時也為線下分析服務。

HBase實戰 | 從MySQL到HBase:資料存儲方案轉型的演進

單執行個體的問題:

一旦MySQL挂了,服務全部停止;

一旦MySQL的磁盤壞了,公司的所有服務都沒有了(一般會定時備份資料檔案)。

主從方案

随着業務增加,單個DB是無法承載這麼多請求的。于是就有了主從複制、讀寫分離的解決方案。

HBase實戰 | 從MySQL到HBase:資料存儲方案轉型的演進

master隻負責寫請求,slave同步master用來服務讀請求:

  • 為了擴充讀能力可以增加多個slave;
  • 允許slave同步有一定的延遲;
  • 一緻性要求嚴格的,可以指定讀主庫。

主從功能的問題:

  • 需要增加管理Proxy層,配置設定寫請求、讀請求;
  • 節點故障:其它節點應該快速接管故障節點的功能。

垂直拆分

業務繼續增長,master甚至無法承載所有的寫請求,資料庫需要按業務拆分

HBase實戰 | 從MySQL到HBase:資料存儲方案轉型的演進

垂直拆分的問題:

線下分析,需要在業務代碼裡join各個表。因為拆成多個庫,已經無法join了。

不容易做資料庫的事務性,使用者餘額減少與下單成功的情況下無法使用MySQL的事務功能。

水準拆分

業務繼續增長,訂單表有大量的并發寫入,而且已經有了幾千萬行資料。

單個庫無法承載大量的并發寫入;

上千萬行的大表,資料寫入可能需要調整一棵巨大的B+樹;

上千萬行,B+樹過深,讀寫需要更多的磁盤IO;

很多老資料通路較少,B+樹上層緩存的部分資訊無用;

……

參考:大衆點評訂單系統分庫分表實踐

https://zhuanlan.zhihu.com/p/24036067
HBase實戰 | 從MySQL到HBase:資料存儲方案轉型的演進

水準分庫/分表帶來的問題:

  • 維護map方案;
  • 輔助索引隻能局部有效;
  • 由于分庫,無法使用join等函數;由于分表count、order、group等聚合函數也無法做了;
  • 擴容:需要再次水準拆分的:修改map,遷移資料……

2.MySQL的問題

MySQL的主要瓶頸,單機單程序。CPU有限、記憶體/磁盤功能、連接配接數有限、網卡吞吐有限……

叢集的限制點:

  • 關系型資料庫,縱向的外鍵互相join;

範式參考連結:

https://zhuanlan.zhihu.com/p/20028672
  • 資料庫事務性,基于單機的鎖機制,無法擴充到叢集中使用;
  • 全局有序列性基于B+樹,資料有序聚合存儲,叢集化後無法保證;

資料本地存儲,擴容需要遷移資料。

叢集的方案:

  • 放棄部分功能,輔助索引檢索、join、全局事務性、聚合函數等;
  • 水準拆分:存儲KV化,用機械的map思路實作叢集;
  • 擴容方案:手動導資料,開發資料遷移腳本;
  • 事務性:兩階段事務、paxos、單庫事務……
  • 備份容災:從節點同步主節點,但有一定的資料延遲;
  • 服務穩定性:主節點挂了,Proxy會将從節點更新為主節點;從節點挂了會被其它從節點替換。

3.HBase叢集化解決方案

region:拆分後的子表;

Region Server:管理這些資料的server,相當于一個MySQL執行個體;

.META.表存儲拆分資訊map。

HBase實戰 | 從MySQL到HBase:資料存儲方案轉型的演進

單個region過大,RegionServer會将region均分為兩個(自動、手工),然後更新.META.表。

擴容方案

  • RegionServer向HMaster彙報狀态。HMaster為RegionServer負載均衡,調整其負責的region 。
  • 增/删RegionServer後,會為重新調整region的配置設定方式。

服務穩定性

RegionServer隻是計算單元,挂掉後Hmaster可以随便再找一個節點代替壞節點服務。

事務性

HBase隻保證行級事務,單行資料肯定存在同一台機器(單機事務很好做)。

備份容災

  • 資料使用HDFS存儲,多複本,任何一個複本挂掉都不影響功能;
  • RegionServer隻是計算單元,挂掉後不影響服務。

二.性能取舍

1.資料請求流程

HBase:

  • Client會通過Zookeeper定位到 .META. 表;
  • 根據 .META. 查找需要服務的RegionServer,連接配接RegionServer進行讀寫;
  • Client會緩存 .META. 表資訊,下次可以直接連到RegionServer 。

MySQL:

  • Client通過Proxy,查找需要連接配接的MySQL執行個體,連接配接并進行讀寫。
  • Rquest的路由流程,MySQL與HBase基本一緻,那麼RegionServer與MySQL的性能差異如何呢?

**2.HBase寫得快

新增**

為什麼MySQL建議自增主鍵?(MySQL随機插入的代價)

  • 主鍵索引是有序的B+樹結構,新增條目的ID肯定是最大的,新增給B+結構帶來的調整最小;
  • 主鍵索引是聚簇的:新增條目,ID是最大的。其data追加在上一次插入的後面,磁盤更容易順序寫。

輔助索引,插入基本是随機的:

  • 插入條目,可能會引起B+樹結構很大的調整。

HBase可以随機插入:

  • HBase的所有插入隻是寫入記憶體memstore,隻保證記憶體資料的有序即可(很快、很容易);
  • 為防止資料丢失寫入memstore前,先寫入wal(可以關閉,速度更快);
  • HBase沒有輔助索引需要維護;
  • memstore寫滿了,申請一塊新的記憶體,舊的memstore被背景線程刷盤,存入HFile。

**修改

MySQL資料變化引起存儲變動:**

  • 資料塊大小變化:磁盤空間不足,可能需要調整磁盤存儲結構,引起大量的磁盤随機讀寫;
  • 輔助索引發生變化:可能需要重新調整輔助索引B+樹。
  • HBase直接将變化寫入到memstore,沒有其它開銷。

删除

MySQL資料删除:

  • 直接操作B+樹的節點,肯定需要重新整理磁盤;
  • 如果引起樹結構變化,甚至可能需要多次重新整理磁盤。

HBase隻是在memstore記錄删除标記,沒有其它開銷。

3.結論

HBase寫入記憶體+背景刷盤(最多是WAL,磁盤順序寫);MySQL需要維護B+樹,大量的磁盤随機讀寫。

MySQL要求盡量追加寫(自增 ID),速度較慢;HBase可以随機插入,速度很快。

MySQL讀得快

MySQL資料是本地存儲的,HBase是基于HDFS,有可能資料不在本地。

B+ 樹天然的全局有序

  • 根據主鍵查詢,可以快速定位到資料所在磁盤塊,隻需要極少的磁盤IO即可拿到資料:通過緩存高層節點,主健查詢隻需要一次磁盤IO就可拿到資料;MySQL單表行數一般建議不會超過2千萬,千萬行以下的大表,B+樹隻需2~3層即可;
  • 輔助索引,提供快速定位能力:輔助索引B+樹,可以快速定位到最終所需的主鍵ID,根據主鍵ID可以快速拿到所需資訊。

**HBase隻有局部資訊,沒有輔助索引

**

  • 查詢會優先查找memstore,如果沒有會查找Hfile(存儲結構類似B+樹)。如果第一個Hfile中沒有所需的資訊,則需要去第二、第三個Hfile中查詢;如果查詢的資料恰好在memstore,第一個Hfile,HBase會優于MySQL;平均下來,HBase讀性能一般。減少Hfile資料以提速,小的HFile合并成大的HFile檔案。這種存儲結構叫LSM樹(Log-structured merge-tree);
  • 如果需要檢索特定的列,可能需要周遊所有Hfile,成本巨高。

MySQL成也B+,敗也B+;HBase成也LSM,敗也LSM。

4.附錄

HBase實戰 | 從MySQL到HBase:資料存儲方案轉型的演進

B+ 樹

查詢“值為25”的節點,隻需要2次定位即可。

HBase實戰 | 從MySQL到HBase:資料存儲方案轉型的演進

LSM樹

查詢“值為25”的節點,隻需要4次定位即可。

三.優化思路

1.HBase優化點 (主要是讀)

異步化

  • 背景線程将memstore寫入Hfile;
  • 背景線程完成Hfile合并;
  • wal異步寫入(資料有丢失的風險)。

資料就近

  • blockcache,緩存常用資料塊:讀請求先到memstore中查資料,查不到就到blockcache中查,再查不到就會到磁盤上讀,把最近讀的資訊放入blockcache,基于LRU淘汰,可以減少磁盤讀寫,提高性能;
  • 本地化,如果Region Server恰好是HDFS的data node,Hfile會将其中一個副本放在本地;
  • 就近原則,如果資料沒在本地,Region Server會取最近的data node中資料。

快速檢索

基于bloomfilter過濾:

  • 正常檢索,RegionServer會周遊所有Hfile查詢所需資料。其中,需要周遊Hfile的索引塊才能判斷Hfile中是否有所需資料;
  • BloomFiler存儲HFile的摘要,可以通過極少磁盤IO,快速判斷目前HFile是否有所需資料:

    行緩存:快速判斷Hflie是否有所需要的行,粒度較粗,存儲占用少,磁盤IO少,資料較快;

列緩存:快速判斷Hfile是否有所需的列,粒度較細,但存儲占用較多。

基于timestamp過濾:

  • HFile基于日志追加、合并,維護了版本資訊;
  • 當查詢1小時内送出的資訊時,可以跳過隻包含1小時前資料的檔案。

HFile存儲結構:

HFile存儲格式

參考連結:

https://link.zhihu.com/?target=https%3A//blog.csdn.net/yangbutao/article/details/8394149
HBase實戰 | 從MySQL到HBase:資料存儲方案轉型的演進
  • Trailer存儲整個Hfile的定位資訊;
  • DataIndex存儲Data塊的索引資訊:Data存儲為一組磁盤塊,存儲資料資訊;DataIndex功能類似于B+樹的非葉子節點;Data每個磁盤塊中的資料按key有序,加載到記憶體後可以用二分查找定位;Key按行 + 列族 + 列 + 時間戳生成,按字典序排序(最佳查詢方式:最左比對);
  • MetaIndex存儲Meta的索引資訊,Meta存儲一系列元資訊;MetaIndex功能類似于B+樹的非葉子節點;Meta存儲bloomfiler等輔助資訊。

2.MySQL優化點(主要是寫)

查詢緩存

将SQL執行結果放入緩存。

緩存B+高層節點

一千萬行的大表,一般隻需要一棵3層的B+樹,其中索引節點 (非葉子節點) 的大小約20MB。完全可以考慮将大部葉子節點緩存,基于主鍵查詢隻需要一次IO。

減少随機寫——緩沖:延遲寫/批量寫

上節提到,B+樹通過自增主鍵大量減少随機插入。由于輔助索引的存在,插入、修改、删除操作,輔助索引可能引起大量的随機IO。

  • 插入緩沖:隻是将被插入資料寫入insert buffer;定期将其merge到B+樹;
  • 修改緩沖:類似于insert buffer的思路。

減少随機讀--MRR

ELECT * FROM t WHERE key_part1 >= 1000 AND key_part1 < 2000 AND key_part2 = 10000;

# 普通操作分解:

key_part1= 1000, key_part2=1000, id = 1

select * from t where id=1

key_part1= 1001, key_part2=1000, id = 10

select * from t where id=10

...

# MRR 操作分解:

SELECT * FROM t WHERE key_part1 >= 1000 AND key_part1 < 2000 AND key_part2 = 10000;

key_part1= 1000, key_part2=1000, id = 1

buffer.append(1)

key_part1= 1000, key_part2=1000, id = 10

buffer.append(10)

...

sort(buffer)   

select * from t where id in (buffer)
 /**           

索引下推

  • MySQL的server處理完索引後,會将索引其它部分傳給引擎層;
  • 引擎層根據過濾條件過濾掉無用的行,減少資料量,進而優化server的性能。

3.叢集化資料庫的輔助索引

InnoDB的輔助索引

  • B+樹全局有序,葉子節點存儲的是主鍵。基于輔助索引定位主鍵,再用主鍵定位資料。MySQL水準切分後,沒辦法跨庫維持建立全局有序索引:
  • 單執行個體維護索引,喪失了全局有序性;

再做一個基于新索引分庫方案,喪失了輔助索引維護的事務性。

HBase相同問題

  • 仿照InnoDB實作輔助索引,輔助索引可以做成單獨的key,其value是被索引行的key;
  • 可以做到全局資訊的維護,但沒法保證事務性。

4.HBase異步合并帶來的好處

  • TTL:基于背景合并,TTL很容易做;
  • 資料多版本支援:基于“追加”,HBase天然的可以支援多版本;
  • 版本數量:基于背景合并,可以将太舊版本幹掉。

四.總結

不知道BigTable的前輩們是出于什麼思路,本人冒昧揣測一下,多少應該是受到SQL資料庫的影響。個人感覺,這些或許就是一脈相承的演進,至少用這種思路學習不顯突兀。HBase不是憑空而來,也絕對不是解決所有問題的萬能靈丹。

最直接的存儲思路肯定是“檔案”,當“檔案”不能滿足需求,就有了資料的組織方式,進而演進到關系資料庫如MySQL。

MySQL以其“單機”很好地解決了ACID問題,但是,性能再好的“單機”勢必演變成“單點”瓶頸,進而,分布式思路成為必然。

最簡單的是擴充讀,“無限”挂slave;進而拆分寫節點,多點寫入:垂直拆庫、水準拆庫。一旦選擇分布式,就涉及如何主從一緻、如何發現節點、如何運維、ACID的如何保證等問題。

進而就是一系列分布式方案,而HBase就是其中一種解決思路——隻讀主庫保證一緻,水準拆分、zk等機制保證自動運維、單行級ACID。至于性能方面,由于存儲思路不同,MySQL與HBase分别取舍了不同的讀寫性能。繼而,就衍生出了如何針對性進行優化。

以這種思路,HBase不是憑空出現。以個人淺顯的目光所及,沒有完美的架構,也沒有絕對厲害的設計。固然SQL類資料庫有其獨領風騷的場景,NoSQL資料庫自然也有縱橫馳騁的疆域,無論是哪種架構,都有自己鞭長莫及的角落。

是以,應該說任何一種方案都沒有完美,隻有合适。而所有的合适都是演變而來,萬變不離其宗:更好的解決問題。