100 億數據 1 萬屬性數據架構設計
對于 version + ext 方案,還是有很多朋友質疑“線上不可能這么用”。 本篇將講述一下 58 同城最核心的數據“帖子”的架構實現技術細節,說明不僅不是“不可能這么用”,而是大數據,可變屬性,高吞吐場景下的“常用手段”。
一、背景描述及業務介紹
問:什么是數據庫擴展的 version + ext 方案?
使用 ext 來承載不同業務需求的個性化屬性,使用 version 來標識 ext 里各個字段的含義 。
例如上述 user 表:
verion=0 表示 ext 里是 passwd/nick
version=1 表示 ext 里是 passwd/nick/age/sex
優點?
( 1 )可以隨時動態擴展屬性,擴展性好
( 2 )新舊兩種數據可以同時存在,兼容性好
不足?
( 1 ) ext 里的字段無法建立索引
( 2 ) ext 里的 key 值有大量冗余,建議 key 短一些
問: 什么是 58 同城最核心的數據?
58 同城是一個信息平臺,有很多垂直品類:招聘、房產、二手物品、二手車、黃頁等等,每個品類又有很多子品類,不管哪個品類, 最核心的數據都是“帖子信息” (業務像一個大論壇?)。
問: 帖子信息有什么特點?
大家去 58 同城的首頁上看看就知道了:
( 1 ) 每個品類的屬性千差萬別 ,招聘帖子和二手帖子屬性完全不同,二手手機和二手家電的屬性又完全不同,目前恐怕有 近萬個屬性
( 2 ) 帖子量很大, 100 億級別
( 3 ) 每個屬性上都有查詢需求 (各組合屬性上都可能有組合查詢需求) ,招聘要查職位 / 經驗 / 薪酬范圍,二手手機要查顏色 / 價格 / 型號,二手要查冰箱 / 洗衣機 / 空調
( 4 ) 查詢量很大,每秒幾 10 萬級別
如何解決 100 億數據量, 1 萬屬性,多屬性組合查詢, 10 萬并發查詢 的技術難題,是今天要討論的內容。
二、最容易想到的方案
每個公司的發展都是一個從小到大的過程,撇開并發量和數據量不談,先看看
( 1 )如何實現屬性擴展性需求
( 2 )多屬性組合查詢需求
最開始,可能只有一個 招聘品類 ,那帖子表可能是這么設計的:
tiezi(tid,uid, c1, c2, c3)
那如何滿足各屬性之間的組合查詢需求呢?
最容易想到的是通過組合索引:
index_1(c1,c2) index_2(c2, c3) index_3(c1, c3)
隨著業務的發展,又新增了一個 房產類別 ,新增了若干屬性,新增了若干組合查詢,于是帖子表變成了:
tiezi(tid,uid, c1, c2, c3, c10, c11, c12, c13)
其中 c1,c2,c3 是招聘類別屬性, c10,c11,c12,c13 是房產類別屬性,這兩塊屬性一般沒有組合查詢需求
但為了滿足房產類別的查詢需求,又要建立了若干組合索引(不敢想有多少個索引能覆蓋所有兩屬性查詢,三屬性查詢)
是不是發現玩不下去了?
三、友商的玩法
新增屬性是一種擴展方式,新增表也是一種方式 ,有友商是這么玩的,按照業務進行垂直拆分:
tiezi_zhaopin(tid,uid, c1, c2, c3)
tiezi_fangchan(tid,uid, c10, c11, c12, c13)
這些表,這些服務維護在不同的部門,不同的研發同學手里,看上去各業務線靈活性強,這恰恰是悲劇的開始 :
( 1 ) tid 如何規范?
( 2 )屬性如何規范?
( 3 )按照 uid 來查詢怎么辦(查詢自己發布的所有帖子)?
( 4 )按照時間來查詢怎么辦(最新發布的帖子)?
( 5 )跨品類查詢怎么辦(例如首頁搜索框)?
( 6 )技術范圍的擴散,有的用 mongo 存儲,有的用 mysql 存儲,有的自研存儲
( 7 )重復開發了不少組件
( 8 )維護成本過高
( 9 ) …
想想看,電商的商品表,不可能一個類目一個表的。
四、 58 同城的玩法
【統一帖子中心服務】
平臺型創業型公司,可能有多個品類,例如 58 同城的招聘房產二手,很多異構數據的存儲需求,到底是分還是合,無需糾結: 基礎數據基礎服務的統一 ,無疑是 58 同城技術路線發展 roadmap 上最正確的決策之一 ,把這個方針堅持下來, @ 老崔 @ 曉飛 這些高瞻遠矚的先賢功不可沒,業務線會有“擴展性”“靈活性”上的微詞,后文看看先賢們如何通過一些巧妙的技術方案來解決的。
如何將不同品類,異構的數據統一存儲起來,采用的就是類似 version+ext 的方式:
tiezi(tid,uid, time, title, cate, subcate, xxid, ext)
( 1 ) 一些通用的字段抽取出來單獨存儲
( 2 ) 通過 cate, subcate, xxid 等來定義 ext 是何種含義 (和 version 有點像?)
( 3 )通過 ext 來存儲不同業務線的個性化需求
例如招聘的帖子:
ext : {“job”:”driver”,”salary”:8000,”location”:”bj”}
而二手的帖子:
ext : {”type”:”iphone”,”money”:3500}
58 同城最核心的帖子數據, 100 億的數據量,分 256 庫,異構數據 mysql 存儲,上層架了一個服務,使用 memcache 做緩存,就是這樣一個簡單的架構,一直堅持這這么多年。上層的這個服務,就是 58 同城最核心的統一服務 IMC ( Imformation Management Center ) ,注意這個最核心,是沒有之一。
解決了海量異構數據的存儲問題,遇到的 新問題 是:
( 1 )每條記錄 ext 內 key 都需要重復存儲,占據了大量的空間,能否壓縮存儲
( 2 ) cateid 已經不足以描述 ext 內的內容,品類有層級,深度不確定, ext 能否具備自描述性
( 3 )隨時可以增加屬性,保證擴展性
【統一類目屬性服務】
每個業務有多少屬性,這些屬性是什么含義,值的約束等揉不到帖子服務里,怎么辦呢?
58 同城的先賢們 抽象出一個統一的類目、屬性服務,單獨來管理這些信息 ,而帖子庫 ext 字段里 json 的 key ,統一由數字來表示,減少存儲空間 。
如上圖所示, json 里的 key 不再是 ”salary” ”location” ”money” 這樣的長字符串了,取而代之的是數字 1,2,3,4 ,這些 數字是什么含義,屬于哪個子分類,值的校驗約束,統一都存儲在類目、屬性服務里 。
這個表里對帖子中心服務里 ext 字段里的數字 key 進行了解釋:
1 代表 job ,屬于招聘品類下 100 子品類,其 value 必須是一個小于 32 的 [a-z] 字符
4 代表 type ,屬于二手品類下 200 子品類,其 value 必須是一個 short
這樣就對原來帖子表 ext 里的
ext : {“1”:”driver”,”2”:8000,”3”:”bj”}
ext : {”4”:”iphone”,”5”:3500}
key 和 value 都做了統一約束 。
除此之外, 如果 ext 里某個 key 的 value 不是正則校驗的值,而是枚舉值時,需要有一個對值進行限定的枚舉表來進行校驗 :
這個枚舉校驗,說明 key=4 的屬性(對應屬性表里二手,手機類型字段),其值不只是要進行“ short 類型”校驗,而是 value 必須是固定的枚舉值。
ext : {”4”:”iphone”,”5”:3500} 這個 ext 就是不合法的( key=4 的 value=iphone 不合法),合法的應該為
ext : {”4”:”5”,”5”:3500}
此外,類目屬性服務還能記錄類目之間的層級關系:
( 1 )一級類目是招聘、房產、二手 …
( 2 )二手下有二級類目二手家具、二手手機 …
( 3 )二手手機下有三級類目二手 iphone ,二手小米,二手三星 …
( 4 ) …
協助解釋 58 同城最核心的帖子數據,描述品類層級關系,保證各類目屬性擴展性,保證各屬性值合理性校驗,就是 58 同城另一個統一的核心服務 CMC ( Category Management Center ) 。
多提一句,類目、屬性服務像不像電商系統里的 SKU 擴展服務?
( 1 )品類層級關系,對應電商里的類別層級體系
( 2 )屬性擴展,對應電商里各類別商品 SKU 的屬性
( 3 )枚舉值校驗,對應屬性的枚舉值,例如顏色:紅,黃,藍
解決了 key 壓縮, key 描述, key 擴展, value 校驗,品類層級的問題, 還有這樣的一個問題 沒有解決:每個品類下帖子的屬性各不相同,查詢需求各不相同,如何解決 100 億數據量, 1 萬屬性的查詢需求,是 58 同城面臨的新問題。
【統一檢索服務】
數據量很大的時候,不同屬性上的查詢需求,不可能通過組合索引來滿足所有查詢需求,怎么辦呢?
58 同城的先賢們,從一早就確定了 “外置索引,統一檢索服務”的技術路線 :
( 1 )數據庫提供“帖子 id ”的正排查詢需求
( 2 )所有非“帖子 id ”的個性化檢索需求,統一走外置索引
元數據與索引數據的操作遵循:
(1)對帖子進行 tid 正排查詢,直接訪問帖子服務
(2)對帖子進行修改,帖子服務通知檢索服務,同時對索引進行修改
(3)對帖子進行復雜查詢,通過檢索服務滿足需求
這個扛起 58 同城 80% 終端請求(不管來自 PC 還是 APP ,不管是主頁、城市頁、分類頁、列表頁、詳情頁,很可能這個請求最終會是一個檢索請求)的服務,就是 58 同城另一個統一的核心服務 E-search ,這個搜索引擎的每一行代碼都來自 58 同城 @ 老崔 @ 老龔 等先賢們,目前系統維護者,就是“架構師之路”里屢次提到的 @ 龍神 。
對于這個服務的架構,簡單展開說明一下:
為應對 100 億級別數據量、幾十萬級別的吞吐量,業務線各種復雜的復雜檢索查詢, 擴展性是設計重點 :
( 1 ) 統一的 Java 代理層集群 ,其無狀態性能夠保證增加機器就能擴充系統性能
( 2 ) 統一的合并層 C 服務集群 ,其無狀態性也能夠保證增加機器就能擴充系統性能
( 3 ) 搜索內核檢索層 C 服務集群 , 服務和索引數據部署在同一臺機器上 ,服務啟動時可以加載索引數據到內存,請求訪問時從內存中 load 數據,訪問速度很快
( 3.1 )為了滿足數據容量的擴展性, 索引數據進行了水平切分 ,增加切分份數,就能夠無限擴展性能
( 3.2 )為了滿足一份數據的性能擴展性, 同一份數據進行了冗余 ,理論上做到增加機器就無限擴展性能
系統時延, 100 億級別帖子檢索,包含請求分合,拉鏈求交集,從 merger 層均可以做到 10ms 返回 。
58 同城的帖子業務, 一致性不是主要矛盾 , E-search 會定期全量重建索引,以保證即使數據不一致,也不會持續很長的時間。
五、總結
文章寫了很長,最后做一個簡單總結,面對 100 億數據量, 1 萬列屬性, 10 萬吞吐量的業務需求 , 58 同城的經驗,是采用了 元數據服務、屬性服務、搜索服務 來解決的。
來自:http://mp.weixin.qq.com/s/3O3kPSwV-tAeYdy2ZRACpg