www.久久久久|狼友网站av天堂|精品国产无码a片|一级av色欲av|91在线播放视频|亚洲无码主播在线|国产精品草久在线|明星AV网站在线|污污内射久久一区|婷婷综合视频网站

當前位置:首頁 > 公眾號精選 > 架構(gòu)師社區(qū)
[導讀]作為Sharding-JDBC 分庫分表實戰(zhàn)系列的開篇文章,我們在前文中回顧了一下分庫分表的基礎知識,對分庫分表的拆分方式有了一定的了解,下邊我們介紹一下 Sharding-JDBC框架和快速的搭建一個分庫分表案例,為講解后續(xù)功能點準備好環(huán)境。

書接上文 《一文快速入門分庫分表(必修課)》,這篇拖了好長的時間,本來計劃在一周前就該寫完的,結(jié)果家庭內(nèi)部突然人事調(diào)整,領導層進行權利交接,隨之宣布我正式當?shù)?,緊接著家庭地位滑落至第三名,還給我分配了一個長期維護任務:帶娃??纯次覀兊撵n照,標準的小淑女一枚奧利給。

必修課!一文快速入門分庫分表中間件Sharding-JDBC
千金小姐姐

作為Sharding-JDBC 分庫分表實戰(zhàn)系列的開篇文章,我們在前文中回顧了一下分庫分表的基礎知識,對分庫分表的拆分方式有了一定的了解,下邊我們介紹一下 Sharding-JDBC框架和快速的搭建一個分庫分表案例,為講解后續(xù)功能點準備好環(huán)境。

一、Sharding-JDBC 簡介

Sharding-JDBC 最早是當當網(wǎng)內(nèi)部使用的一款分庫分表框架,到2017年的時候才開始對外開源,這幾年在大量社區(qū)貢獻者的不斷迭代下,功能也逐漸完善,現(xiàn)已更名為 ShardingSphere,2020年4?16?正式成為 Apache 軟件基?會的頂級項?。

隨著版本的不斷更迭 ShardingSphere 的核心功能也變得多元化起來。從最開始 Sharding-JDBC 1.0 版本只有數(shù)據(jù)分片,到 Sharding-JDBC 2.0 版本開始支持數(shù)據(jù)庫治理(注冊中心、配置中心等等),再到 Sharding-JDBC 3.0版本又加分布式事務 (支持 Atomikos、NarayanaBitronix、Seata),如今已經(jīng)迭代到了 Sharding-JDBC 4.0 版本。

必修課!一文快速入門分庫分表中間件Sharding-JDBC
在這里插入圖片描述

現(xiàn)在的 ShardingSphere 不單單是指某個框架而是一個生態(tài)圈,這個生態(tài)圈 Sharding-JDBC、Sharding-ProxySharding-Sidecar 這三款開源的分布式數(shù)據(jù)庫中間件解決方案所構(gòu)成。

ShardingSphere 的前身就是 Sharding-JDBC,所以它是整個框架中最為經(jīng)典、成熟的組件,我們先從 Sharding-JDBC 框架入手學習分庫分表。

二、核心概念

在開始 Sharding-JDBC分庫分表具體實戰(zhàn)之前,我們有必要先了解分庫分表的一些核心概念。

分片

一般我們在提到分庫分表的時候,大多是以水平切分模式(水平分庫、分表)為基礎來說的,數(shù)據(jù)分片將原本一張數(shù)據(jù)量較大的表 t_order 拆分生成數(shù)個表結(jié)構(gòu)完全一致的小數(shù)據(jù)量表 t_order_0、t_order_1、···、t_order_n,每張表只存儲原大表中的一部分數(shù)據(jù),當執(zhí)行一條SQL時會通過 分庫策略、分片策略 將數(shù)據(jù)分散到不同的數(shù)據(jù)庫、表內(nèi)。

必修課!一文快速入門分庫分表中間件Sharding-JDBC
在這里插入圖片描述

數(shù)據(jù)節(jié)點

數(shù)據(jù)節(jié)點是分庫分表中一個不可再分的最小數(shù)據(jù)單元(表),它由數(shù)據(jù)源名稱和數(shù)據(jù)表組成,例如上圖中 order_db_1.t_order_0、order_db_2.t_order_1 就表示一個數(shù)據(jù)節(jié)點。

邏輯表

邏輯表是指一組具有相同邏輯和數(shù)據(jù)結(jié)構(gòu)表的總稱。比如我們將訂單表t_order 拆分成 t_order_0 ··· ?t_order_9 等 10張表。此時我們會發(fā)現(xiàn)分庫分表以后數(shù)據(jù)庫中已不在有 t_order 這張表,取而代之的是 t_order_n,但我們在代碼中寫 SQL 依然按 t_order 來寫。此時 t_order 就是這些拆分表的邏輯表。

真實表

真實表也就是上邊提到的 t_order_n 數(shù)據(jù)庫中真實存在的物理表。

分片鍵

用于分片的數(shù)據(jù)庫字段。我們將 t_order 表分片以后,當執(zhí)行一條SQL時,通過對字段 order_id 取模的方式來決定,這條數(shù)據(jù)該在哪個數(shù)據(jù)庫中的哪個表中執(zhí)行,此時 order_id 字段就是 t_order 表的分片健。

必修課!一文快速入門分庫分表中間件Sharding-JDBC
在這里插入圖片描述

這樣以來同一個訂單的相關數(shù)據(jù)就會存在同一個數(shù)據(jù)庫表中,大幅提升數(shù)據(jù)檢索的性能,不僅如此 sharding-jdbc 還支持根據(jù)多個字段作為分片健進行分片。

分片算法

上邊我們提到可以用分片健取模的規(guī)則分片,但這只是比較簡單的一種,在實際開發(fā)中我們還希望用 >=、<=、>、<、BETWEENIN 等條件作為分片規(guī)則,自定義分片邏輯,這時就需要用到分片策略與分片算法。

從執(zhí)行 SQL 的角度來看,分庫分表可以看作是一種路由機制,把 SQL 語句路由到我們期望的數(shù)據(jù)庫或數(shù)據(jù)表中并獲取數(shù)據(jù),分片算法可以理解成一種路由規(guī)則。

咱們先捋一下它們之間的關系,分片策略只是抽象出的概念,它是由分片算法和分片健組合而成,分片算法做具體的數(shù)據(jù)分片邏輯。

分庫、分表的分片策略配置是相對獨立的,可以各自使用不同的策略與算法,每種策略中可以是多個分片算法的組合,每個分片算法可以對多個分片健做邏輯判斷。

必修課!一文快速入門分庫分表中間件Sharding-JDBC
分片算法和分片策略的關系

注意:sharding-jdbc 并沒有直接提供分片算法的實現(xiàn),需要開發(fā)者根據(jù)業(yè)務自行實現(xiàn)。

sharding-jdbc 提供了4種分片算法:

1、精確分片算法

精確分片算法(PreciseShardingAlgorithm)用于單個字段作為分片鍵,SQL中有 =IN 等條件的分片,需要在標準分片策略(StandardShardingStrategy )下使用。

2、范圍分片算法

范圍分片算法(RangeShardingAlgorithm)用于單個字段作為分片鍵,SQL中有 BETWEEN AND、>、<、>=<= ?等條件的分片,需要在標準分片策略(StandardShardingStrategy )下使用。

3、復合分片算法

復合分片算法(ComplexKeysShardingAlgorithm)用于多個字段作為分片鍵的分片操作,同時獲取到多個分片健的值,根據(jù)多個字段處理業(yè)務邏輯。需要在復合分片策略(ComplexShardingStrategy )下使用。

4、Hint分片算法

Hint分片算法(HintShardingAlgorithm)稍有不同,上邊的算法中我們都是解析SQL 語句提取分片鍵,并設置分片策略進行分片。但有些時候我們并沒有使用任何的分片鍵和分片策略,可還想將 SQL 路由到目標數(shù)據(jù)庫和表,就需要通過手動干預指定SQL的目標數(shù)據(jù)庫和表信息,這也叫強制路由。

分片策略

上邊講分片算法的時候已經(jīng)說過,分片策略是一種抽象的概念,實際分片操作的是由分片算法和分片健來完成的。

1、標準分片策略

標準分片策略適用于單分片鍵,此策略支持 PreciseShardingAlgorithmRangeShardingAlgorithm 兩個分片算法。

其中 PreciseShardingAlgorithm 是必選的,用于處理 =IN 的分片。RangeShardingAlgorithm 是可選的,用于處理BETWEEN AND>, <,>=,<= 條件分片,如果不配置RangeShardingAlgorithm,SQL中的條件等將按照全庫路由處理。

2、復合分片策略

復合分片策略,同樣支持對 SQL語句中的 =,>, <, >=, <=,INBETWEEN AND 的分片操作。不同的是它支持多分片鍵,具體分配片細節(jié)完全由應用開發(fā)者實現(xiàn)。

3、行表達式分片策略

行表達式分片策略,支持對 SQL語句中的 =IN 的分片操作,但只支持單分片鍵。這種策略通常用于簡單的分片,不需要自定義分片算法,可以直接在配置文件中接著寫規(guī)則。

t_order_$->{t_order_id % 4} 代表 t_order 對其字段 t_order_id取模,拆分成4張表,而表名分別是t_order_0t_order_3

4、Hint分片策略

Hint分片策略,對應上邊的Hint分片算法,通過指定分片健而非從 SQL中提取分片健的方式進行分片的策略。

分布式主鍵

數(shù)據(jù)分?后,不同數(shù)據(jù)節(jié)點?成全局唯?主鍵是?常棘?的問題,同?個邏輯表(t_order)內(nèi)的不同真實表(t_order_n)之間的?增鍵由于?法互相感知而產(chǎn)?重復主鍵。

盡管可通過設置?增主鍵 初始值步? 的?式避免ID碰撞,但這樣會使維護成本加大,乏完整性和可擴展性。如果后去需要增加分片表的數(shù)量,要逐一修改分片表的步長,運維成本非常高,所以不建議這種方式。

實現(xiàn)分布式主鍵?成器的方式很多,可以參考我之前寫的《9種分布式ID生成方式》。

為了讓上手更加簡單,ApacheShardingSphere 內(nèi)置了UUID、SNOWFLAKE 兩種分布式主鍵?成器,默認使?雪花算法(snowflake)?成64bit的?整型數(shù)據(jù)。不僅如此它還抽離出分布式主鍵?成器的接口,?便我們實現(xiàn)?定義的?增主鍵?成算法。

廣播表

廣播表:存在于所有的分片數(shù)據(jù)源中的表,表結(jié)構(gòu)和表中的數(shù)據(jù)在每個數(shù)據(jù)庫中均完全一致。一般是為字典表或者配置表 t_config,某個表一旦被配置為廣播表,只要修改某個數(shù)據(jù)庫的廣播表,所有數(shù)據(jù)源中廣播表的數(shù)據(jù)都會跟著同步。

綁定表

綁定表:那些分片規(guī)則一致的主表和子表。比如:t_order 訂單表和 t_order_item 訂單服務項目表,都是按 order_id 字段分片,因此兩張表互為綁定表關系。

那綁定表存在的意義是啥呢?

通常在我們的業(yè)務中都會使用 t_ordert_order_item 等表進行多表聯(lián)合查詢,但由于分庫分表以后這些表被拆分成N多個子表。如果不配置綁定表關系,會出現(xiàn)笛卡爾積關聯(lián)查詢,將產(chǎn)生如下四條SQL

SELECT?*?FROM?t_order_0?o?JOIN?t_order_item_0?i?ON?o.order_id=i.order_id?
SELECT?*?FROM?t_order_0?o?JOIN?t_order_item_1?i?ON?o.order_id=i.order_id?
SELECT?*?FROM?t_order_1?o?JOIN?t_order_item_0?i?ON?o.order_id=i.order_id?
SELECT?*?FROM?t_order_1?o?JOIN?t_order_item_1?i?ON?o.order_id=i.order_id?
必修課!一文快速入門分庫分表中間件Sharding-JDBC
笛卡爾積查詢

而配置綁定表關系后再進行關聯(lián)查詢時,只要對應表分片規(guī)則一致產(chǎn)生的數(shù)據(jù)就會落到同一個庫中,那么只需 t_order_0t_order_item_0 表關聯(lián)即可。

SELECT?*?FROM?t_order_0?o?JOIN?t_order_item_0?i?ON?o.order_id=i.order_id?
SELECT?*?FROM?t_order_1?o?JOIN?t_order_item_1?i?ON?o.order_id=i.order_id?
必修課!一文快速入門分庫分表中間件Sharding-JDBC
綁定表關系

注意:在關聯(lián)查詢時 t_order 它作為整個聯(lián)合查詢的主表。所有相關的路由計算都只使用主表的策略,t_order_item 表的分片相關的計算也會使用 t_order 的條件,所以要保證綁定表之間的分片鍵要完全相同。

三、和JDBC的貓膩

從名字上不難看出,Sharding-JDBCJDBC有很大關系,我們知道 JDBC 是一種 Java 語言訪問關系型數(shù)據(jù)庫的規(guī)范,其設計初衷就是要提供一套用于各種數(shù)據(jù)庫的統(tǒng)一標準,不同廠家共同遵守這套標準,并提供各自的實現(xiàn)方案供應用程序調(diào)用。

必修課!一文快速入門分庫分表中間件Sharding-JDBC
在這里插入圖片描述

但其實對于開發(fā)人員而言,我們只關心如何調(diào)用 JDBC API 來訪問數(shù)據(jù)庫,只要正確使用 DataSource、ConnectionStatement 、ResultSet 等 API 接口,直接操作數(shù)據(jù)庫即可。所以如果想在 JDBC 層面實現(xiàn)數(shù)據(jù)分片就必須對現(xiàn)有的 API 進行功能拓展,而 Sharding-JDBC 正是基于這種思想,重寫了 JDBC 規(guī)范并完全兼容了 JDBC 規(guī)范。

必修課!一文快速入門分庫分表中間件Sharding-JDBC
JDBC流程

對原有的 DataSource、Connection 等接口擴展成 ShardingDataSource、ShardingConnection,而對外暴露的分片操作接口與 JDBC 規(guī)范中所提供的接口完全一致,只要你熟悉 JDBC 就可以輕松應用 Sharding-JDBC 來實現(xiàn)分庫分表。

必修課!一文快速入門分庫分表中間件Sharding-JDBC
在這里插入圖片描述

因此它適用于任何基于 JDBCORM 框架,如:JPA, HibernateMybatis,Spring JDBC Template 或直接使用的 JDBC。完美兼容任何第三方的數(shù)據(jù)庫連接池,如:DBCPC3P0, BoneCPDruid, HikariCP 等,幾乎對主流關系型數(shù)據(jù)庫都支持。

Sharding-JDBC 又是如何拓展這些接口的呢?想知道答案我們就的從源碼入手了,下邊我們以 JDBC API ?中的 DataSource 為例看看它是如何被重寫擴展的。

數(shù)據(jù)源 DataSource 接口的核心作用就是獲取數(shù)據(jù)庫連接對象 Connection,我們看其內(nèi)部提供了兩個獲取數(shù)據(jù)庫連接的方法 ,并且繼承了 CommonDataSourceWrapper 兩個接口。


public?interface?DataSource??extends?CommonDataSource,?Wrapper?{

??/**
???*?

Attempts?to?establish?a?connection?with?the?data?source?that
???*?this?{@code?DataSource}?object?represents.
???*?@return??a?connection?to?the?data?source
???*/
??Connection?getConnection()?throws?SQLException;

??/**
???*?

Attempts?to?establish?a?connection?with?the?data?source?that
???*?this?{@code?DataSource}?object?represents.
???*?@param?username?the?database?user?on?whose?behalf?the?connection?is
???*??being?made
???*?@param?password?the?user's?password
???*/
??Connection?getConnection(String?username,?String?password)
????throws?SQLException
;
}

其中 CommonDataSource 是定義數(shù)據(jù)源的根接口這很好理解,而 Wrapper 接口則是拓展 JDBC 分片功能的關鍵。

由于數(shù)據(jù)庫廠商的不同,他們可能會各自提供一些超越標準 JDBC API 的擴展功能,但這些功能非 JDBC 標準并不能直接使用,而 Wrapper 接口的作用就是把一個由第三方供應商提供的、非 JDBC 標準的接口包裝成標準接口,也就是適配器模式。

既然講到了適配器模式就多啰嗦幾句,也方便后邊的理解。

適配器模式個種比較常用的設計模式,它的作用是將某個類的接口轉(zhuǎn)換成客戶端期望的另一個接口,使原本因接口不匹配(或者不兼容)而無法在一起工作的兩個類能夠在一起工作。比如用耳機聽音樂,我有個圓頭的耳機,可手機插孔卻是扁口的,如果我想要使用耳機聽音樂就必須借助一個轉(zhuǎn)接頭才可以,這個轉(zhuǎn)接頭就起到了適配作用。舉個栗子:假如我們 Target 接口中有 hello()word() 兩個方法。

public?interface?Target?{

????void?hello();

????void?world();
}

可由于接口版本迭代Target 接口的 word() 方法可能會被廢棄掉或不被支持,Adaptee ?類的 greet()方法將代替hello() 方法。

public?class?Adaptee?{

????public?void?greet(){

????}
????public?void?world(){

????}
}

但此時舊版本仍然有大量 word() 方法被使用中,解決此事最好的辦法就是創(chuàng)建一個適配器Adapter,這樣就適配了 Target 類,解決了接口升級帶來的兼容性問題。

public?class?Adapter?extends?Adaptee?implements?Target?{

????@Override
????public?void?world()?{
????????
????}

????@Override
????public?void?hello()?{
????????super.greet();
????}

????@Override
????public?void?greet()?{
????????
????}
}

Sharding-JDBC 提供的正是非 JDBC 標準的接口,所以它也提供了類似的實現(xiàn)方案,也使用到了 Wrapper 接口做數(shù)據(jù)分片功能的適配。除了 DataSource 之外,Connection、Statement、ResultSet 等核心對象也都繼承了這個接口。

下面我們通過 ShardingDataSource ?類源碼簡單看下實現(xiàn)過程,下圖是繼承關系流程圖。

必修課!一文快速入門分庫分表中間件Sharding-JDBC
ShardingDataSource實現(xiàn)流程

ShardingDataSource ?類它在原 DataSource 基礎上做了功能拓展,初始化時注冊了分片SQL路由包裝器、SQL重寫上下文和結(jié)果集處理引擎,還對數(shù)據(jù)源類型做了校驗,因為它要同時支持多個不同類型的數(shù)據(jù)源。到這好像也沒看出如何適配,那接著向上看 ShardingDataSource 的繼承類 ?AbstractDataSourceAdapter 。

@Getter
public?class?ShardingDataSource?extends?AbstractDataSourceAdapter?{
????
????private?final?ShardingRuntimeContext?runtimeContext;

????/**
?????*?注冊路由、SQl重寫上下文、結(jié)果集處理引擎
?????*/

????static?{
????????NewInstanceServiceLoader.register(RouteDecorator.class);
????????NewInstanceServiceLoader.register(SQLRewriteContextDecorator.class);
????????NewInstanceServiceLoader.register(ResultProcessEngine.class);
????}

????/**
?????*?初始化時校驗數(shù)據(jù)源類型?并根據(jù)數(shù)據(jù)源?map、分片規(guī)則、數(shù)據(jù)庫類型得到一個分片上下文,用來獲取數(shù)據(jù)庫連接
?????*/

????public?ShardingDataSource(final?Map<String,?DataSource>?dataSourceMap,?final?ShardingRule?shardingRule,?final?Properties?props)?throws?SQLException?{
????????super(dataSourceMap);
????????checkDataSourceType(dataSourceMap);
????????runtimeContext?=?new?ShardingRuntimeContext(dataSourceMap,?shardingRule,?props,?getDatabaseType());
????}

????private?void?checkDataSourceType(final?Map<String,?DataSource>?dataSourceMap)?{
????????for?(DataSource?each?:?dataSourceMap.values())?{
????????????Preconditions.checkArgument(!(each?instanceof?MasterSlaveDataSource),?"Initialized?data?sources?can?not?be?master-slave?data?sources.");
????????}
????}

????/**
?????*?數(shù)據(jù)庫連接
?????*/

????@Override
????public?final?ShardingConnection?getConnection()?{
????????return?new?ShardingConnection(getDataSourceMap(),?runtimeContext,?TransactionTypeHolder.get());
????}
}

AbstractDataSourceAdapter 抽象類內(nèi)部主要獲取不同類型的數(shù)據(jù)源對應的數(shù)據(jù)庫連接對象,實現(xiàn) AutoCloseable 接口是為在使用完資源后可以自動將這些資源關閉(調(diào)用 close方法),那再看看繼承類 AbstractUnsupportedOperationDataSource 。

@Getter
public?abstract?class?AbstractDataSourceAdapter?extends?AbstractUnsupportedOperationDataSource?implements?AutoCloseable?{
????
????private?final?Map<String,?DataSource>?dataSourceMap;
????
????private?final?DatabaseType?databaseType;
????
????public?AbstractDataSourceAdapter(final?Map<String,?DataSource>?dataSourceMap)?throws?SQLException?{
????????this.dataSourceMap?=?dataSourceMap;
????????databaseType?=?createDatabaseType();
????}
????
????public?AbstractDataSourceAdapter(final?DataSource?dataSource)?throws?SQLException?{
????????dataSourceMap?=?new?HashMap<>(1,?1);
????????dataSourceMap.put("unique",?dataSource);
????????databaseType?=?createDatabaseType();
????}
????
????private?DatabaseType?createDatabaseType()?throws?SQLException?{
????????DatabaseType?result?=?null;
????????for?(DataSource?each?:?dataSourceMap.values())?{
????????????DatabaseType?databaseType?=?createDatabaseType(each);
????????????Preconditions.checkState(null?==?result?||?result?==?databaseType,?String.format("Database?type?inconsistent?with?'%s'?and?'%s'",?result,?databaseType));
????????????result?=?databaseType;
????????}
????????return?result;
????}
????
????/**
?????*?不同數(shù)據(jù)源類型獲取數(shù)據(jù)庫連接
?????*/

????private?DatabaseType?createDatabaseType(final?DataSource?dataSource)?throws?SQLException?{
????????if?(dataSource?instanceof?AbstractDataSourceAdapter)?{
????????????return?((AbstractDataSourceAdapter)?dataSource).databaseType;
????????}
????????try?(Connection?connection?=?dataSource.getConnection())?{
????????????return?DatabaseTypes.getDatabaseTypeByURL(connection.getMetaData().getURL());
????????}
????}
????
????@Override
????public?final?Connection?getConnection(final?String?username,?final?String?password)?throws?SQLException?{
????????return?getConnection();
????}
????
????@Override
????public?final?void?close()?throws?Exception?{
????????close(dataSourceMap.keySet());
????}
}

AbstractUnsupportedOperationDataSource 實現(xiàn)DataSource 接口并繼承了 WrapperAdapter 類,它內(nèi)部并沒有什么具體方法只起到橋接的作用,但看著是不是和我們前邊講適配器模式的例子方式有點相似。

public?abstract?class?AbstractUnsupportedOperationDataSource?extends?WrapperAdapter?implements?DataSource?{
????
????@Override
????public?final?int?getLoginTimeout()?throws?SQLException?{
????????throw?new?SQLFeatureNotSupportedException("unsupported?getLoginTimeout()");
????}
????
????@Override
????public?final?void?setLoginTimeout(final?int?seconds)?throws?SQLException?{
????????throw?new?SQLFeatureNotSupportedException("unsupported?setLoginTimeout(int?seconds)");
????}
}

WrapperAdapter 是一個包裝器的適配類,實現(xiàn)了 JDBC 中的 Wrapper 接口,其中有兩個核心方法 recordMethodInvocation 用于添加需要執(zhí)行的方法和參數(shù),而 replayMethodsInvocation 則將添加的這些方法和參數(shù)通過反射執(zhí)行。仔細看不難發(fā)現(xiàn)兩個方法中都用到了 JdbcMethodInvocation類。

public?abstract?class?WrapperAdapter?implements?Wrapper?{
????
????private?final?Collection?jdbcMethodInvocations?=?new?ArrayList<>();
?
????/**
?????*?添加要執(zhí)行的方法
?????*/

????@SneakyThrows
????public?final?void?recordMethodInvocation(final?Class?targetClass,?final?String?methodName,?final?Class[]?argumentTypes,?final?Object[]?arguments)?{
????????jdbcMethodInvocations.add(new?JdbcMethodInvocation(targetClass.getMethod(methodName,?argumentTypes),?arguments));
????}
????
????/**
?????*?通過反射執(zhí)行?上邊添加的方法
?????*/

????public?final?void?replayMethodsInvocation(final?Object?target)?{
????????for?(JdbcMethodInvocation?each?:?jdbcMethodInvocations)?{
????????????each.invoke(target);
????????}
????}
}

JdbcMethodInvocation 類主要應用反射通過傳入的 method 方法和 arguments 參數(shù)執(zhí)行對應的方法,這樣就可以通過 JDBC API 調(diào)用非 JDBC 方法了。

@RequiredArgsConstructor
public?class?JdbcMethodInvocation?{
????
????@Getter
????private?final?Method?method;
????
????@Getter
????private?final?Object[]?arguments;
????
????/**
?????*?Invoke?JDBC?method.
?????*?
?????*?@param?target?target?object
?????*/

????@SneakyThrows
????public?void?invoke(final?Object?target)?{
????????method.invoke(target,?arguments);
????}
}

Sharding-JDBC 拓展 JDBC API 接口后,在新增的分片功能里又做了哪些事情呢?

一張表經(jīng)過分庫分表后被拆分成多個子表,并分散到不同的數(shù)據(jù)庫中,在不修改原業(yè)務 SQL 的前提下,Sharding-JDBC 就必須對 SQL進行一些改造才能正常執(zhí)行。

大致的執(zhí)行流程:SQL 解析 -> 執(zhí)?器優(yōu)化 -> SQL 路由 -> SQL 改寫 -> SQL 執(zhí)? -> 結(jié)果歸并 六步組成,一起瞅瞅每個步驟做了點什么。

必修課!一文快速入門分庫分表中間件Sharding-JDBC
在這里插入圖片描述

SQL 解析

SQL解析過程分為詞法解析和語法解析兩步,比如下邊這條查詢用戶訂單的SQL,先用詞法解析將SQL拆解成不可再分的原子單元。在根據(jù)不同數(shù)據(jù)庫方言所提供的字典,將這些單元歸類為關鍵字,表達式,變量或者操作符等類型。

SELECT?order_no,price?FROM?t_order_?where?user_id?=?10086?and?order_status?>?0

接著語法解析會將拆分后的SQL轉(zhuǎn)換為抽象語法樹,通過對抽象語法樹遍歷,提煉出分片所需的上下文,上下文包含查詢字段信息(Field)、表信息(Table)、查詢條件(Condition)、排序信息(Order By)、分組信息(Group By)以及分頁信息(Limit)等,并標記出 SQL中有可能需要改寫的位置。

必修課!一文快速入門分庫分表中間件Sharding-JDBC
抽象語法樹

執(zhí)?器優(yōu)化

執(zhí)?器優(yōu)化對SQL分片條件進行優(yōu)化,處理像關鍵字 OR這種影響性能的壞味道。

SQL 路由

SQL 路由通過解析分片上下文,匹配到用戶配置的分片策略,并生成路由路徑。簡單點理解就是可以根據(jù)我們配置的分片策略計算出 SQL該在哪個庫的哪個表中執(zhí)行,而SQL路由又根據(jù)有無分片健區(qū)分出 分片路由廣播路由。

必修課!一文快速入門分庫分表中間件Sharding-JDBC
官方路由圖譜

有分?鍵的路由叫分片路由,細分為直接路由、標準路由和笛卡爾積路由這3種類型。

標準路由

標準路由是最推薦也是最為常?的分??式,它的適?范圍是不包含關聯(lián)查詢或僅包含綁定表之間關聯(lián)查詢的SQL。

當 SQL分片健的運算符為 = 時,路由結(jié)果將落?單庫(表),當分?運算符是BETWEENIN 等范圍時,路由結(jié)果則不?定落?唯?的庫(表),因此?條邏輯SQL最終可能被拆分為多條?于執(zhí)?的真實SQL。

SELECT?*?FROM?t_order??where?t_order_id?in?(1,2)

SQL路由處理后

SELECT?*?FROM?t_order_0??where?t_order_id?in?(1,2)
SELECT?*?FROM?t_order_1??where?t_order_id?in?(1,2)

直接路由

直接路由是通過使用 HintAPI 直接將 SQL路由到指定?庫表的一種分?方式,而且直接路由可以?于分?鍵不在SQL中的場景,還可以執(zhí)?包括?查詢、?定義函數(shù)等復雜情況的任意SQL。

比如根據(jù) t_order_id 字段為條件查詢訂單,此時希望在不修改SQL的前提下,加上 user_id作為分片條件就可以使用直接路由。

笛卡爾積路由

笛卡爾路由是由?綁定表之間的關聯(lián)查詢產(chǎn)生的,查詢性能較低盡量避免走此路由模式。


無分?鍵的路由又叫做廣播路由,可以劃分為全庫表路由、全庫路由、 全實例路由、單播路由和阻斷路由這 5種類型。

全庫表路由

全庫表路由針對的是數(shù)據(jù)庫 DQLDML,以及 DDL等操作,當我們執(zhí)行一條邏輯表 t_order SQL時,在所有分片庫中對應的真實表 t_order_0 ··· ?t_order_n 內(nèi)逐一執(zhí)行。

全庫路由

全庫路由主要是對數(shù)據(jù)庫層面的操作,比如數(shù)據(jù)庫 SET 類型的數(shù)據(jù)庫管理命令,以及 TCL 這樣的事務控制語句。

對邏輯庫設置 autocommit 屬性后,所有對應的真實庫中都執(zhí)行該命令。

SET?autocommit=0;

全實例路由

全實例路由是針對數(shù)據(jù)庫實例的 DCL 操作(設置或更改數(shù)據(jù)庫用戶或角色權限),比如:創(chuàng)建一個用戶 order ,這個命令將在所有的真實庫實例中執(zhí)行,以此確保 order 用戶可以正常訪問每一個數(shù)據(jù)庫實例。

CREATE?USER?order@127.0.0.1?identified?BY?'程序員內(nèi)點事';

單播路由

單播路由用來獲取某一真實表信息,比如獲得表的描述信息:

DESCRIBE?t_order;?

t_order 的真實表是 t_order_0 ···· t_order_n,他們的描述結(jié)構(gòu)相完全同,我們只需在任意的真實表執(zhí)行一次就可以。

阻斷路由

?來屏蔽SQL對數(shù)據(jù)庫的操作,例如:

USE?order_db;

這個命令不會在真實數(shù)據(jù)庫中執(zhí)?,因為 ShardingSphere 采?的是邏輯 Schema(數(shù)據(jù)庫的組織和結(jié)構(gòu)) ?式,所以無需將切換數(shù)據(jù)庫的命令發(fā)送?真實數(shù)據(jù)庫中。

SQL 改寫

將基于邏輯表開發(fā)的SQL改寫成可以在真實數(shù)據(jù)庫中可以正確執(zhí)行的語句。比如查詢 t_order 訂單表,我們實際開發(fā)中 SQL是按邏輯表 t_order 寫的。

SELECT?*?FROM?t_order

但分庫分表以后真實數(shù)據(jù)庫中 t_order 表就不存在了,而是被拆分成多個子表 t_order_n 分散在不同的數(shù)據(jù)庫內(nèi),還按原SQL執(zhí)行顯然是行不通的,這時需要將分表配置中的邏輯表名稱改寫為路由之后所獲取的真實表名稱。

SELECT?*?FROM?t_order_n

SQL執(zhí)?

將路由和改寫后的真實 SQL 安全且高效發(fā)送到底層數(shù)據(jù)源執(zhí)行。但這個過程并不是簡單的將 SQL 通過JDBC 直接發(fā)送至數(shù)據(jù)源執(zhí)行,而是平衡數(shù)據(jù)源連接創(chuàng)建以及內(nèi)存占用所產(chǎn)生的消耗,它會自動化的平衡資源控制與執(zhí)行效率。

結(jié)果歸并

將從各個數(shù)據(jù)節(jié)點獲取的多數(shù)據(jù)結(jié)果集,合并成一個大的結(jié)果集并正確的返回至請求客戶端,稱為結(jié)果歸并。而我們SQL中的排序、分組、分頁和聚合等語法,均是在歸并后的結(jié)果集上進行操作的。

四、快速實踐

下面我們結(jié)合 Springboot + mybatisplus 快速搭建一個分庫分表案例。

1、準備工作

先做準備工作,創(chuàng)建兩個數(shù)據(jù)庫 ds-0、ds-1,兩個庫中分別建表 t_order_0t_order_1、t_order_2 、t_order_item_0、t_order_item_1、t_order_item_2t_config,方便后邊驗證廣播表、綁定表的場景。

必修課!一文快速入門分庫分表中間件Sharding-JDBC表結(jié)構(gòu)如下:

t_order_0 訂單表

CREATE?TABLE?`t_order_0`?(
??`order_id`?bigint(200)?NOT?NULL,
??`order_no`?varchar(100)?DEFAULT?NULL,
??`create_name`?varchar(50)?DEFAULT?NULL,
??`price`?decimal(10,2)?DEFAULT?NULL,
??PRIMARY?KEY?(`order_id`)
)?ENGINE=InnoDB?DEFAULT?CHARSET=utf8?ROW_FORMAT=DYNAMIC;

t_order_0t_order_item_0 ?互為關聯(lián)表

CREATE?TABLE?`t_order_item_0`?(
??`item_id`?bigint(100)?NOT?NULL,
??`order_no`?varchar(200)?NOT?NULL,
??`item_name`?varchar(50)?DEFAULT?NULL,
??`price`?decimal(10,2)?DEFAULT?NULL,
??PRIMARY?KEY?(`item_id`)
)?ENGINE=InnoDB?DEFAULT?CHARSET=utf8?ROW_FORMAT=DYNAMIC;


廣播表 t_config

??`id`?bigint(30)?NOT?NULL,
??`remark`?varchar(50)?CHARACTER?SET?utf8?DEFAULT?NULL,
??`create_time`?timestamp?NOT?NULL?DEFAULT?CURRENT_TIMESTAMP,
??`last_modify_time`?timestamp?NOT?NULL?DEFAULT?CURRENT_TIMESTAMP?ON?UPDATE?CURRENT_TIMESTAMP,
??PRIMARY?KEY?(`id`)
)?ENGINE=InnoDB?DEFAULT?CHARSET=latin1;

ShardingSphere 提供了4種分片配置方式:

  • Java 代碼配置

  • Yaml 、properties 配置

  • Spring 命名空間配置

  • Spring Boot配置

為讓代碼看上去更簡潔和直觀,后邊統(tǒng)一使用 properties 配置的方式,引入 shardingsphere 對應的 sharding-jdbc-spring-boot-startersharding-core-common 包,版本統(tǒng)一用的 4.0.0-RC1。

2、分片配置

<dependency>
?<groupId>org.apache.shardingspheregroupId>
?<artifactId>sharding-jdbc-spring-boot-starterartifactId>
?<version>4.0.0-RC1version>
dependency>

<dependency>
?<groupId>org.apache.shardingspheregroupId>
?<artifactId>sharding-core-commonartifactId>
?<version>4.0.0-RC1version>
dependency>

準備工作做完( mybatis 搭建就不贅述了),接下來我們逐一解讀分片配置信息。

我們首先定義兩個數(shù)據(jù)源 ds-0ds-1,并分別加上數(shù)據(jù)源的基礎信息。

#?定義兩個全局數(shù)據(jù)源
spring.shardingsphere.datasource.names=ds-0,ds-1

#?配置數(shù)據(jù)源?ds-0
spring.shardingsphere.datasource.ds-0.type=com.alibaba.druid.pool.DruidDataSource
spring.shardingsphere.datasource.ds-0.driverClassName=com.mysql.jdbc.Driver
spring.shardingsphere.datasource.ds-0.url=jdbc:mysql://127.0.0.1:3306/ds-0?useUnicode=true&characterEncoding=utf8&tinyInt1isBit=false&useSSL=false&serverTimezone=GMT
spring.shardingsphere.datasource.ds-0.username=root
spring.shardingsphere.datasource.ds-0.password=root

#?配置數(shù)據(jù)源?ds-1
spring.shardingsphere.datasource.ds-1.type=com.alibaba.druid.pool.DruidDataSource
spring.shardingsphere.datasource.ds-1.driverClassName=com.mysql.jdbc.Driver
spring.shardingsphere.datasource.ds-1.url=jdbc:mysql://127.0.0.1:3306/ds-1?useUnicode=true&characterEncoding=utf8&tinyInt1isBit=false&useSSL=false&serverTimezone=GMT
spring.shardingsphere.datasource.ds-1.username=root
spring.shardingsphere.datasource.ds-1.password=root

配置完數(shù)據(jù)源接下來為表添加分庫和分表策略,使用 sharding-jdbc 做分庫分表需要我們?yōu)槊恳粋€表單獨設置分片規(guī)則。

#?配置分片表?t_order
#?指定真實數(shù)據(jù)節(jié)點
spring.shardingsphere.sharding.tables.t_order.actual-data-nodes=ds-$->{0..1}.t_order_$->{0..2}

actual-data-nodes 屬性指定分片的真實數(shù)據(jù)節(jié)點,$是一個占位符,{0..1}表示實際拆分的數(shù)據(jù)庫表數(shù)量。

ds-$->{0..1}.t_order_$->{0..2} ?表達式相當于 6個數(shù)據(jù)節(jié)點

  • ds-0.t_order_0
  • ds-0.t_order_1
  • ds-0.t_order_2
  • ds-1.t_order_0
  • ds-1.t_order_1
  • ds-1.t_order_2
###?分庫策略
#?分庫分片健
spring.shardingsphere.sharding.tables.t_order.database-strategy.inline.sharding-column=order_id
#?分庫分片算法
spring.shardingsphere.sharding.tables.t_order.database-strategy.inline.algorithm-expression=ds-$->{order_id?%?2}

為表設置分庫策略,上邊講了 sharding-jdbc 它提供了四種分片策略,為快速搭建我們先以最簡單的行內(nèi)表達式分片策略來實現(xiàn),在下一篇會介紹四種分片策略的詳細用法和使用場景。

database-strategy.inline.sharding-column ?屬性中 database-strategy ?為分庫策略,inline ?為具體的分片策略,sharding-column ?代表分片健。

database-strategy.inline.algorithm-expression 是當前策略下具體的分片算法,ds-$->{order_id % 2} 表達式意思是 對 order_id字段進行取模分庫,2 代表分片庫的個數(shù),不同的策略對應不同的算法,這里也可以是我們自定義的分片算法類。


#?分表策略
#?分表分片健
spring.shardingsphere.sharding.tables.t_order.table-strategy.inline.sharding-column=order_id
#?分表算法
spring.shardingsphere.sharding.tables.t_order.table-strategy.inline.algorithm-expression=t_order_$->{order_id?%?3}
#?自增主鍵字段
spring.shardingsphere.sharding.tables.t_order.key-generator.column=order_id
#?自增主鍵ID?生成方案
spring.shardingsphere.sharding.tables.t_order.key-generator.type=SNOWFLAKE

分表策略 和 分庫策略 的配置比較相似,不同的是分表可以通過 key-generator.columnkey-generator.type 設置自增主鍵以及指定自增主鍵的生成方案,目前內(nèi)置了SNOWFLAKEUUID 兩種方式,還能自定義的主鍵生成算法類,后續(xù)會詳細的講解。

#?綁定表關系
spring.shardingsphere.sharding.binding-tables=?t_order,t_order_item

必須按相同分片健進行分片的表才能互為成綁定表,在聯(lián)合查詢時就能避免出現(xiàn)笛卡爾積查詢。

#?配置廣播表
spring.shardingsphere.sharding.broadcast-tables=t_config

廣播表,開啟 SQL解析日志,能清晰的看到 SQL分片解析的過程

#?是否開啟?SQL解析日志
spring.shardingsphere.props.sql.show=true

3、驗證分片

分片配置完以后我們無需在修改業(yè)務代碼了,直接執(zhí)行業(yè)務邏輯的增、刪、改、查即可,接下來驗證一下分片的效果。

我們同時向 t_order、t_order_item 表插入 5條訂單記錄,并不給定主鍵 order_id ,item_id 字段值。

public?String?insertOrder()?{

???for?(int?i?=?0;?i?4;?i++)?{
???????TOrder?order?=?new?TOrder();
???????order.setOrderNo("A000"?+?i);
???????order.setCreateName("訂單?"?+?i);
???????order.setPrice(new?BigDecimal(""?+?i));
???????orderRepository.insert(order);

???????TOrderItem?orderItem?=?new?TOrderItem();
???????orderItem.setOrderId(order.getOrderId());
???????orderItem.setOrderNo("A000"?+?i);
???????orderItem.setItemName("服務項目"?+?i);
???????orderItem.setPrice(new?BigDecimal(""?+?i));
???????orderItemRepository.insert(orderItem);
???}
???return?"success";
}

看到訂單記錄被成功分散到了不同的庫表中, order_id 字段也自動生成了主鍵ID,基礎的分片功能就完成了。

必修課!一文快速入門分庫分表中間件Sharding-JDBC
基礎分片

那向廣播表 t_config 中插入一條數(shù)據(jù)會是什么效果呢?


public?String?config()?{

????TConfig?tConfig?=?new?TConfig();
????tConfig.setRemark("我是廣播表");
????tConfig.setCreateTime(new?Date());
????tConfig.setLastModifyTime(new?Date());
????configRepository.insert(tConfig);
????return?"success";
}

發(fā)現(xiàn)所有庫中 t_config 表都執(zhí)行了這條SQL,廣播表和 MQ廣播訂閱的模式很相似,所有訂閱的客戶端都會收到同一條消息。

必修課!一文快速入門分庫分表中間件Sharding-JDBC
廣播表

簡單SQL操作驗證沒問通,接下來在試試復雜一點的聯(lián)合查詢,前邊我們已經(jīng)把 t_order 、t_order_item 表設為綁定表,直接聯(lián)表查詢執(zhí)行一下。

必修課!一文快速入門分庫分表中間件Sharding-JDBC
關聯(lián)查詢

通過控制臺日志發(fā)現(xiàn),邏輯表SQL 經(jīng)過解析以后,只對 ?t_order_0t_order_item_0 表進行了關聯(lián)產(chǎn)生一條SQL。

必修課!一文快速入門分庫分表中間件Sharding-JDBC
綁定表SQL

那如果不互為綁定表又會是什么情況呢?去掉 spring.shardingsphere.sharding.binding-tables試一下。

發(fā)現(xiàn)控制臺解析出了 3條真實表SQL,而去掉 order_id 作為查詢條件再次執(zhí)行后,結(jié)果解析出了 9條SQL,進行了笛卡爾積查詢。所以相比之下綁定表的優(yōu)點就不言而喻了。

必修課!一文快速入門分庫分表中間件Sharding-JDBC
笛卡爾積查詢

五、總結(jié)

以上對分庫分表中間件 sharding-jdbc 的基礎概念做了簡單梳理,快速的搭建了一個分庫分表案例,但這只是實踐分庫分表的第一步,下一篇我們會詳細的介紹四種分片策略的具體用法和使用場景(必知必會),后邊將陸續(xù)講解自定義分布式主鍵、分布式數(shù)據(jù)庫事務、分布式服務治理,數(shù)據(jù)脫敏等。

案例 GitHub 地址:https://github.com/chengxy-nds/Springboot-Notebook/tree/master/springboot-sharding-jdbc

必修課!一文快速入門分庫分表中間件Sharding-JDBC

特別推薦一個分享架構(gòu)+算法的優(yōu)質(zhì)內(nèi)容,還沒關注的小伙伴,可以長按關注一下:

必修課!一文快速入門分庫分表中間件Sharding-JDBC

必修課!一文快速入門分庫分表中間件Sharding-JDBC

必修課!一文快速入門分庫分表中間件Sharding-JDBC

長按訂閱更多精彩▼

必修課!一文快速入門分庫分表中間件Sharding-JDBC

如有收獲,點個在看,誠摯感謝

免責聲明:本文內(nèi)容由21ic獲得授權后發(fā)布,版權歸原作者所有,本平臺僅提供信息存儲服務。文章僅代表作者個人觀點,不代表本平臺立場,如有問題,請聯(lián)系我們,謝謝!

本站聲明: 本文章由作者或相關機構(gòu)授權發(fā)布,目的在于傳遞更多信息,并不代表本站贊同其觀點,本站亦不保證或承諾內(nèi)容真實性等。需要轉(zhuǎn)載請聯(lián)系該專欄作者,如若文章內(nèi)容侵犯您的權益,請及時聯(lián)系本站刪除。
換一批
延伸閱讀

9月2日消息,不造車的華為或?qū)⒋呱龈蟮莫毥谦F公司,隨著阿維塔和賽力斯的入局,華為引望愈發(fā)顯得引人矚目。

關鍵字: 阿維塔 塞力斯 華為

加利福尼亞州圣克拉拉縣2024年8月30日 /美通社/ -- 數(shù)字化轉(zhuǎn)型技術解決方案公司Trianz今天宣布,該公司與Amazon Web Services (AWS)簽訂了...

關鍵字: AWS AN BSP 數(shù)字化

倫敦2024年8月29日 /美通社/ -- 英國汽車技術公司SODA.Auto推出其旗艦產(chǎn)品SODA V,這是全球首款涵蓋汽車工程師從創(chuàng)意到認證的所有需求的工具,可用于創(chuàng)建軟件定義汽車。 SODA V工具的開發(fā)耗時1.5...

關鍵字: 汽車 人工智能 智能驅(qū)動 BSP

北京2024年8月28日 /美通社/ -- 越來越多用戶希望企業(yè)業(yè)務能7×24不間斷運行,同時企業(yè)卻面臨越來越多業(yè)務中斷的風險,如企業(yè)系統(tǒng)復雜性的增加,頻繁的功能更新和發(fā)布等。如何確保業(yè)務連續(xù)性,提升韌性,成...

關鍵字: 亞馬遜 解密 控制平面 BSP

8月30日消息,據(jù)媒體報道,騰訊和網(wǎng)易近期正在縮減他們對日本游戲市場的投資。

關鍵字: 騰訊 編碼器 CPU

8月28日消息,今天上午,2024中國國際大數(shù)據(jù)產(chǎn)業(yè)博覽會開幕式在貴陽舉行,華為董事、質(zhì)量流程IT總裁陶景文發(fā)表了演講。

關鍵字: 華為 12nm EDA 半導體

8月28日消息,在2024中國國際大數(shù)據(jù)產(chǎn)業(yè)博覽會上,華為常務董事、華為云CEO張平安發(fā)表演講稱,數(shù)字世界的話語權最終是由生態(tài)的繁榮決定的。

關鍵字: 華為 12nm 手機 衛(wèi)星通信

要點: 有效應對環(huán)境變化,經(jīng)營業(yè)績穩(wěn)中有升 落實提質(zhì)增效舉措,毛利潤率延續(xù)升勢 戰(zhàn)略布局成效顯著,戰(zhàn)新業(yè)務引領增長 以科技創(chuàng)新為引領,提升企業(yè)核心競爭力 堅持高質(zhì)量發(fā)展策略,塑強核心競爭優(yōu)勢...

關鍵字: 通信 BSP 電信運營商 數(shù)字經(jīng)濟

北京2024年8月27日 /美通社/ -- 8月21日,由中央廣播電視總臺與中國電影電視技術學會聯(lián)合牽頭組建的NVI技術創(chuàng)新聯(lián)盟在BIRTV2024超高清全產(chǎn)業(yè)鏈發(fā)展研討會上宣布正式成立。 活動現(xiàn)場 NVI技術創(chuàng)新聯(lián)...

關鍵字: VI 傳輸協(xié)議 音頻 BSP

北京2024年8月27日 /美通社/ -- 在8月23日舉辦的2024年長三角生態(tài)綠色一體化發(fā)展示范區(qū)聯(lián)合招商會上,軟通動力信息技術(集團)股份有限公司(以下簡稱"軟通動力")與長三角投資(上海)有限...

關鍵字: BSP 信息技術
關閉
關閉