海量數據存儲基礎
微博平臺研發作為微博的底層數據及業務支撐部門,已經經歷了5年的發展歷程。伴隨著從數據及業務暴發式增長,我們在海量數據存儲方面遭遇了諸多挑戰,與此同時也伴隨著豐富經驗的積累。
本次新兵訓練營,受眾在于應屆畢業生,目的在于讓新同學系統化并且有針對性的了解平臺的核心技術及核心業務,以使新同學在新兵訓練營結束后,能夠對平臺的底層架構與業務有一定的了解。
本文主要面向新同學介紹平臺的核心技術之一——海量數據存儲,主要介紹在海量數據存儲在大規模分布式系統下的架構變遷與設計。
課程大綱:
1. 課程目標
2. 存儲服務概述
3. MySQL與MySQL分布式架構設計
4. Redis與Redis分布式架構設計
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(Memcached、Redis),這類NoSQL產品在互聯網業內應用范圍最廣。Memcached提供具備LRU淘汰策略的K-V內存存儲;而Redis提供支持復雜結構(List、Hash等)的內存及持久化存儲
-
Column(HBase、Cassandra),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模型如圖:
-
根據uid hash到所在的數據庫db_1,然后再查找201507 201506獲取兩個月的數據
思考一個問題:如何能夠快速定位,一個人的第1000條到1100條數據呢?
-
-
二級索引快速定位(一級)索引位置
-
-
描述數據在以及索引中的分布狀況
-
用于快速定位/縮小查詢范圍
-
一般情況字段列表: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查詢應該遇到的問題
-
-
避免使用存儲過程、觸發器、函數等
-
-
讓數據庫做最擅長的事
-
降低業務耦合度避開服務端BUG
-
-
避免使用大表的JOIN
-
-
MySQL最擅長的是單表的主鍵/索引查詢
-
JOIN消耗較多內存,產生臨時表
-
-
避免在數據庫中進行數學運算
-
-
MySQL不擅長數學運算
-
無法使用索引
-
-
減少與數據庫的交互次數
-
-
select條件查詢要利用索引
-
同一字段的條件判定要用in而不要用or
-
-
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的字典表結構
-
-
-
Key字典表hash table結構,有hash結構就意味著需要按需進行rehash,rehash的時間段內,對內存是有成倍開銷的
-
Value結構,存儲Key對應的value
-
Expire表結構,存儲key的過期時間
-
額外開銷60B+
-
持久化方式
-
-
AOF
-
Snapshot——RDB文件快照
-
-
-
與MC的差異
-
平臺的定制CounterService
-
-
修改hash table為,增量擴展式的hash tables,例如每1億個key存儲在一個table中,數據超過1億(或者一個臨界比例)則開辟下一個1億空間的table
-
廢棄expire,Redis的主動過期策略無法像MC的LRU策略確保熱數據留存在內存中,冷數據從緩存剔除,我們多數場景需要控制Redis中的數據量不突破內存限制
-
2. Redis的主要數據結構
-
String (key-value)
-
Hash (key-field-value)
-
List(key-values)
-
Set(key-members)
-
SortedSet(key-member-score)
3. Redis的分布式部署方案是怎樣的?與MySQL有什么異同
-
Reids由于其M-S特性與MySQL類似,因此分布式部署方案同MySQL相當
-
單實例——小型業務 or 業務初期
-
主從——HA、讀寫分離
-
一主多從——讀取性能出現瓶頸
-
數據水平拆分——容量不足|寫入性能瓶頸
-
常用的分布式部署方案
4. 分布式Redis架構如何實現高可用(HA)?
-
采用M-S高可用方案,原因也式由于其Master-Slave的特性
-
服務域名化是必要的,目前大型的Redis集群應用大多采用域名方式
5. 基本容量規劃
-
空間=key數量*單條占用(K-V占用+額外空間) 用戶空間=5億用戶*200B(平均)=100G 微博計數器=(500億+預期2年新增300億)*10B=800G
-
訪問量=服務訪問量*單次訪問對資源的hit量 微博計數器Feed訪問量=10000/s * 20 = 20萬/s
6. CounterService
微博具備龐大的數據基數,因此所需要存儲的數據量級也極其龐大
例如微博計數器,具有百億條紀錄,全部存儲在Redis中,需要T級別的空間,成本過高
因此我們對Redis進行定制化改造,以使其適合多數數據小,大小有固定限制的數據
-
優化存儲空間
-
采用分段哈希桶的形式,進行存儲,避免rehash (分段存儲要求key為遞增序)
-
空間占用優化效果
-
key:8B
-
value:自定義
7. 如何支持上述分布式架構下的client訪問?(redis3.0+支持Redis Cluster)
-
Reids具有多個開源的client支持,我們所使用的是Jedis
-
Jedis除了提供client外,還提供了操作封裝以及M-S組件
-
我們所使用的Redis系列組件如下:
8. Redis練習題
-
使用Redis,實現用戶受到的贊列表及贊計數功能
-
使用測試環境,啟動兩個Redis實例
-
使用Redis存儲用戶受到的贊列表[{uid, time}..]及贊計數uid-count
-
完成贊操作業務邏輯,包括贊、取消贊、查看贊列表、查看贊計數
五、思考與討論
1. Memcache當容量到達瓶頸會 截取LRU鏈以釋放空間。上文介紹過Redis的key過期機制,思考以下幾個問題:
-
Redis滿了會發生什么?如何避免發生上述問題呢?
-
為什么我們的定制Redis會廢棄expire表?
2. MySQL與Redis各自適合什么樣的場景?
-
數據冷熱?
-
數據大小?
-
數據量級?
-
數據增長速度?
-
是否持久化?
-
訪問量(read/write)?
-
請求性能要求?
------------------新兵訓練營簡介------------------
微博平臺新兵訓練營活動是微博平臺內部組織的針對新入職同學的團隊融入培訓課程,目標是團隊融入,包括人的融入,氛圍融入,技術融入。當前已經進行4期活動,很多學員迅速成長為平臺技術骨干。
微博平臺是非常注重團隊成員融入與成長的團隊,在這里有人幫你融入,有人和你一起成長,也歡迎小伙伴們加入微博平臺,歡迎私信咨詢。
------------------講師簡介------------------
畢建坤,@bijiankun 微博平臺及大數據部——平臺研發系統研發工程師,2012年7月畢業于哈爾濱理工大學,校招入職微博工作至今,先后負責微博Feed、贊、評論等底層服務研發以及方案評審等工作。聚焦大規模系統的架構設計與優化,以及大規模系統下的服務穩定性保障。新兵訓練營第一期學員。
來自:http://weibo.com/p/1001643874615465508614