1. 程式人生 > >後端架構深度好文

後端架構深度好文

今天看到了一篇不錯的文章(轉載一下細細思考)

微博平臺研發作為微博的底層資料及業務支撐部門,已經經歷了5年的發展歷程。伴隨著從資料及業務暴發式增長,我們在海量資料儲存方面遭遇了諸多挑戰,與此同時也伴隨著豐富經驗的積累。

本次新兵訓練營,受眾在於應屆畢業生,目的在於讓新同學系統化並且有針對性的瞭解平臺的核心技術及核心業務,以使新同學在新兵訓練營結束後,能夠對平臺的底層架構與業務有一定的瞭解。

本文主要面向新同學介紹平臺的核心技術之一——海量資料儲存,主要介紹在海量資料儲存在大規模分散式系統下的架構變遷與設計。

課程大綱:

1. 課程目標

2. 儲存服務概述

3. MySQL

MySQL分散式架構設計

4. RedisRedis分散式架構設計

5. 思考與討論

一、課程目標

1. 瞭解儲存服務概況,以及RDBMS及NoSQL的差異

2. 理解MySQL、Redis、HBase基本實現機制、特性、適用場景

3. 理解幾種儲存產品的大規模分散式服務方案

4. 學會使用平臺的MySQL、Redis client元件

5. 理解對於MySQL、Redis分散式系統設計想要注意的問題

6. 瞭解平臺幾種典型案例

7. 理解幾種儲存產品在平臺的定製修改與名詞術語

二、儲存服務概述

1. 關係型資料庫是基於實體關係模型(Entity-Relationship Model)

的資料服務,具備以下特點。

  • 適合儲存結構化資料

  • 查詢語言SQL,insert delete update select

  • 主流關係型資料庫多是持久化儲存系統,系統性能與機器效能相關性較大

  • 幾類主流的 關係型資料庫

    • MySQL

    • Oracle

    • DB2

    • SQL Server

  • 效能

    • 侷限於伺服器效能,與其是磁碟效能

    • 侷限於資料複雜度

    • 常見的SSD磁碟伺服器,單機讀取效能可達萬級/s

大型網際網路服務大多采用MySQL進行作為關係型資料庫,微博平臺的核心業務(如微博內容使用者微博列表)也同樣如此

本次培訓也會著重介紹MySQL及其分散式架構方案。

2. NoSQL

(Not only SQL)資料庫,泛指非關係型的資料庫,興起的契機在於傳統關係型資料庫應對大規模、高併發的能力有限,而NoSQL的普遍效能優勢能夠彌補關係型資料庫在這方面的不足

  • 儲存非結構化資料、半結構化資料

  • 效能

業界使用的NoSQL多為記憶體集中型服務,受限於I/O及網路,通常請求響應時間在毫秒級別,單機QPS在10萬級別(與資料大小及儲存複雜度相關)

  • 常見的幾類NoSQL產品

  • K-V(MemcachedRedis),這類NoSQL產品在網際網路業內應用範圍最廣。Memcached提供具備LRU淘汰策略的K-V記憶體儲存;而Redis提供支援複雜結構(List、Hash等)的記憶體及持久化儲存

  • Column(HBaseCassandra),HBase是基於列式儲存的分散式資料庫集群系統

  • Document(MongoDb)

  • Graph(Neo4J),最龐大、最複雜的Graph模型是人的關係,理論上用圖描述並且用Graph資料庫儲存最合適不過,不過目前的資料規模、系統性能仍然有待優化

web2.0時代,NoSQL產品在網際網路行業中的重要性隨著網際網路及移動網際網路的發展而與日劇增 大型網際網路應用,為應對大規模、高併發訪問,大多都引入了NoSQL產品,其中Memcached、Redis以其高成熟度、高效能、高穩定性而被廣泛使用。微博平臺也具備千臺規模的NoSQL叢集,微博核心的Feed業務、關係業務也都依賴Memcached及Redis提供高效能服務

本次培訓,會著重介紹Redis及其分散式架構

三、MySQL

微博平臺核心業務的資料都儲存在MySQL上,目前具備千臺規模的叢集,單個核心業務資料突破千億級,單個核心業務QPS峰值可達10萬級每秒,寫入也是萬級每秒。

在海量資料並且資料量持續增長的景下,在如何設計滿足 高併發(w/r)、低延時(10ms級別)、高可用性(99.99%)的分散式MySQL系統方面,我們已經具備充足的經驗並且依然在持續攻堅這一問題,而我們的課程也會著重介紹海量資料儲存之MySQL。

1. MySQL簡介

  • MySQL是一個關係型資料庫系統RDBMS

  • 使用SQL作為查詢語言

  • 開源

  • 儲存引擎

  • Innodb 支援事務、行鎖,寫入效能稍差

  • MyIsam 不支援事務,讀寫效能略好

  • 滿足ACID特性

  • 主鍵、唯一鍵、外來鍵(大規模系統一般不用)

  • Transaction,事務即一系列操作,要麼完全地執行,要麼完全地不執行

  • 服務、埠、例項,都是指 服務端啟動的一個MySQL資料庫

  • 效能

  • 隨著磁碟效能升高,讀寫效能也逐步升高,但成本也隨之增加

  • 資料庫的寫入效能:寫入tps隨著併發量增加而增加,但上升到一定瓶頸,增速放緩至併發數臨界點後 tps會急劇下滑

  • 思考:如果對效能有更高(超出上述三種儲存介質併發量級)的要求怎麼辦?

    • 定製儲存:針對服務特點,定製儲存,定製更適合自己業務場景的儲存產品。然而一般業界成熟產品為考慮通用性而會犧牲部分效能

    • 引入NoSQL

2. 從單機到叢集的架構變遷

  • 業務上線初期,web服務規模較小,一般具備以下特點

    • 服務原型時期,使用者基數小,多種業務公用資源,日均寫入百萬級別,讀取千萬級別

    • 資料規模小,單機效能能夠滿足需求

    • 使用者規模小,開發重心偏向迭代速度

      考慮到上述小型業務特點,為節約資源成本及開發成本,可以採用多個業務混合部署形式

  • 當用戶增多,資料量、訪問量升高(2倍以下),資料庫壓力較大,怎樣在有限程度提高MySQL吞吐量呢?

  • 壓力還在有限的範圍內增長,通過簡單、低成本優化,可以一定成都上提高有限的服務效能

    • SQL優化

    • 硬體升級

  • 業務持續發展,讀取效能出現瓶頸&&各業務互相影響,多個業務出 現資源搶佔,如何快速解決業務搶佔問題以提高服務效能?

  • 按業務進行拆分,以使業務隔離,timeline的壓力增加,不會影響content資料庫服務效能;進行拆分後,資源增加,服務效能也相應提升。

    • 直拆分——按業務進行資料拆分

  • 隨著業務的繼續發展,讀取效能出現瓶頸,讀寫互相影響,如何確保讀請求量的增加,不要影響寫入效能?相反寫入請求量增加如何確保不影響讀取效能?(寫入效能出現問題會造成資料丟失)

  • 讀寫分離,寫入僅寫master,master與slave自動同步;讀取僅以slave作為來源

讀寫分離後,slave僅專注於承擔讀請求,讀取效能得到優化;同裡獨立的master服務的寫入效能也得到優化。

  • 一臺/一對M-S伺服器效能終歸是很有限的,當單例項服務效能無法承載線上的請求量時,如何進行優化?

    • 升級為一主多從架構

    • 一個master承載所有寫入請求,理論上master效能不變

    • 多個slave分擔讀取請求,讀取效能提升n倍

  • 隨著業務量的增長,服務出現如下變化:

  • 資料量增長,意味著原本的儲存空間不足

  • 寫入量增長,意味著master寫入效能存在瓶頸

  • 讀取量增長,意味著slave讀取效能也存在瓶頸,但通過擴充slave是有限制的:一方面M-S replication效能有風險;另一方面擴充slave的成本較高

如何優化以解決上述問題?

  • 水平拆分

業務經歷資料量的增長、讀寫請求量的增長,資料庫服務已經演進為分散式架構,一個業務的資料,怎樣合理的分佈到上述複雜的分散式資料庫是下一個需要解決的問題

3. 如何基於上述演進到最後的架構進行資料庫設計呢?

  • 分散式資料庫設計

  • hash拆分方式,既按hash規則,將資料讀寫請求分散到多個例項上,見上述水平拆分示意圖

  • 時間拆分方式,基於確定好的時間劃分規則,將資料按時間段分散儲存再多個例項中

資料分佈到一個分散式資料庫內,一個例項儲存1/n的資料,一個例項只需要一個數據庫就能夠滿足功能需求。

經歷幾年的發展,資料規模會成倍增長,當需要再次水平擴容(4太8臺),需要通過程式,將資料一分為二,資料遷移成本較高,需要開發人員介入。

如果在資料庫設計時,一個就預先建好2個數據庫 ,每個資料庫儲存1/n/2的資料,需要水平擴容時,即可完整遷移一個數據庫,而無需開發人員干預。

在一個數據庫例項上,建立的多個數據庫,稱為邏輯庫。

  • 邏輯庫設計

    • 邏輯庫是相對與物理庫而言的概念:物理庫只數據庫服務的例項;邏輯庫指在一個數據庫例項上建立的多個database

    • 定義邏輯庫的目的是便於擴容。假如4臺數據庫伺服器,每臺上的物理庫包含8個邏輯庫,當系統出現容量、寫入量瓶頸時,可以新增一倍即4臺伺服器,直接以同步方式同步資料庫,而不需要單獨編寫應用程式利用進行匯入

4. 基於上述分散式資料庫下的表拆分設計方式

  • hash拆分方式:按hash規則將一個數據庫的資料,分散hash到多張表中

  • 結合資料庫的hash模型如圖:

    • 根據uid hash到uid所在到資料庫,然後再hash到資料庫_1下的tb_5表

    • 適合資料規模有限的資料集

    • 適合增長速度可控的資料集

  • 按時間拆分方式,按時間規則將同一時段的資料儲存在一張表,多個時段時間儲存在多張表。例如按月劃分,每個月表儲存一個月的資料,如果需要獲取全部資料需要跨越多個月表

  • 結合資料庫的hash模型如圖:

    思考一個問題:如何能夠快速定位,一個人的第1000條到1100條資料呢?

    • 根據uid hash到所在的資料庫db_1,然後再查詢201507 201506獲取兩個月的資料

    • 適合儲存增速較快的資料集

    • 但查詢資料需要跨越多個時間段的表

  • 二級索引快速定位(一級)索引位置

    • 描述資料在以及索引中的分佈狀況

    • 用於快速定位/縮小查詢範圍

    • 一般情況欄位列表:uid, date_time, min_id, count

5. 當一臺伺服器宕機怎麼辦?

  • Slave(一主多從)宕機?

    • 剩餘健康Slave無風險,則無需緊急操作,例行修復

    • 切換流量到容災機房(如果具備容災機房)

    • 緊急擴容[優先]、重啟、替換

    • 有損降級部分請求

  • Master宕機?

  • 由於master資料的唯一性,致使master出現異常會直接造成資料寫入失敗

    • 速下線master

    • 下線一臺salve的讀服務(如果slave效能有風險,則同時快速擴容)

    • 提升slave為master

    • 生效新master與slave的同步機制

6. 如此複雜的分散式資料庫+資料庫拆分+資料表拆分,client端如何便捷操作呢?

多數使用分散式資料庫服務的團隊,都有各自實現的資料庫Client元件,微博平臺採用如下幾個層級的組建來進行分散式資料庫操作

  • 獲取TableContainer,獲取所有表定義規則

  • 通過table name從TableContainer中獲取指定的TableItem

  • TableItem關聯多個JdbcTeplate-DataSource

  • 通過TableItem結合uid、id、date獲取經過hash計算得到正確的JdbcTemplate及SQL

  • 使用JdbcTemplate進行SQL操作

7. 注意事項

  • MySQL設計應該注意的問題

    • 表字符集選擇UTF8

    • 儲存引擎使用InnoDB

    • 使用Varchar/Varbinary儲存變長字串

    • 不在資料庫中儲存圖片、檔案等

    • 每張表資料量控制在20000W以下

    • 提前對業務做好垂直拆分

  • MySQL查詢應該遇到的問題

    • select條件查詢要利用索引

    • 同一欄位的條件判定要用in而不要用or

    • MySQL不擅長數學運算

    • 無法使用索引

    • MySQL最擅長的是單表的主鍵/索引查詢

    • JOIN消耗較多記憶體,產生臨時表

    • 讓資料庫做最擅長的事

    • 降低業務耦合度避開服務端BUG

    • 避免使用儲存過程、觸發器、函式等

    • 避免使用大表的JOIN

    • 避免在資料庫中進行數學運算

    • 減少與資料庫的互動次數

8. MySQL練習題

  • 設計一個每秒2000qps,1億條資料的使用者基本資訊儲存資料庫。完成資料庫設計,資料庫搭建,web寫入查詢服務搭建。

  • 定義使用者資訊結構:uid,name,age,gender

  • 給定2個mysql例項,每個例項建立2個數據庫

  • 每個資料庫建立2長表

  • 編寫程式碼,以hash形式,實現對資料庫、表的資料操作

四、Redis

微博作為web2.0時代具備代表性的SNS服務,具備龐大的使用者群體和億級的活躍使用者,同時也承擔著高併發、低延遲的服務效能壓力。

Redis作為NoSQL系列的一個典型應用,以其高成熟度、高可用性、高效能而被用來解決web2.0時代關係型資料庫效能瓶頸問題。例如微博的計數服務的請求量以達到百萬級/s,數以百計的關係型資料庫才能應對如此高的QPS,而且請求耗時較高且波動較大;然而使用Redis這種NoSQL產品,僅僅需要10臺級別的叢集即可應對,平均請求耗時5ms以下。

這一章節,為大家介紹Redis以及其大規模叢集架構。

1. Redis簡介

  • Redis是一個支援記憶體儲存及持久化儲存的K-V儲存系統

  • 支援複雜資料結構,相比與Memcached僅支援簡單的key-value儲存,Redis原生支援幾類常用的儲存結構,例如

    • hash:儲存雜湊結構資料

    • list:儲存列表數

  • 單執行緒

  • 高效能,避免過多考慮併發、鎖、上下文切換

  • 資料一致性好,例如對一個計數的併發操作,不會有‘讀者寫者’問題

  • 單執行緒無法利用多核,單可以通過啟動多個例項方式,充分利用多核

  • 原生支援Master-Slave

  • 過期機制

  • 被動過期——client訪問key時,判斷過期時間選擇是否過期

  • 主動過期——預設使用valatile-lru

    • volatile-lru:從已設定過期時間的資料集中挑選最近最少使用的資料淘汰

    • volatile-ttl:從已設定過期時間的資料集中挑選將要過期的資料淘汰

    • volatile-random:從已設定過期時間的資料集中任意選擇資料淘汰

    • allkeys-lru:從全部資料集中挑選最近最少使用的資料淘汰

    • allkeys-random:從全部資料集中任意選擇資料淘汰no-enviction(驅逐):禁止驅逐資料

  • Redis的字典表結構

    • AOF

    • Snapshot——RDB檔案快照