網際網路資料庫架構設計思路
一 、58同城資料庫架構設計思路
(1)可用性設計
解決思路:複製+冗餘
副作用:複製+冗餘一定會引發一致性問題
保證“讀”高可用的方法:複製從庫,冗餘資料,如下圖
解決方案:見下文
保證“寫”高可用的一般方法:雙主模式,即複製主庫(很多公司用單master,此時無法保證寫的可用性),冗餘資料,如下圖
解決方案:
a)方案一:由資料庫或者業務層保證key在兩個主上不衝突
b)方案二:見下文
58同城保證“寫”高可用的方法:“雙主”當“主從”用,不做讀寫分離,在“主”掛掉的情況下,“從”(其實是另外一個主),頂上,如下圖
優點:讀寫都到主,解決了一致性問題;“雙主”當“主從”用,解決了可用性問題
帶來的問題:讀效能如何擴充?解決方案見下文
(2)讀效能設計:如何擴充套件讀效能
最常用的方法是,建立索引
建立非常多的索引,副作用是:
a)降低了寫效能
b)索引佔記憶體多了,放在記憶體中的資料就少了,資料命中率就低了,IO次數就多了
但是否想到,不同的庫可以建立不同的索引呢?如下圖
主庫只提供寫,不建立索引
online從庫只提供online讀,建立online讀索引
offline從庫只提供offline讀,建立offline讀索引
提高讀效能常見方案二,增加從庫
上文已經提到,這種方法會引發主從不一致問題,從庫越多,主從時延越長,不一致問題越嚴重
這種方案很常見,但58沒有采用
提高讀效能方案三,增加快取
傳統快取的用法是:
a)發生寫請求時,先淘汰快取,再寫資料庫
b)發生讀請求時,先讀快取,hit則返回,miss則讀資料庫並將資料入快取(此時可能舊資料入快取),如下圖
a)如上文所述,資料複製會引發一致性問題,由於主從延時的存在,可能引發快取與資料庫資料不一致
b)所有app業務層都要關注快取,無法遮蔽“主+從+快取”的複雜性
58同城快取使用方案:服務+資料+快取
1)引入服務層遮蔽“資料庫+快取”
2)不做讀寫分離,讀寫都到主的模式,不會引發不一致
(3)一致性設計
主從不一致解決方案
方案一:引入中介軟體
中介軟體將key上的寫路由到主,在一定時間範圍內(主從同步完成的經驗時間),該key上的讀也路由到主
方案二:讀寫都到主
上文已經提到,58同城採用了這種方法,不做讀寫分離,不會不一致
資料庫與快取不一致解決方案
兩次淘汰法
異常的讀寫時序,或導致舊資料入快取,一次淘汰不夠,要進行二次淘汰
a)發生寫請求時,先淘汰快取,再寫資料庫,額外增加一個timer,一定時間(主從同步完成的經驗時間)後再次淘汰
b)發生讀請求時,先讀快取,hit則返回,miss則讀資料庫並將資料入快取(此時可能舊資料入快取,但會被二次淘汰淘汰掉,最終不會引發不一致)
(4)擴充套件性設計
(4.1)58同城秒級別資料擴容
需求:原來水平切分為N個庫,現在要擴充為2N個庫,希望不影響服務,在秒級別完成
最開始,分為2庫,0庫和1庫,均採用“雙主當主從用”的模式保證可用性
由於是2擴4,不會存在資料遷移,原來的0庫變為0庫+2庫,原來的1庫變為1庫和3庫
此時損失的是資料的可用性
最後,解除舊的雙主同步(0庫和2庫不會資料衝突),為了保證可用性增加新的雙主同步,並刪除掉多餘的資料
這種方案可以秒級完成N庫到2N庫的擴容。
存在的問題:只能完成N庫擴2N庫的擴容(不需要資料遷移),非通用擴容方案(例如3庫擴4庫就無法完成)
(4.2)非指數擴容,資料庫增加欄位,資料遷移
[這些方法在(上)篇中都已經介紹過,此處不再冗餘,有興趣的朋友回覆“同城”回看(上)篇]
方案一:追日誌方案
方案二:雙寫方案
(4.3)水平切分怎麼切
四類場景覆蓋99%拆庫業務
a)“單key”場景,使用者庫如何拆分: user(uid, XXOO)
b)“1對多”場景,帖子庫如何拆分: tiezi(tid, uid, XXOO)
c)“多對多”場景,好友庫如何拆分: friend(uid, friend_uid, XXOO)
d)“多key”場景,訂單庫如何拆分:order(oid, buyer_id, seller_id, XXOO)
[這些拆庫方案在(上)篇中都已經介紹過,此處不再冗餘,有興趣的朋友回覆“同城”回看(上)篇]
(5)海量資料下,SQL怎麼玩
不會這麼玩
a)各種聯合查詢
b)子查詢
c)觸發器
d)使用者自定義函式
e)“事務”都用的很少
原因:對資料庫效能影響極大
拆庫後,IN查詢怎麼玩[回覆“同城”回看(上)篇]
拆庫後,非Partition key的查詢怎麼玩[回覆“同城”回看(上)篇]
拆庫後,誇庫分頁怎麼玩?[回覆“同城”回看(上)篇]
問題的提出與抽象:ORDER BY xxx OFFSET xxx LIMIT xxx
單機方案:ORDER BY time OFFSET 10000 LIMIT 100
分庫後的難題:如何確認全域性偏移量
分庫後傳統解決方案:查詢改寫+記憶體排序
a)ORDER BY time OFFSET 0 LIMIT 10000+100
b)對20200條記錄進行排序
c)返回第10000至10100條記錄
優化方案一:增加輔助id,以減少查詢量
優化方案二:模糊查詢
a)業務上:禁止查詢XX頁之後的資料
b)業務上:允許模糊返回 => 第100頁資料的精確性真這麼重要麼?
最後的大招!!!(由於時間問題,只在DTCC2015上分享了喲)
優化方案三:終極方案,業務無損,查詢改寫與兩段查詢
需求:ORDER BY x OFFSET 10000 LIMIT 4; 如何在分庫下實現(假設分3庫)
步驟一、查詢改寫: ORDER BY x OFFSET 3333 LIMIT 4
[4,7,9,10] <= 1庫返回
[3,5,6,7] <= 2庫返回
[6,8,9,11] <= 3庫返回
步驟二、找到步驟一返回的min和max,即3和11
步驟三、通過min和max二次查詢:ORDER BY x WHERE x BETWEEN 3 AND 11
[3,4,7,9,10] <= 1庫返回,4在1庫offset是3333,於是3在1庫的offset是3332
[3,5,6,7,11] <= 2庫返回,3在2庫offset是3333
[3,5,6,8,9,11] <= 3庫返回,6在3庫offset是3333,於是3在3庫的offset是3331
步驟四、找出全域性OFFSET
3是全域性offset3332+3333+3331=9996
噹噹噹當,跳過3,3,3,4,於是全域性OFFSET 10000 LIMIT 4是[5,5,6,6]
總結:58同城資料庫架構設計思路
(1)可用性,解決思路是冗餘(複製)
(1.1)讀可用性:多個從庫
(1.2)寫可用性:雙主模式 or 雙主當主從用(58的玩法)
(2)讀效能,三種方式擴充讀效能
(2.1)增加索引:主從上的索引可以不一樣
(2.2)增加從庫
(2.3)增加快取:服務+快取+資料一套(58的玩法)
(3)一致性
(3.1)主從不一致:引入中間層 or 讀寫都走主庫(58的玩法)
(3.2)快取不一致:雙淘汰來解決快取不一致問題
(4)擴充套件性
(4.1)資料擴容:提升從庫,double主庫,秒級擴容
(4.2)欄位擴充套件:追日誌法 or 雙寫法
(4.3)水平切分
(單key)使用者庫如何拆分:, user(uid XXOO)
(1對多)帖子庫如何拆分: tiezi(tid, uid, XXOO)
(多對多)好友庫如何拆分: friend(uid, friend_uid, XXOO)
(多key)訂單庫如何拆分:order(oid, buyer_id, seller_id, XXOO)
(5)SQL玩法
(5.0)不這麼玩:聯合查詢,子查詢,觸發器,自定義函式,事務
(5.1)IN查詢:分發MR or 拼裝成不同SQL語句
(5.2)非partition key查詢:定位一個庫 or 分發MR
(5.3)誇庫分頁
(5.3.1)修改sql語句,服務內排序
(5.3.2)引入特殊id,減少返回數量
(5.3.3)業務優化,允許模糊查詢
(5.3.4)查詢改寫,二段查詢
58同城的案例到這兒
二、資料庫之父Codd的12條法則
另外,我們回顧一下資料庫之父Codd的12條法則,作為資料庫設計的指導性方針:
- 資訊法則
關係資料庫中的所有資訊都用唯一的一種方式表示——表中的值。 - 保證訪問法則
依靠表名、主鍵值和列名的組合,保證能訪問每個資料項。 - 空值的系統化處理
支援空值(NULL),以系統化的方式處理空值,空值不依賴於資料型別。 - 基於關係模型的動態聯機目錄
資料庫的描述應該是自描述的,在邏輯級別上和普通資料採用同樣的表示方式,即資料庫必須含有描述該資料庫結構的系統表或者資料庫描述資訊應該包含在使用者可以訪問的表中。 - 統一的資料子語言法則
一個關係資料庫系統可以支援幾種語言和多種終端使用方式,但必須至少有一種語言,它的語句能夠一某種定義良好的語法表示為字串,並能全面地支援以下所有規則:資料定義、檢視定義、資料操作、約束、授權以及事務。(這種語言就是SQL) - 檢視更新法則
所有理論上可以更新的檢視也可以由系統更新。 - 高階的插入、更新和刪除操作
把一個基礎關係或派生關係作為單個操作物件處理的能力不僅適應於資料的檢索,還適用於資料的插入、修改個刪除,即在插入、修改和刪除操作中資料行被視作集合。 - 資料的物理獨立性
不管資料庫的資料在儲存表示或訪問方式上怎麼變化,應用程式和終端活動都保持著邏輯上的不變性。 - 資料的邏輯獨立性
當對錶做了理論上不會損害資訊的改變時,應用程式和終端活動都會保持邏輯上的不變性。 - 資料完整性的獨立性
專用於某個關係型資料庫的完整性約束必須可以用關係資料庫子語言定義,而且可以儲存在資料目錄中,而非程式中。 - 分佈獨立性
不管資料在物理是否分散式儲存,或者任何時候改變分佈策略,RDBMS的資料操縱子語言必須能使應用程式和終端活動保持邏輯上的不變性。 - 非破壞性法則
如果一個關係資料庫系統支援某種低階(一次處理單個記錄)語言,那麼這個低階語言不能違反或繞過更高階語言(一次處理多個記錄)規定的完整性法則或約束,即使用者不能以任何方式違反資料庫的約束。
還有一些經驗:
- 降低對資料庫功能的依賴
功能應該由程式實現,而非DB實現。原因在於,如果功能由DB實現時,一旦更換的DBMS不如之前的系統強大,不能實現某些功能,這時我們將不得不去修改程式碼。所以,為了杜絕此類情況的發生,功能應該有程式實現,資料庫僅僅負責資料的儲存,以達到最低的耦合。 - 定義實體關係的原則
當定義一個實體與其他實體之間的關係時,需要考量如下:- 牽涉到的實體 識別出關系所涉及的所有實體。
- 所有權 考慮一個實體“擁有”另一個實體的情況。
- 基數 考量一個實體的例項和另一個實體例項關聯的數量。
來自網路資料收集與整合,希望對您軟體開發有幫助。 其它您可能感興趣的文章:
企業應用之效能實時度量系統演變
雲端計算參考架構幾例
智慧移動導遊解決方案簡介
人力資源管理系統的演化
如有想了解更多軟體,系統 IT,企業資訊化 資訊,請關注我的微信訂閱號:
作者:Petter Liu
出處:http://www.cnblogs.com/wintersun/
本文版權歸作者和部落格園共有,歡迎轉載,但未經作者同意必須保留此段宣告,且在文章頁面明顯位置給出原文連線,否則保留追究法律責任的權利。
該文章也同時釋出在我的獨立部落格中-Petter Liu Blog。