本文原創(chuàng),使用本文請注明出處。
本文總結(jié)較為淺顯,有興趣的同學(xué)可直接參考官方文檔
背景
????????為解決關(guān)系型數(shù)據(jù)庫面對海量數(shù)據(jù)由于數(shù)據(jù)量過大而導(dǎo)致的性能問題時,將數(shù)據(jù)進行分片是行之有效的解決方案,而將集中于單一節(jié)點的數(shù)據(jù)拆分并分別存儲到多個數(shù)據(jù)庫或表,稱為分庫分表。 分庫可以有效分散高并發(fā)量,分表雖然無法緩解并發(fā)量,但僅跨表仍然可以使用數(shù)據(jù)庫原生的ACID事務(wù)。而一旦跨庫,涉及到事務(wù)的問題就會變得無比復(fù)雜。
????????分庫分表一般有兩種拆分方式。按照業(yè)務(wù)拆分的方式稱為垂直拆分。
????????例如,根據(jù)業(yè)務(wù)的不同將訂單庫拆成兩個相同的數(shù)據(jù)庫,稱之為垂直拆分。垂直拆分可以緩解數(shù)據(jù)量和訪問量帶來的問題,但無法根治,如果垂直拆分之后的訂單數(shù)量依然超過單節(jié)點所能承載的閾值,則需要水平拆分來進一步處理。 將一個表中的數(shù)據(jù)按照一定的業(yè)務(wù)規(guī)則拆分至不同表和數(shù)據(jù)庫中,稱之為水平拆分。例如,原來的訂單數(shù)據(jù)在order_ds.t_order表中,如果按照訂單的user_id將訂單拆分為2個庫,再按照訂單的order_id在每個庫中分成4個表,那么拆分的結(jié)果則是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。 這只是簡單的水平拆分案例,在實際使用中,將庫和表拆分的更加分散也是十分常見的。
????????雖然數(shù)據(jù)分片解決了性能問題,但也額外的引入了其他問題。面對如此散亂的分庫分表之后的數(shù)據(jù),應(yīng)用開發(fā)和運維人員對數(shù)據(jù)庫的操作變得異常繁重就是其中的重要挑戰(zhàn)之一。他們需要知道什么樣的數(shù)據(jù)需要從哪個具體的數(shù)據(jù)庫的分表中去獲取。透明化分庫分表所帶來的影響,讓使用方盡量像使用一個數(shù)據(jù)庫一樣使用水平拆分之后的數(shù)據(jù)庫,是分庫分表中間件的主要功能。
分片的概念
分片鍵
用于分片的數(shù)據(jù)庫字段,是將數(shù)據(jù)庫(表)水平拆分的關(guān)鍵字段。例:訂單表訂單ID分片尾數(shù)取模分片,則訂單ID為分片字段。SQL中如果無分片字段,將執(zhí)行全路由,性能較差,支持多分片字段。
分片算法
通過分片算法將數(shù)據(jù)分片,支持通過等號、BETWEEN和IN分片。分片算法需要應(yīng)用方開發(fā)者自行實現(xiàn),可實現(xiàn)的靈活度非常高。
目前提供4種分片算法。由于分片算法和業(yè)務(wù)實現(xiàn)緊密相關(guān),因此并未提供內(nèi)置分片算法,而是通過分片策略將各種場景提煉出來,提供更高層級的抽象,并提供接口讓應(yīng)用開發(fā)者自行實現(xiàn)分片算法。
精確分片算法
對應(yīng)PreciseShardingAlgorithm,用于處理使用單一鍵作為分片鍵的=與IN進行分片的場景。需要配合StandardShardingStrategy使用。
范圍分片算法
對應(yīng)RangeShardingAlgorithm,用于處理使用單一鍵作為分片鍵的BETWEEN AND進行分片的場景。需要配合StandardShardingStrategy使用。
復(fù)合分片算法
對應(yīng)ComplexKeysShardingAlgorithm,用于處理使用多鍵作為分片鍵進行分片的場景,多分片鍵邏輯較復(fù)雜,需要應(yīng)用開發(fā)者自行處理其中的復(fù)雜度。需要配合ComplexShardingStrategy使用。
Hint分片算法
對應(yīng)HintShardingAlgorithm,用于處理使用Hint行分片的場景。需要配合HintShardingStrategy使用。
分片策略
包含分片鍵和分片算法,由于分片算法的獨立性,將其獨立抽離。真正可用于分片操作的是分片鍵 + 分片算法,也就是分片策略。目前提供5種分片策略。
標(biāo)準(zhǔn)分片策略
對應(yīng)StandardShardingStrategy。提供對SQL語句中的=, IN和BETWEEN AND的分片操作支持。StandardShardingStrategy只支持單分片鍵,提供PreciseShardingAlgorithm和RangeShardingAlgorithm兩個分片算法。PreciseShardingAlgorithm是必選的,用于處理=和IN的分片。RangeShardingAlgorithm是可選的,用于處理BETWEEN AND分片,如果不配置RangeShardingAlgorithm,SQL中的BETWEEN AND將按照全庫路由處理。
復(fù)合分片策略
對應(yīng)ComplexShardingStrategy。復(fù)合分片策略。提供對SQL語句中的=, IN和BETWEEN AND的分片操作支持。ComplexShardingStrategy支持多分片鍵,由于多分片鍵之間的關(guān)系復(fù)雜,因此并未進行過多的封裝,而是直接將分片鍵值組合以及分片操作符交于算法接口,完全由應(yīng)用開發(fā)者實現(xiàn),提供最大的靈活度。
行表達(dá)式分片策略
對應(yīng)InlineShardingStrategy。使用Groovy的表達(dá)式,提供對SQL語句中的=和IN的分片操作支持,只支持單分片鍵。對于簡單的分片算法,可以通過簡單的配置使用,從而避免繁瑣的Java代碼開發(fā),如:?t_user_${u_id % 8}?表示t_user表按照u_id按8取模分成8個表,表名稱為t_user_0到t_user_7。
Hint分片策略
對應(yīng)HintShardingStrategy。通過Hint而非SQL解析的方式分片的策略。
不分片策略
對應(yīng)NoneShardingStrategy。不分片的策略。
Hint
對于分片字段非SQL決定,而由其他外置條件決定的場景,可使用SQL Hint靈活的注入分片字段。例:內(nèi)部系統(tǒng),按照員工登錄ID分庫,而數(shù)據(jù)庫中并無此字段。SQL Hint支持通過Java API和SQL注釋(待實現(xiàn))兩種方式使用。
實踐
首先建立兩個相同單命名不同的庫

然后在項目中(本文使用spring boot框架)
引入maven依賴

基于Java編碼的分庫規(guī)則配置(DataConfig.java)

查詢語句(更新語句同理)

當(dāng)執(zhí)行查詢或更新語句時,系統(tǒng)會根據(jù)DataConfig中的配置路由到對應(yīng)的庫下面的表中,“platformId”、“orderId”是路由的關(guān)鍵,“platformId”、“orderId”作為分片鍵,必須是唯一的。
分布式主鍵的概念
實現(xiàn)動機
? ? ? ?傳統(tǒng)數(shù)據(jù)庫軟件開發(fā)中,主鍵自動生成技術(shù)是基本需求。而各個數(shù)據(jù)庫對于該需求也提供了相應(yīng)的支持,比如MySQL的自增鍵,Oracle的自增序列等。 數(shù)據(jù)分片后,不同數(shù)據(jù)節(jié)點生成全局唯一主鍵是非常棘手的問題。同一個邏輯表內(nèi)的不同實際表之間的自增鍵由于無法互相感知而產(chǎn)生重復(fù)主鍵。 雖然可通過約束自增主鍵初始值和步長的方式避免碰撞,但需引入額外的運維規(guī)則,使解決方案缺乏完整性和可擴展性。
????????目前有許多第三方解決方案可以完美解決這個問題,如UUID等依靠特定算法自生成不重復(fù)鍵,或者通過引入主鍵生成服務(wù)等。 但也正因為這種多樣性導(dǎo)致了Sharding-Sphere如果強依賴于任何一種方案就會限制其自身的發(fā)展。
基于以上的原因,Sharding-Sphere最終采用以接口來實現(xiàn)對于生成主鍵的訪問,而將底層具體的主鍵生成實現(xiàn)分離出來。
默認(rèn)分布式主鍵生成器
采用snowflake算法實現(xiàn),生成的數(shù)據(jù)為64bit的長整型數(shù)據(jù)。
其二進制表示形式包含四部分,從高位到低位分表為:1bit符號位(為0),41bit時間位,10bit工作進程位,12bit序列位。
該算法保證不同進程的主鍵肯定是不同的,同一個進程首先是通過時間位保證不重復(fù),如果時間相同則是通過序列位保證。 同時由于時間位是單調(diào)遞增的,且各個服務(wù)器如果大體做了時間同步,那么生成的主鍵在分布式環(huán)境可以認(rèn)為是總體有序的,這就保證了對索引字段的插入的高效性。例如MySQL的Innodb存儲引擎的主鍵。
在數(shù)據(jù)庫中應(yīng)該用大于等于64bit的數(shù)字類型的字段來保存該值,比如在MySQL中應(yīng)該使用BIGINT。
類名稱:io.shardingjdbc.core.keygen.DefaultKeyGenerator
時間位(41bit)
從2016年11月1日零點到現(xiàn)在的毫秒數(shù),時間可以使用到2156年,滿足大部分系統(tǒng)的要求。
工作進程位(10bit)
該標(biāo)志在Java進程內(nèi)是唯一的,如果是分布式應(yīng)用部署應(yīng)保證每個進程的工作進程Id是不同的。該值默認(rèn)為0,可通過調(diào)用靜態(tài)方法DefaultKeyGenerator.setWorkerId("xxxx")設(shè)置。
序列位(12bit)
該序列是用來在同一個毫秒內(nèi)生成不同的Id。如果在這個毫秒內(nèi)生成的數(shù)量超過4096(2的12次方),那么生成器會等待到下個毫秒繼續(xù)生成。