
時間倒轉穿越回2007年年底
一覺醒來,我還是照常去上班,走到西溪濕地附近,馬路沒有,高樓沒有,有的是小山坡和金色的稻田。一番打聽之後,才知道此時沒有什麼西溪園區。沒辦法,硬着頭皮去濱江上班,一刷卡,才發現我并不是我,我現在的身份是淘寶資料庫團隊的核心成員。
此時全國上下在迎接着奧運的到來,一片祥和。淘寶網成交額突破400億,日活使用者達1000萬。工程師們都非常興奮,磨刀霍霍。但是也遇到了棘手的問題。
一 分析目前的現狀
1.1 現有業務背景
- 淘寶網給中國市場提供了全新的購物形式,在網際網路的大潮下,使用者暴增,成交量暴增,公司持續飛速增長。
- 截止2007年,淘寶網成交額突破400億,日活使用者達1000萬。
- 全天有1000萬使用者通路淘寶。而絕大多數使用者都是在網上逛,什麼也不買。
1.2 目前的問題
1.2.1 使用者體驗與回報
使用者普遍回報逛淘寶卡頓,操作延遲特别明顯。
1.2.2 分析核心原因
- 大量的使用者在浏覽商品,并不下單。這個人數和場景的比例有20:1。
-
- 說明:資料庫模式事務,寫操作會對表或者行加寫鎖,阻塞讀操作。
- 業務資料集中在一張表裡,如user表。一張表裡資料破幾千萬。查詢一條資料需要好幾秒(單表資料量太大)。
-
- 說明:一張表資料提升,必然會導緻檢索變慢, 這是必然事實。不論如何加索引或者優化都無法解決的。
- 所有表集中在一個庫裡,所有庫集中在一個機器裡。資料庫集中在一台機器上,動不動就說硬碟不夠了(單機單庫)。
-
- 說明:所有業務共用一份實體機器資源。機器存在瓶頸:磁盤和CPU不夠用且後期拓展性不佳。
1.2.3 總結問題
- 20:1讀寫比例場景。
- 單表單庫資料量太大。
- 小型機與單機場景,抗不住目前規模。
二 我要做什麼?
- 如何滿足目前每天1000萬使用者逛淘寶的需求,且使用者體驗好(最基本要求:響應快)。
- 如何滿足未來有上億使用者的通路,甚至是同時通路,且使用者體驗好(最基本要求:響應快)。
高築牆,廣積糧,積極做好準備。
提煉核心:
- 提高資料庫操作速度。
- 同時能應對未來規模變化。
三 我能做什麼?
為實作以上兩大目标,我能做什麼?
3.1 提高資料庫操作速度,通用方法
提煉常見的通用方法:
sql優化
- 排除文法問題,爛sql
- 下推優化
下推的目的:提前過濾資料 -> 減少網絡傳輸、并行計算。
- 提前過濾資料
- 小表驅動大表等
建立索引
- 查詢頻率高的熱點字段
- 區分度高的(DISTINCT column_name)/COUNT(),以主鍵為榜樣(1/COUNT())
- 長度小
- 盡量能覆寫常用查詢字段
- 注意索引失效的場景
分庫分表
- 垂直分庫分表
- 水準分庫分表
讀寫分離
緩存的使用
等等。
3.2 如何應對未來的持續變化?
必須支援動态擴容。
必須走分布式化路線,百分百不動搖。
3.3 結合定位,分析自己能做的
3.3.1 分析我們的架構定位
(1)大前提
- 我們要做通用型架構,不參與業務。
- 從軟體設計原則出發,開閉原則:對擴充開放,對修改關閉。
說明:大修改就意味着不穩定,是以:我們要做到盡可能少的修改原來的代碼。在程式需要進行拓展的時候,不能去修改原有的代碼,實作一個熱插拔的效果。
(2)目前架構現狀
淘寶網主要使用hibernate/ibatis傳統架構:
(3)分析我們的架構定位
淘寶資料庫團隊當時使用映射架構(hibernate/ibatis)作為資料庫互動入庫,為了不讓他們修改代碼,那我們隻能在ibatis/hibenate這類映射架構之下。
同時jdbc是與底層資料庫互動的Java資料庫連接配接驅動程式,是基礎能力,我們要使用它,而不是改造它。
結論:我得把TDDL安插于ibatis/jdbc之間,于是有了第一張架構圖:
3.4 總結,我們能做什麼?
結合我們的目标,通用方法,大前提以及架構定位,分析下我們能做和不能做的。
不能做的:
- 索引,因為這個是設計階段,強業務相關。與大前提沖突:我們不參與業務。
能做的:
- 文法優化
-
- 排除sql問題
-
- 分表分庫(自動水準分表,水準分庫)
-
- 讀寫分離(讀寫分離/分布式化與動态擴容)
四 我們如何做?
4.1 文法優化
為達到文法優化的目的,我們需要具備什麼能力?
簡單來說:
- 我們需要認識這個别人送出給我的sql。
- 我能拆解sql。
- 優化與重組這個sql。
專業點來說:語義分析能力。
- sql解析
- sql規則制定
- sql重組
是以:我們需要設計一個sql解析器,sql優化器。
4.1.1 解析器
解析器的核心是詞法分析、文法語義分析,也就是說來了一條 select/update/insert/delete語句,你能認識它,而且你知道下一步該怎麼處理,同時為後面的優化器打下基礎。
核心:将sql解析為一棵文法樹。
例:
SELECT id, member_id FROM wp_image WHERE member_id = ‘123’
sql文法樹:
4.1.2 優化器
核心:
- 在sql解析成sql文法樹後,使用sql優化規則(1. 文法優化 2. 下推優化), 通過對樹進行左旋,右旋,删除子樹來對文法樹進行重構sql文法樹。
- 将重構的文法樹進行周遊得到優化後的sql。
(1)文法優化
- 函數提前計算
a. id = 1 + 1 => id = 2
- 判斷永真/永假式
1 = 1 and id = 1 => id = 1
0 = 1 and id = 1 => 空結果
- 合并範圍
id > 1 or id < 5 => 永真式
id > 1 and id = 3 => id = 3
- 類型處理
id = ‘1’ => id為數字類型,自動Long.valueof(1)
create=‘2015-02-14 12:12:12’ => create為timestamp類型,解析為時間類型
(2)下推優化
- Where條件下推
select from (A) o where o.id = 1
=>
select from (A.query(id = 1))
說明:提前條件過濾,提前擷取資料,減少後期計算/IO/網絡成本。
- JOIN中非join列的條件下推
A join B on A.id = B.id where A.name = 1 and B.title = 2
=>
A.query(name = 1) join B.query(title = 2) on A.id = B.id
說明:提前過濾,減輕後期join計算成本,達到“小表驅動”的目的。
- 等值條件的推導
A join B on A.id = B.id where A.id = 1 => B.id = 1
=>
A join B.query(B.id=1) on A.id = B.id
說明:同理,提前過濾。
4.1.3 總結
- sql解析器
-
- 負責将sql語句化為sql文法樹。
- sql優化器
-
- 負責将sql文法樹利用sql優化規則,重構sql文法樹。
-
- 将sql文法樹轉化為sql語句。
4.2 分表分庫
單庫單表的問題:
幾年前,業務簡單,應用的資料比較少,表結構也不複雜。隻有一個資料庫,資料庫中的表是一張完整的表。而到了今天,2007年了,業務複雜起來了,資料量爆增,單表資料破千萬甚至上億條,一條DML語句,死慢死慢的。這種情況下加索引已不再有顯著的效果。
這個時候,資料庫效率瓶頸不是靠加索引,sql優化能搞定的。
正确出路:分表分庫,通過将表拆分,來降低單表資料量,進而提高資料庫操作效率。
分表分為:
- 垂直分表
- 水準分表
分庫分為:
- 垂直分庫
- 水準分庫
由于TDDL不參與業務,而垂直分庫分表是強業務相關的,是以TDDL暫不參與垂直分庫分表,隻在水準分庫分表方向上努力。
4.2.1 垂直分表
垂直拆分是将一張表垂直拆成多個表。往往是把常用的列獨立成一張主表。不常用的列以及特别長的列拆分成另一張拓展表。
簡單垂直分表舉例
核心要素:
- 冷熱分離,把常用的列放在一個表,不常用的放在一個表。
- 大字段列獨立存放,如描述資訊。
- 關聯關系的列緊密的放在一起。
它帶來的提升是:
- 為了避免IO争搶并減少鎖表的幾率,檢視詳情的使用者與商品資訊浏覽互不影響。
- 充分發揮熱門資料的操作效率,商品資訊的操作的高效率不會被商品描述的低效率所拖累。
4.2.2 水準分表
水準分表是在同一個資料庫内,把同一個表的資料按一定規則拆到多個表中。
簡單點的技巧:按照枚舉類型區分。
作用總結:
- 庫内的水準分表,解決了單一表資料量過大的問題,分出來的小表中隻包含一部分資料,進而使得單個表的資料量變小,提高檢索性能。
- 避免IO争搶并減少鎖表的幾率。
4.2.3 垂直分庫
垂直分庫是指按照業務将表進行分類,分布到不同的資料庫上面,每個庫可以放在不同的伺服器上,它的核心理念是專庫專用。
- 解決業務層面的耦合,業務清晰。
- 高并發場景下,垂直分庫一定程度的提升IO、資料庫連接配接數、降低單機硬體資源的瓶頸。
- 能對不同業務的資料進行分級管理、維護、監控、擴充等。
- 垂直分庫通過将表按業務分類,然後分布在不同資料庫,并且可以将這些資料庫部署在不同伺服器上,進而達到多個伺服器共同分攤壓力的效果,但是依然沒有解決單表資料量過大的問題。
4.2.4 水準分庫(TDDL 核心)
水準分庫是把同一個表的資料按一定規則拆到不同的資料庫中,每個庫可以放在不同的伺服器上。
- 解決了單庫單表資料量過大的問題,理論上解決了高并發的性能瓶頸。
水準分庫核心要解決的問題:
- 如何知道資料在哪個庫裡?- 路由問題
- 結果合并
- 全局唯一主鍵ID
- 分布式事務(暫時不支援)
4.2.5 水準分庫——問題解決
(1)自動路由算法
sql轉發:在水準拆分後,資料被分散到多張表裡。原來的一個sql需要拆解,進行轉發路由。
select * from tb1 where member_id in ('test1234', 'pavaretti17', 'abcd');
=>
select * from tb1 where member_id in ('test1234', 'pavaretti17', 'abcd');
select * from tb1 where member_id in ('abcd');
其中拆分和尋找的算法:怎麼知道對應哪個表?即自動路由算法。常見的有:固定雜湊演算法和一緻性雜湊演算法。
a)固定雜湊演算法
b)一緻性雜湊演算法
一緻性雜湊演算法在1997年由麻省理工學院提出,是一種特殊的雜湊演算法,目的是解決分布式緩存的問題。
一緻性雜湊演算法的優勢:
- 極好的應對了伺服器當機的場景。
- 很好的支援後期伺服器擴容。
- 在引入虛拟節點後:能很好的平衡各節點的資料分布。
由于一緻性雜湊演算法的優勢,此算法幾乎是所有分布式場景下使用的方案,包括mysql的分布式、redis的分布式等。
(2) 結果合并
升華:引入fork-Join,提升操作速度(多線程并發重點場景,代碼中也很常用哦)。
- 任務拆分
- 多路并行操作
(3)全局唯一主鍵
算法:基于資料庫更新+記憶體配置設定。在資料庫中維護一個ID,擷取下一個ID時,會對資料庫進行ID=ID+100 WHERE ID=XX,拿到100個ID後,在記憶體中進行配置設定。
- 優勢:簡單高效。
- 缺點:無法保證自增順序。
水準分庫分表:一拆三場景。
主鍵分隔值:1000。
- 表1新增一條資料,于是給表1配置設定1000個主鍵ID, 直到它用完。
- 同理,表2、表3在新增資料時,也給它們配置設定1000個主鍵ID。直到它用完。
- 當它們的1000個主鍵ID用完後,繼續給它們配置設定1000個即可。
- 重複下去,可保證各庫表上的主鍵不重疊,唯一。
這種産生全局唯一id的方式相當有效,保證基本的全局唯一特性和高性能的同時,可以對生成id的資料庫分機架分機房部署達到容災的目的。
4.2.6 分表分庫總結
架構師角度:
- 優先考慮緩存降低對資料庫的讀操作。
- 再考慮讀寫分離,降低資料庫寫操作。
- 最後開始資料拆分,切分模式:首先垂直(縱向)拆分、再次水準拆分。
-
- 首先考慮按照業務垂直拆分。
-
- 再考慮水準拆分:先分庫(設定資料路由規則,把資料配置設定到不同的庫中)。
- 最後再考慮分表,單表拆分到資料1000萬以内。
個人開發角度:
- 優先使用分表分庫架構(直接使用)。
- 自己垂直分表。
- 自己水準分表。
之是以先垂直拆分才水準拆分,是因為垂直拆分後資料業務清晰而且單一,更加友善指定水準的标準。
4.3 分布式化
分布式化是大潮,是大規模伺服器最後都要走的一步。
4.3.1 讀寫分離
設計讀寫分離的資料庫,有兩大意義:
- 主從隻負責各自的寫和讀,極大程度的緩解X鎖和S鎖的競争。
- 從庫可配置myisam引擎,提升查詢性能以及節約系統開銷。
說明:myisam查詢效率高于預設的innodb效率。參考:myisam和innodb的差別。
核心問題:
- 資料的備份同步問題:參考4.4.3。
- 讀寫比例支援動态設定:結合業務,如淘寶可設定為20:1。
4.3.2 容災
主備倒換:提高可靠性 > 應對個别資料庫當機場景,尤其主庫當機。
說明:DB2主庫當機後,自動将主庫轉為DB3。
- 資料的備份同步問題:binlog 參考4.4.3。
- 檢測資料庫的線上狀态:心跳機制。
4.3.3 資料備份與同步
當隻有單機或者一份資料時,一但資料庫出問題,那麼整體服務将不可用,而且更嚴重的是會造成資料損害丢失不可逆。
在讀寫分離與主備倒換的場景下,核心要解決的是多個資料庫的資料同步與備份問題。
目前主流的是采用日志備份方式(redis也類似)。
實作原理:binlog日志備份。
說明:
- 主庫負責寫操作,在資料變更時,會寫入binlog,同時通知各從庫。
- 從庫收到通知後,IO線程會主動過來讀取主庫的binlog,并寫入自己的log。
- 寫完從庫log後,通知sql線程,sql線程讀取自己的日志,寫入從庫。
4.3.4 動态擴容
動态擴容的意義在于:随着後期業務量增大,資料庫個數可以通過增多的方式來應對,而相對的改造代價很小。
擴容前:
擴容後:
核心内容:
- 在添加新庫時
-
- 注冊機器與庫
-
- 路由算法調整:固定雜湊演算法-調整模數/一緻性雜湊演算法天然支援擴容
- 可選的權重調整
-
- 修改權重,資料插入偏向于新庫5。
-
- 在各庫數量平衡時,觸發修改回原來平衡的權重,以保證後續的均衡配置設定。
五 架構成型
sql流向
下圖介紹sql從流入TDD到流入資料庫,期間TDDL各子產品對Sql的處理。
架構圖
下圖介紹了TDDL三層的位置以及作用。
核心能力圖
TDDL 核心能力,核心組建示意圖,其中标出了各子產品核心要解決功能,核心算法等。
參考
TDDL 官方文檔
http://mw.alibaba-inc.com/products/tddl/_book/TDD産品原理介紹
http://gitlab.alibaba-inc.com/middleware/tddl5-wiki/raw/master/docs/Tddl_Intro.pptTDDL(07-10年)初始版本介紹
https://wenku.baidu.com/view/9cb630ab7f1922791788e825.html阿裡雲SQL調優指南
https://help.aliyun.com/document_detail/144293.html一緻性雜湊演算法原理
https://www.cnblogs.com/lpfuture/p/5796398.htmlTDDL初期源碼(碼雲)
https://gitee.com/justwe9891/TDDLMyISAM與InnoDB 的差別(9個不同點)
https://blog.csdn.net/qq_35642036/article/details/82820178