簡介: 為什么要冷熱分離由於2020疫情的原因,在線教育行業提前被大家所重視,釘釘教育已經服務超過21萬所學校、700萬教師和1.4億學生用戶,每天大量的教育數據產生。整體數據量:隨着時間的積累,數據量越來直大,龐大的數據量對穩定性與性能是一個很大的挑戰。當前策略:分庫分表,對於大單表的場景,第一個能跳出腦海的就是分庫分表。在中國互聯網技術圈流傳着這么一個說法:MySQL 單表數據量大於 2000 萬行,
為什么要冷熱分離
由於2020疫情的原因,在線教育行業提前被大家所重視,釘釘教育已經服務超過21萬所學校、700萬教師和1.4億學生用戶,每天大量的教育數據產生。
-
整體數據量:隨着時間的積累,數據量越來直大,龐大的數據量對穩定性與性能是一個很大的挑戰。
-
當前策略:分庫分表,對於大單表的場景,第一個能跳出腦海的就是分庫分表。
在中國互聯網技術圈流傳着這么一個說法:MySQL 單表數據量大於 2000 萬行,性能會明顯下降。事實上,這個傳聞據說最早起源於百度。具體情況大概是這樣的,當年的 DBA 測試 MySQL性能時發現,當單表的量在 2000 萬行量級的時候,SQL 操作的性能急劇下降,因此,結論由此而來。然后又據說百度的工程師流動到業界的其它公司,隨之也帶去了這個信息,所以,就在業界流傳開這么一個說法。再后來,阿里巴巴《Java 開發手冊》提出單表行數超過 500 萬行或者單表容量超過2GB,才推薦進行分庫分表。對此,有阿里的黃金鐵律支撐,所以,很多人設計大數據存儲時,多會以此為標准,進行分表操作。
有業界傳說和阿里巴巴的開發手冊支撐,這個結論應該是靠譜的,畢竟實踐出真知,但這背后的原理是什么呢,目前我們用的MYSQL大部分都是InnoDB引擎,現在我們就從InnoDB引擎說起來扒一扒為什么單表數據在2000W+后會明顯下降。
-
最小儲存單元:InnoDB存儲引擎最小儲存單元就是頁(Page),頁可以用於存放數據也可以用於存放鍵值+指針,一個頁的大小默認是16K。也就是說InnoDB中不管你的數量量是多少,最終占用的存儲空間肯定是16K的整數倍。
-
InnoDB索引結構:為什么在關心索引結構呢,因為在千萬級的數據查詢中如果沒有索引,根本就沒法查詢,索引的數據結構直接影響我們的查詢效率。InnoDB的索引結構是B+樹,B+樹的特點是葉子節點存放數據,非葉子結點存放鍵值+指針。[這里我就不再分析MYSQL的索引原理了,感興趣的同學可以看我的另一篇關於MYSQL索引原理解析的文章。]
-
B+樹數據存儲計算:這里假設單條紀錄的數據大小為1K(一般的業務數據記錄也就在1K左右),那么單個葉子結節所能存儲的紀錄數:16K/1K=16。非葉子節點能夠存儲多少指針呢?一般我們的主鍵ID都是bigint類型,長度為8字節,而指針大小在InnoDB源碼中設置為6字節,這樣鍵值+指針占用的大小就是14字節,一頁能夠存儲的指針數:16K/14=1170。那么一棵高度為2的B+樹能夠存放的紀錄數:1170*16=18720,一棵高度為3的B+樹能夠存放的紀錄數:1170*1170*16=21902400。在查找數據時,一次頁的查找代表一次IO,而IO的字數又和B+樹的高度有關,如果B+樹為3層,那么通過主鍵索引數據時就需要3次IO,而IO的代價是非常高的,一般要控制在3以下,所以說一量數據量達到2000W+,那么B+樹的高度將會變成4,從而導致每次主鍵索引都需要4次IO,IO次數的增加導致性能明顯下降。
總結一下:單表數據量越大,B+樹高度越高,查詢需要IO次數越多,性能越差。這里的幾個分界值就是2W和2000W,也就是說1000W和100W通過主鍵來索引的性能其實是差不多的,都需要2次IO。
那么在分庫分表后單表數據量依然是2000W+,這種場景怎么破局呢?從上面的分析中我們可以推導出一個MYSQL的性能公式,還是以紀錄大小為1K為例:
MYSQL單表查詢性能指數 = 單表數據量/(頁大小/14)*(頁大小/1K) ,這個性能指數越大,性能就越低。那么在降低這個性能指數就只有兩種方法:
-
降低分子:降低單表數據量。
-
增大分母:調大InnoDB的頁大小。但是InnoDB頁的大小還涉及表掃描的查詢和批量更新等 DML 操作。對於涉及許多小寫操作的 OLTP 工作負載,保持 InnoDB 頁面大小接近存儲設備塊大小,可以最大限度地減少被重寫到磁盤的未更改數據量。當頁大小被調大后單個頁面包含的數據行也會更大,出現頁面爭用的概率就會變大,而且MySQL讀取數據的最小單位就是page,如果設置過大,對順序讀可能性能會有提升,但是對於隨機讀會極大增加負載。
我們的業務場景是CRUD比較高頻且數據量比較小,同時數據的時效性要求又比較高,比如發布的作業,一般時效就是最近2天,很少有人說我要去看下我半年前的作業,然后再做一下。綜合考慮下來,降低分子即降低單表數據量是能夠有效提升查詢性能和穩定性的可靠途徑。於是冷熱分離應運而生。
冷熱分離的好處:
-
降低MYSQL單表數據量,提升MYSQL的單表性能。
-
大量業務冷數據轉冷存,存儲成本相比MYSQL可以降低很多,至少50%+。
什么是OTS
OTS表格存儲(Tablestore)是阿里雲自研的多模型結構化數據存儲,提供海量結構化數據存儲以及快速的查詢和分析服務。表格存儲的分布式存儲和強大的索引引擎能夠支持PB級存儲、千萬TPS以及毫秒級延遲的服務能力。
OTS的幾個核心特性:
-
全托管
-
表格存儲是一種全托管的結構化數據存儲,無需擔心軟硬件預置、配置、故障、集群擴展、安全等問題,在保證高服務可用性的同時,極大地減少了管理及運維成本。
-
模型豐富:表格存儲支持多種數據模型,包括Wide column、Timeline、Timestream、Grid。
-
Wide column模型:一款經典模型,目前絕大部分半結構化、結構化數據都存儲在Wide column模型系統中。
-
Timeline模型:表格存儲自研模型,主要用於消息數據,適用於IM、Feed和物聯網設備消息下推等消息系統中消息的存儲和同步,目前已被廣泛使用。
-
Timestream模型:適用於時序數據、時空數據等核心數據場景。
-
Grid模型:適用於科學大數據的存儲和查詢場景。
-
無縫擴展
-
表格存儲通過數據分片和負載均衡技術,實現了存儲無縫擴展。隨着表數據量的不斷增大,表格存儲會進行數據分區的調整從而為該表配置更多的存儲。表格存儲可支持不少於10 PB數據存儲量,單表可支持不少於1 PB數據存儲量或1萬億條記錄。
-
查詢能力強:除了支持主鍵查詢,表格存儲還支持二級索引、多元索引。
-
二級索引:相當於給數據表提供了另外一種排序方式,即對查詢條件預先設計了一種數據分布,可加快數據查詢的效率。
-
多元索引:基於倒排索引和列式存儲,支持多字段自由組合查詢、模糊查詢、地理位置查詢、全文檢索等,可解決大數據的復雜查詢難題。
-
高可靠
-
表格存儲將數據的多個備份存儲在不同機架的不同機器上,並會在備份失效時進行快速恢復,提供99.99999999%(10個9)的可靠性。
-
數據強一致
-
表格存儲保證數據寫入強一致,並保證數據3副本均寫入磁盤,且所有數據保持一致。寫操作一旦返回成功,應用程序就能立即讀到最新的數據。
-
高並發讀寫:表格存儲支持千萬級並發讀寫能力。
冷熱分離技術方案
技術架構
冷熱遷移方案
做業務最重要的就是穩定,為了保證在進行冷熱數據分離過程中的系統穩定,在數據遷移的過程中一定要做到:可灰度[降低影響,提前發現],可一鍵回滾[快速止血]。
1.冷數據遷移
-
通過DTS任務,根據業務規則掃描待遷移業務數據。
-
給遷移成功的業務數據打標,標識數據已經遷移到OTS。[此時數據在OTS和MYSQL中都存在]
2.對數據查詢服務進行灰度驗證。
-
數據查詢符合預期:驗證通過。
-
數據查詢不符合預期:一鍵回滾並修復問題。
3.通過DTS任務掃描出已經遷移的業務數據並進行邏輯刪除。[此時數據在OTS和MYSQL中都存在]
4.對已經邏輯刪除的用戶進行灰度驗證。
-
數據查詢符合預期:驗證通過。
-
數據查詢不符合預期:一鍵回滾並修復問題。
5.開啟DTS任務中的物理刪除開關。
-
針對已經遷移到OTS的數據進行物理刪除,釋放MYSQL存儲空間。
6.擴大灰度放量,觀察,直至全量。
從MYSQL遷移到OTS的實戰總結
表的映射
MYSQL是結構化數據存儲,如果業務需要平滑遷移的話,可以將MYSQL中的表映射到OTS中的寬表,寬表具體結構化數據功能。
與MYSQL相比的不同點:
-
建表時可以設置數據版本數,當屬性列數據的版本個數超過設置的最大版本數時,系統會自動刪除較早版本的。這里有一個點要特別注意下,如果表需要設置索引的話,最大的數據版本則必須設置為1。
-
建表時可以設置數據的生命周期,過期的數據系統會自動清理。這個點感覺挺好的,對於一些超過一定時間就不需要的流水數據就可以設置TTL,保證表空間大小的穩定性。
-
字段類型只支持 INTEGER(64位),DOUBLE,BOOLEAN,STRING,BINARY這5種類型,對於不支持的類型,需要用戶自己進行轉換。比如將Date轉換成INTEGER或String。主鍵列只支持INTEGER,STRING,BINARY這三種類型。
OTS限制:
-
主鍵列個數不能超過4個。
-
主鍵的第一個列為分區列,相同的分區值無法再做切分。單個分區值下的所有行大小不能超過10GB 。
寬表建表語句示例:
TableMeta tableMeta = new TableMeta("user_xxx");
//添加主鍵列
tableMeta.addPrimaryKeyColumn(new PrimaryKeySchema("user_xxx", PrimaryKeyType.STRING));
tableMeta.addPrimaryKeyColumn(new PrimaryKeySchema("org_xxx", PrimaryKeyType.INTEGER));
tableMeta.addPrimaryKeyColumn(new PrimaryKeySchema("task_xxx", PrimaryKeyType.INTEGER));
//添加屬性列
tableMeta.addDefinedColumn(new DefinedColumnSchema("xxx", DefinedColumnType.INTEGER));
tableMeta.addDefinedColumn(new DefinedColumnSchema("xxxx", DefinedColumnType.INTEGER));
tableMeta.addDefinedColumn(new DefinedColumnSchema("xxxxx", DefinedColumnType.STRING));
tableMeta.addDefinedColumn(new DefinedColumnSchema("xxxxxx", DefinedColumnType.STRING));
....
int timeToLive = -1; //數據的過期時間,單位為秒,-1表示永不過期。帶索引表的數據表數據生命周期必須設置為-1。
int maxVersions = 1; //保存的最大版本數,1表示每列上最多保存一個版本即保存最新的版本。帶索引表的數據表最大版本數必須設置為1。
TableOptions tableOptions = new TableOptions(timeToLive, maxVersions);
ArrayList<IndexMeta> indexMetas = new ArrayList<IndexMeta>();
//任務在班級維度的索引
IndexMeta indexMeta = new IndexMeta("idx_task_xxx");
indexMeta.addPrimaryKeyColumn("task_xxx"); //為索引表添加主鍵列。
indexMeta.addPrimaryKeyColumn("org_xxx"); //為索引表添加主鍵列。
indexMeta.addPrimaryKeyColumn("user_xxx"); //為索引表添加主鍵列。
indexMetas.add(indexMeta);
CreateTableRequest request = new CreateTableRequest(tableMeta, tableOptions, indexMetas); //創建數據表的同時創建索引表。
otsClient.createTable(request);
索引的映射
MYSQL中的索引映射到OTS則有三種索引。一種是全局二級索引,一種是局部索引,一種是多元索引。
-
全局二級索引:主要作用是對主鍵字段進行重排序,同時也可以添加一些非主鍵元素加入到全局二級索引中來進行數據查詢。
-
局部索引:與全局索引類似,只是索引的第一個字段必須與主鍵的第一個字段一樣。因為第一個字段決定了數據分區,局部索引實際上就是這個數據分區內的索引。
-
多元索引:索引字段可以是表中多個字段的自由組合。
全局二級索引和局部索引和MYSQL一樣,查詢匹配時需要滿足左匹配原則才能命中索引。
與MYSQL相比的不同點:
-
全局二級索引其本質上是一張數據表,只不過表中的數據是由OTS自動關聯的。同時索引不會自動回表,需要用戶手動回表。
-
全局二級索引查詢時都只能查詢出索引中有的字段,而不能查詢出索引表中沒有的字段。比如索引由字段A,B,C組織,那查詢結果集也只能從A,B,C這三個字段組成的集合中取。
-
全局二級索引必須包含主鍵的全部字段,這個點剛用起來很不習慣,因為MYSQL中的索引是可以由表中的字段任意組合的。但是后面一想也是合理的,因為目前OTS的索引不會自動回表,需要用戶手動進行回表操作,如果你的索引中沒有包含主鍵的全部字段,那么就會導致無法回表。
-
多元索引是超越MYSQL索引的存在,任意一個索引列命中即會使用索引,無需滿足左匹配,這一點很強大。而且目前多元索引支持列數已達500列,基本上能夠滿足絕大部分的業務需求了。另外,多元索引也能自動回表,十分強大。但是一分錢一分貨,這么強大的多元索引相比於全局二級索引,價格上也要更貴,差不多是3-5倍的樣子,具體和數據量有一定關系,量大從優。
OTS限制:
-
全局二級索引與多元索引數據同步為異步,存在數據延遲。也就是說某一條數據寫入成功了,但馬上通過多元索引去查詢可能查不到數據。正常情況下,同步時延在毫秒級。
-
單張數據表最多創建5個全局二級索引。
-
有了索引后,數據不支持多版本。
CRUD映射
MYSQL中的常用查詢映射到OTS為4種查詢:主鍵查詢、批量查詢、范圍查詢、索引查詢。但是相比於MYSQL,OTS的查詢在靈活性和易用性方便會差比較多。
與MYSQL相比的不同點:
-
不支持類SQL,需要通過API進行編碼查詢。這里存在一定的熟悉成本,大概是1-2個人/日的樣子。
-
批量查詢的返回需要手動過濾:查詢參數列表包含10個參數,查詢結果中也會包含10個返回對象,但是這10個返回對象中可能只有5條是有數據的,另5個沒數據的需要通過返回對象中的狀態進行手動過濾。
-
排序返回的結果集中數據是按主鍵升序排列的。舉例說明:數據1{主鍵:1,排序值:10},數據2{主鍵:2,排序值:13},數據3{主鍵:3,排序值:12},通過過濾條件按排序值升序得到數據1,數據2,數據3,查詢返回的數據列表中的數據順序是數據1,數據2,數據3,而不是按排序值排序的數據2,數據3,數據1 。排序過濾只能確保數據的准確性,不能確保數據的返回順序。需要用戶在內存中針對結果集再進行一次排序。
-
無法直接支持分頁查詢,只能通過范圍查詢來實現分頁查詢。范圍查詢必須指定開始值和結束值,但是分頁查詢時只知道開始值,這里OTS提供了一個占位符PrimaryKeyValue.INF_MAX 來表示最大值,從而實現分頁查詢邏輯。
OTS限制:
-
批量查詢1次最多查詢100條數據。
-
批量寫入一次最多寫入200條數據。
-
批量寫入一次數據大小不能超過4MB。
-
范圍查詢一次掃描的數據范圍不能超過5000條或4MB,超出上限會被截斷。也就是說范圍查詢只能保證在范圍查詢內的邏輯准確。如果范圍查詢時匹配的數據超過限制,則需要多次拉取並在內存中進行數據過濾。
-
數據過濾器個數不能超過10個。
給OTS的建議
-
支持類SQL,降低業務遷移的熟悉成本。[OTS團隊反饋已有計划推出類SQL能力,點贊]
-
支持自動回表,避免業務手工回表,進一步降低業務的接入難度。[OTS團隊反饋續會進行優化,由OTS自動回表,點贊]
-
進一步降低多元索引成本,現在的多元索引是真貴,一般人用不起,但是多元索引也是真香功能強大,如果把這個成本降低到和全局二級索引一樣的成本,那OTS的競爭力就很強大了。[OTS團隊反饋年內會解決多元索引的成本問題,這樣的話OTS的查詢能力都能秒殺MYSQL了]
致謝
感謝OTS團隊 @十品 @無維 @木洛 @翰哲 在接入過程中的耐心指導,為你們的專業精神點贊,正是你們的專業服務讓我們堅定的選擇了OTS。
感謝DBA @昭升 在數據遷移方案中的專業指導。
希望本文能夠為大家在選擇OTS做冷熱分離時提供些幫助和指引,讓大家少走一些彎路,這樣的話這篇文章就很有價值了。
本文為阿里雲原創內容,未經允許不得轉載。