分布式數據庫,是近些年來頗受關注的領域。一方面隨著數據規模不斷增大,數據使用場景更為多樣,對底層數據庫的要求越來越高;另一方面對數據庫的可用性、擴展能力等也都提出更高的要求。分布式數據庫的出現,恰好滿足了上述兩方面的訴求。但當用戶選擇使用分布式的第一個問題,就是如何將之前基于單機或集中式數據庫設計的數據結構遷移到分布式環境中,核心點就在于數據分片的設計。這其中的核心要點有兩個:一是選擇什么字段或字段組合作為分片鍵;二是使用什么分片算法來分片。本文嘗試說明第一個問題。
1. 是否需要設計分片
是否需要設計分片?相信是大家首要提出的問題,作為一種新架構的出現,分布式數據庫確實可以解決一些場景問題,但數據分片是在設計之初就需考慮。是否有一種更為透明的方式解決分片問題呢,這就引出一個概念—“數據分布獨立性”。它是指用戶或用戶程序使用分布式數據庫如同使用集中式數據庫那樣,不必關系全局數據的分布情況。也就是說全局數據的邏輯分片、片段的物理位置等情況對用戶和用戶應用程序式透明的。因此分布式數據庫中分布獨立性也成為分布透明性。一個分布式數據庫系統可能提供的分布透明性層次越高,用戶編寫應用程序越容易。比較理想的情況是,軟件工程師完全不必處理應用級別的分片,因為這并不是他們真正的工作。然而,理想很豐滿,現實很骨感。完全透明的數據分片,在部分場景下仍然會面臨問題,如低延遲、復雜業務、多中心等,這些場景仍然希望能夠精準控制數據分片規則甚至物理位置。因此,如何設計分片策略也就成為新環境下DBA不得不面對的問題,起碼是在相當長的時間是如此。正如同數據庫初學者需學習的范式理論一樣,未來數據分片的設計也是考驗架構、研發及DBA的基本要求之一。
這里稍微展開說明下,分布式數據庫的實現有兩種主要方式,一是分庫分表;一是NewSQL。兩者的對比可參考下圖。具體在分片上,前者通常可提供更為靈活精準的分片控制,后者則在分片易用性上更具優勢。
2. 如何選擇分片字段
數據分片的設計上需考慮兩點:一是分片字段的選擇;二是對應的分片算法。后續將重點談及分片字段選擇上,下面先簡單說明下分片算法問題。
1).分片算法
分片算法,常規的有LIST、RANGE、HASH或自定義算法。根據各拆分算法特點,可進行選擇。若范圍均勻可采用HASH,冷熱數據明顯可采用RANGE等。同時可配合一些特性化設計,如采用二級映射方式解決擴縮容問題、特征編碼字段滿足多特征拆分等。針對最為常見的兩個算法描述如下:
- RANGE
通過數據的范圍進行分庫分表,是最樸實的一種分庫方案,它也可以和其他分庫分表方案靈活結合使用。當需要使用分片字段進行范圍查找時,RANGE分片策略可快速定位數據進行高效查詢。大多數情況下有效避免跨分片查詢的問題。在后期擴容時,也比較方便,只需要添加節點即可,無需對其他分片的數據進行遷移。但這種分布方式容易存在數據熱點問題。
- HASH
雖然分庫分表的方案眾多,但是Hash分庫分表是最大眾最普遍的方案。隨機分片其實并不是隨機,也遵循一定規則。通常采用HASH取模的方式進行分片拆分,所以有時候也稱為離散分片。隨機分片的數據相對均勻,不容易出現熱點和并發訪問的瓶頸。但涉及后面數據遷移的話,不太方便。可使用一致性HASH算法在很大程度上避免此問題。此外,離散分片也容易面臨跨分片查詢的復雜問題。
2).分片字段
分片字段的選擇,需涉及的因素很多,列個腦圖分類下。下面將針對各因素詳細說明:
- 數據結構:主鍵或唯一鍵
主鍵及唯一鍵,是數據庫作為常見的約束,其是為了保證非空且唯一性。在分布式環境下,通常建議將主鍵或唯一鍵字段作為分片鍵或分片鍵的一部分,否則無法完成約束校驗;當然也有產品支持單獨約束校驗。這里有個引申問題,就是主鍵設計問題,在分布式數據庫架構下,盡量不要用自增作為表的主鍵,自增性能很差、安全性不高、不適用于分布式架構。通常可使用如UUID或全局發號器(雪花算法)。總之,用有序的全局唯一替代自增,是分布式數據庫主鍵的推薦做法。
- 數據結構:索引
通過分片鍵可以把 SQL 查詢路由到指定的分片,但是在現實的生產環境中,業務還要通過其他的索引訪問表。針對原有系統的索引需要有單獨策略。通常的策略是通過索引表的方式,即將索引轉化為另一張分片表,對于查詢來說通過二次查詢解決,但顯然這種方式不夠優雅。因此,最優的設計不是創建一個索引表,而是將索引數據融入到分片鍵的信息中,這樣通過查詢的列就能直接知道所在的分片信息。效率更高,查詢可以提前知道數據對應的分片信息,只需 一次查詢就能獲取想要的結果。總結下,索引對分片字段的選擇上,沒有直接影響。對于高頻索引查詢,可以考慮通過分片鍵的設計上進行增強。也可以通過全局二級索引(有些分布式數據庫支持)來實現或針對分片內做普通索引。
- 數據結構:字段類型
作為分片鍵的字段,通常選擇較為簡單的數據類型字段,可以提高效率,如常見的數字、日期、文本等,對復雜字段如LOB、JSON等不推薦使用。此外,在設計時需考慮分片字段的類型穩定,盡量不要發生DDL變更。
- 數據特征:表規模
表規模是是否使用分片的關鍵因素之一。一旦表做了分片后,勢必會造成一定的“功能退化”,如能采取其他方式縮小表的大小,盡量優先其他方式。可通過表的全生命周期規劃,如常規的數據歸檔、壓縮、轉儲、清理策略,減少數據量;或者利用數據庫內置的如表分區、垂直分表等策略有效減小表的大小。
- 數據特征:離散度
這里說的離散度是指按某個字段或字段組合后,應用分片算法后,數據是否足夠分散。數據分片的初衷就是減少表的規模,盡量做到數據打散是其根本原則之一。這里需要統計數據拆分后離散程度,盡量選擇能充分打散的字段作為分片鍵。這里需注意,如果選擇字段是帶有業務特征,還要關注未來業務變化對它的影響。
- 訪問特征:可變化性
選擇固定、不再變化的字段作為分片鍵。雖然有些分布式數據庫也支持分片鍵的修改,但畢竟修改后會涉及數據移動,成本代價很高;還是優選不變的字段為好。
- 訪問特征:事務隔離
盡量選擇按字段拆分后的數據,對數據的變化處理可集中在分片內解決。這樣大量的業務變化是可以通過本地事務完成,開銷比全局的要小很多,效率也高。
- 訪問特征:數據過濾與關聯
如此字段經常作為數據篩選字段被頻繁使用,且選擇率很好,可優先作為分片字段。另一種情況則是作為與其他關聯表聯合使用,優先選擇那些參與到關聯操作的字段為佳。盡量是數據在關聯后,能在本地完成join動作,減少數據shuffle或上移匯聚類的操作。可通過對系統中執行的SQL進行統計分析,選擇出需要分片那個表中最頻繁被使用到或最為重要的字段類分片。這其中可能包含一些來自OLAP類的查詢,可將此部分SQL排除在外。
- 分片字段順序
如涉及多個字段作為分片鍵的話,順序因素一般沒有什么影響。主要是針對分片算法,可利用字段做分片即可。但對于復合分片的情況,是要考慮分片字段的主次關系的。