本文原創,使用本文請注明出處。
本文總結較為淺顯,有興趣的同學可直接參考官方文檔
背景
????????為解決關系型數據庫面對海量數據由于數據量過大而導致的性能問題時,將數據進行分片是行之有效的解決方案,而將集中于單一節點的數據拆分并分別存儲到多個數據庫或表,稱為分庫分表。 分庫可以有效分散高并發量,分表雖然無法緩解并發量,但僅跨表仍然可以使用數據庫原生的ACID事務。而一旦跨庫,涉及到事務的問題就會變得無比復雜。
????????分庫分表一般有兩種拆分方式。按照業務拆分的方式稱為垂直拆分。
????????例如,根據業務的不同將訂單庫拆成兩個相同的數據庫,稱之為垂直拆分。垂直拆分可以緩解數據量和訪問量帶來的問題,但無法根治,如果垂直拆分之后的訂單數量依然超過單節點所能承載的閾值,則需要水平拆分來進一步處理。 將一個表中的數據按照一定的業務規則拆分至不同表和數據庫中,稱之為水平拆分。例如,原來的訂單數據在order_ds.t_order表中,如果按照訂單的user_id將訂單拆分為2個庫,再按照訂單的order_id在每個庫中分成4個表,那么拆分的結果則是order_ds_0.t_order_0,order_ds_0.t_order_1,order_ds_0.t_order_2,order_ds_0.t_order_3,order_ds_1.t_order_0,order_ds_1.t_order_1,order_ds_1.t_order_2,order_ds_1.t_order_3。 這只是簡單的水平拆分案例,在實際使用中,將庫和表拆分的更加分散也是十分常見的。
????????雖然數據分片解決了性能問題,但也額外的引入了其他問題。面對如此散亂的分庫分表之后的數據,應用開發和運維人員對數據庫的操作變得異常繁重就是其中的重要挑戰之一。他們需要知道什么樣的數據需要從哪個具體的數據庫的分表中去獲取。透明化分庫分表所帶來的影響,讓使用方盡量像使用一個數據庫一樣使用水平拆分之后的數據庫,是分庫分表中間件的主要功能。
分片的概念
分片鍵
用于分片的數據庫字段,是將數據庫(表)水平拆分的關鍵字段。例:訂單表訂單ID分片尾數取模分片,則訂單ID為分片字段。SQL中如果無分片字段,將執行全路由,性能較差,支持多分片字段。
分片算法
通過分片算法將數據分片,支持通過等號、BETWEEN和IN分片。分片算法需要應用方開發者自行實現,可實現的靈活度非常高。
目前提供4種分片算法。由于分片算法和業務實現緊密相關,因此并未提供內置分片算法,而是通過分片策略將各種場景提煉出來,提供更高層級的抽象,并提供接口讓應用開發者自行實現分片算法。
精確分片算法
對應PreciseShardingAlgorithm,用于處理使用單一鍵作為分片鍵的=與IN進行分片的場景。需要配合StandardShardingStrategy使用。
范圍分片算法
對應RangeShardingAlgorithm,用于處理使用單一鍵作為分片鍵的BETWEEN AND進行分片的場景。需要配合StandardShardingStrategy使用。
復合分片算法
對應ComplexKeysShardingAlgorithm,用于處理使用多鍵作為分片鍵進行分片的場景,多分片鍵邏輯較復雜,需要應用開發者自行處理其中的復雜度。需要配合ComplexShardingStrategy使用。
Hint分片算法
對應HintShardingAlgorithm,用于處理使用Hint行分片的場景。需要配合HintShardingStrategy使用。
分片策略
包含分片鍵和分片算法,由于分片算法的獨立性,將其獨立抽離。真正可用于分片操作的是分片鍵 + 分片算法,也就是分片策略。目前提供5種分片策略。
標準分片策略
對應StandardShardingStrategy。提供對SQL語句中的=, IN和BETWEEN AND的分片操作支持。StandardShardingStrategy只支持單分片鍵,提供PreciseShardingAlgorithm和RangeShardingAlgorithm兩個分片算法。PreciseShardingAlgorithm是必選的,用于處理=和IN的分片。RangeShardingAlgorithm是可選的,用于處理BETWEEN AND分片,如果不配置RangeShardingAlgorithm,SQL中的BETWEEN AND將按照全庫路由處理。
復合分片策略
對應ComplexShardingStrategy。復合分片策略。提供對SQL語句中的=, IN和BETWEEN AND的分片操作支持。ComplexShardingStrategy支持多分片鍵,由于多分片鍵之間的關系復雜,因此并未進行過多的封裝,而是直接將分片鍵值組合以及分片操作符交于算法接口,完全由應用開發者實現,提供最大的靈活度。
行表達式分片策略
對應InlineShardingStrategy。使用Groovy的表達式,提供對SQL語句中的=和IN的分片操作支持,只支持單分片鍵。對于簡單的分片算法,可以通過簡單的配置使用,從而避免繁瑣的Java代碼開發,如:?t_user_${u_id % 8}?表示t_user表按照u_id按8取模分成8個表,表名稱為t_user_0到t_user_7。
Hint分片策略
對應HintShardingStrategy。通過Hint而非SQL解析的方式分片的策略。
不分片策略
對應NoneShardingStrategy。不分片的策略。
Hint
對于分片字段非SQL決定,而由其他外置條件決定的場景,可使用SQL Hint靈活的注入分片字段。例:內部系統,按照員工登錄ID分庫,而數據庫中并無此字段。SQL Hint支持通過Java API和SQL注釋(待實現)兩種方式使用。
實踐
首先建立兩個相同單命名不同的庫
然后在項目中(本文使用spring boot框架)
引入maven依賴
基于Java編碼的分庫規則配置(DataConfig.java)
查詢語句(更新語句同理)
當執行查詢或更新語句時,系統會根據DataConfig中的配置路由到對應的庫下面的表中,“platformId”、“orderId”是路由的關鍵,“platformId”、“orderId”作為分片鍵,必須是唯一的。
分布式主鍵的概念
實現動機
? ? ? ?傳統數據庫軟件開發中,主鍵自動生成技術是基本需求。而各個數據庫對于該需求也提供了相應的支持,比如MySQL的自增鍵,Oracle的自增序列等。 數據分片后,不同數據節點生成全局唯一主鍵是非常棘手的問題。同一個邏輯表內的不同實際表之間的自增鍵由于無法互相感知而產生重復主鍵。 雖然可通過約束自增主鍵初始值和步長的方式避免碰撞,但需引入額外的運維規則,使解決方案缺乏完整性和可擴展性。
????????目前有許多第三方解決方案可以完美解決這個問題,如UUID等依靠特定算法自生成不重復鍵,或者通過引入主鍵生成服務等。 但也正因為這種多樣性導致了Sharding-Sphere如果強依賴于任何一種方案就會限制其自身的發展。
基于以上的原因,Sharding-Sphere最終采用以接口來實現對于生成主鍵的訪問,而將底層具體的主鍵生成實現分離出來。
默認分布式主鍵生成器
采用snowflake算法實現,生成的數據為64bit的長整型數據。
其二進制表示形式包含四部分,從高位到低位分表為:1bit符號位(為0),41bit時間位,10bit工作進程位,12bit序列位。
該算法保證不同進程的主鍵肯定是不同的,同一個進程首先是通過時間位保證不重復,如果時間相同則是通過序列位保證。 同時由于時間位是單調遞增的,且各個服務器如果大體做了時間同步,那么生成的主鍵在分布式環境可以認為是總體有序的,這就保證了對索引字段的插入的高效性。例如MySQL的Innodb存儲引擎的主鍵。
在數據庫中應該用大于等于64bit的數字類型的字段來保存該值,比如在MySQL中應該使用BIGINT。
類名稱:io.shardingjdbc.core.keygen.DefaultKeyGenerator
時間位(41bit)
從2016年11月1日零點到現在的毫秒數,時間可以使用到2156年,滿足大部分系統的要求。
工作進程位(10bit)
該標志在Java進程內是唯一的,如果是分布式應用部署應保證每個進程的工作進程Id是不同的。該值默認為0,可通過調用靜態方法DefaultKeyGenerator.setWorkerId("xxxx")設置。
序列位(12bit)
該序列是用來在同一個毫秒內生成不同的Id。如果在這個毫秒內生成的數量超過4096(2的12次方),那么生成器會等待到下個毫秒繼續生成。