ShardingSphere-Proxy:從實(shí)際場(chǎng)景出發(fā),快速上手
本篇文章主要從項(xiàng)目中實(shí)際場(chǎng)景出發(fā),講解分庫分表等功能在日常運(yùn)維中遇到的問題,以及 ShardingSphere-Proxy 對(duì)應(yīng)的解決方案,版本號(hào):v5.1.0。
如無特別聲明,以下示例中的數(shù)據(jù)庫指 MySQL。
一、這個(gè)項(xiàng)目做什么
ShardingSphere-Proxy,可以讓用戶像使用原生數(shù)據(jù)庫一樣使用 Apache ShardingSphere。
了解一項(xiàng)技術(shù)的開始,一般從官網(wǎng)開始。先來看一看官網(wǎng)對(duì) ShardingSphere-Proxy 的定義是什么樣的:
定位為透明化的數(shù)據(jù)庫代理端,提供封裝了數(shù)據(jù)庫二進(jìn)制協(xié)議的服務(wù)端版本,用于完成對(duì)異構(gòu)語言的支持。 目前提供 MySQL 和 PostgreSQL(兼容 openGauss 等基于 PostgreSQL 的數(shù)據(jù)庫)版本,它可以使用任何兼容 MySQL/PostgreSQL 協(xié)議的訪問客戶端(如:MySQL Command Client, MySQL Workbench, Navicat 等)操作數(shù)據(jù),對(duì) DBA 更加友好。

先明確一個(gè)概念,ShardingSphere-Proxy 是一個(gè)服務(wù)進(jìn)程。從客戶端程序連接來說,它和 MySQL 數(shù)據(jù)庫并沒有什么區(qū)別。
二、為什么要用 Proxy
在做了分庫分表或其他規(guī)則的情況下,數(shù)據(jù)會(huì)分散到多個(gè)數(shù)據(jù)庫實(shí)例上,在管理上難免會(huì)有一些不便;或者使用非 Java 語言的開發(fā)者,需要 ShardingSphere 所提供的能力…… 以上這些情況,正是 ShardingSphere-Proxy 力所能及之處。
1. Proxy 應(yīng)用場(chǎng)景
日常工作中,大家使用 ShardingSphere-JDBC 進(jìn)行分庫分表的場(chǎng)景是比較多的。假設(shè)你有一張用戶表,通過用戶 ID 以 Hash 的方式進(jìn)行了水平分庫,那么此時(shí)客戶端連接數(shù)據(jù)庫的方式是這樣:

我們舉例工作中真實(shí)存在的幾個(gè)場(chǎng)景:
測(cè)試同學(xué)想看下用戶 ID 123456 的信息在數(shù)據(jù)庫表里情況,需要你提供下用戶在哪一張分表; 公司領(lǐng)導(dǎo)需要技術(shù)提供一份 2022 年用戶的增長總量以及用戶信息; 公司舉行 8 周年活動(dòng),需要技術(shù)提供一份注冊(cè)日期超過 8 周年的活躍老用戶名單。
因?yàn)閿?shù)據(jù)分庫分表后,數(shù)據(jù)是散落在不同的庫表中,對(duì)于上述的場(chǎng)景實(shí)現(xiàn)并不容易;如果為了實(shí)現(xiàn)類似臨時(shí)需求,每次都需要開發(fā)代碼,顯得有些笨重。這個(gè)時(shí)候就需要文章主角 ShardingSphere-Proxy 登場(chǎng)了。
ShardingSphere-Proxy 隱藏了后端實(shí)際數(shù)據(jù)庫,對(duì)于客戶端來說就是在使用一個(gè)數(shù)據(jù)庫,不需要關(guān)心 ShardingSphere 如何協(xié)調(diào)背后的數(shù)據(jù)庫,對(duì)于使用非 Java 語言的開發(fā)者或 DBA 更友好。
比如說 t_user 在數(shù)據(jù)庫層面拆分為若干真實(shí)表:t_user_0 到 t_user_9,在客戶端操作 ShardingSphere-Proxy 的過程中,只會(huì)知道有一張 t_user 邏輯表,路由至真實(shí)表的過程都在 ShardingSphere-Proxy 內(nèi)部執(zhí)行。
邏輯表:相同結(jié)構(gòu)的水平拆分?jǐn)?shù)據(jù)庫(表)的邏輯名稱,是 SQL 中表的邏輯標(biāo)識(shí)。 例:用戶數(shù)據(jù)根據(jù)主鍵尾數(shù)拆分為 10 張表,分別是 t_user_0到t_user_9,他們的邏輯表名為t_user。真實(shí)表:在水平拆分的數(shù)據(jù)庫中真實(shí)存在的物理表。 即上個(gè)示例中的 t_user_0到t_user_9。

2. JDBC 和 Proxy 的區(qū)別
看了上面的描述,怎么感覺 ShardingSphere-Proxy 和 ShardingSphere-JDBC 這么像,兩者有什么區(qū)別?
| ShardingSphere-JDBC | ShardingSphere-Proxy | |
|---|---|---|
| 數(shù)據(jù)庫 | 任意 | 基于 MySQL / PostgreSQL 協(xié)議的數(shù)據(jù)庫 |
| 連接消耗數(shù) | 高 | 低 |
| 異構(gòu)語言 | 支持 Java 等基于 JVM 語言 | 任意 |
| 性能 | 損耗低 | 損耗略高 |
| 無中心化 | 是 | 否 |
| 靜態(tài)入口 | 無 | 有 |
簡單總結(jié)下兩者的不同:
ShardingSphere-JDBC 是一個(gè) Jar 包,底層通過重寫 JDBC 組件完成 SQL 解析、路由、改寫、執(zhí)行等流程;需要在項(xiàng)目中添加對(duì)應(yīng)功能的配置文件,對(duì)應(yīng)用有一定侵入性; ShardingSphere-Proxy 是一個(gè)進(jìn)程服務(wù),大部分情況下定位為輔助開發(fā)運(yùn)維的效率工具。它將自己偽裝為數(shù)據(jù)庫,應(yīng)用對(duì)接后對(duì)代碼是無侵入的;對(duì) SQL 的執(zhí)行邏輯同 ShardingSphere-JDBC 一致,兩者復(fù)用同一內(nèi)核。
ShardingSphere-Proxy 既然對(duì)應(yīng)用無侵入,而且兩者復(fù)用同一內(nèi)核,那為什么大家還要用 ShardingSphere-JDBC 呢?
應(yīng)用通過 ShardingSphere-JDBC 是直接操作數(shù)據(jù)庫,相當(dāng)于只有一次網(wǎng)絡(luò) IO;而應(yīng)用連接 ShardingSphere-Proxy 是一次網(wǎng)絡(luò) IO,ShardingSphere-Proxy 再操作數(shù)據(jù)庫,又會(huì)發(fā)生一次網(wǎng)絡(luò) IO; 應(yīng)用調(diào)用鏈路多了一層,容易形成流量瓶頸,對(duì)應(yīng)用增加了潛在的風(fēng)險(xiǎn);一般來說,應(yīng)用程序會(huì)搭配 ShardingSphere-JDBC 使用。
當(dāng)然,ShardingSphere-JDBC 和 ShardingSphere-Proxy 可以進(jìn)行混合部署,ShardingSphere-JDBC 適用于 Java 開發(fā)的高性能的輕量級(jí) OLTP 應(yīng)用,ShardingSphere-Proxy 適用于 OLAP 應(yīng)用以及對(duì)分片數(shù)據(jù)庫進(jìn)行管理和運(yùn)維的場(chǎng)景。

三、如何開始
ShardingSphere-Proxy 的啟動(dòng)方式有三種:二進(jìn)制包、Docker、Helm,并分為單機(jī)部署和集群部署。文章以單機(jī)二進(jìn)制包的方式啟動(dòng)。
通過 下載頁面[1] 獲取 ShardingSphere-Proxy 二進(jìn)制安裝包; 解壓縮后修改 conf/server.yaml和以config-前綴開頭的文件,進(jìn)行分片、讀寫分離等規(guī)則配置;Linux 操作系統(tǒng)請(qǐng)運(yùn)行 bin/start.sh,Windows 操作系統(tǒng)請(qǐng)運(yùn)行bin/start.bat啟動(dòng) ShardingSphere-Proxy。
下載后的文件目錄如下:
├──?LICENSE
├──?NOTICE
├──?README.txt
├──?bin?#?啟動(dòng)停止腳本
├──?conf?#?服務(wù)配置,分庫分表、讀寫分離、數(shù)據(jù)加密等功能的配置文件
├──?lib?#?Jar?包
└──?licenses
1. 將 MySQL 的 JDBC 驅(qū)動(dòng)復(fù)制到 ext-lib 包
下載驅(qū)動(dòng) mysql-connector-java-5.1.47.jar[2] 或者 mysql-connector-java-8.0.11.jar[3] 放入 ext-lib 包。因?yàn)槌跏寄夸浿胁]有 ext-lib,需要自行創(chuàng)建。
2. 修改 conf/server.yaml 配置文件
server.yaml 配置中默認(rèn)集群運(yùn)行模式,這里提供一份單機(jī)的運(yùn)行配置。
mode:
?type:?Standalone?#?單機(jī)模式
?repository:
???type:?File
???props:
?????path:?/Users/xxx/software/apache-shardingsphere-5.1.0-shardingsphere-proxy/file?#?元數(shù)據(jù)配置等持久化文件路徑
?overwrite:?false?#?是否覆蓋已存在的元數(shù)據(jù)
rules:?#?認(rèn)證信息
?-?!AUTHORITY
???users:?#?初始化用戶
?????-?root@%:root
?????-?sharding@:sharding
???provider:
?????type:?ALL_PRIVILEGES_PERMITTED
?-?!TRANSACTION
???defaultType:?XA
???providerType:?Atomikos
?-?!SQL_PARSER
???sqlCommentParseEnabled:?true
???sqlStatementCache:
?????initialCapacity:?2000
?????maximumSize:?65535
?????concurrencyLevel:?4
???parseTreeCache:
?????initialCapacity:?128
?????maximumSize:?1024
?????concurrencyLevel:?4
props:?#?公用配置
?max-connections-size-per-query:?1
?kernel-executor-size:?16??#?Infinite?by?default.
?proxy-frontend-flush-threshold:?128??#?The?default?value?is?128.
?proxy-opentracing-enabled:?false
?proxy-hint-enabled:?false
?sql-show:?false
?check-table-metadata-enabled:?false
?show-process-list-enabled:?false
???#?Proxy?backend?query?fetch?size.?A?larger?value?may?increase?the?memory?usage?of?ShardingSphere?Proxy.
???#?The?default?value?is?-1,?which?means?set?the?minimum?value?for?different?JDBC?drivers.
?proxy-backend-query-fetch-size:?-1
?check-duplicate-table-enabled:?false
?proxy-frontend-executor-size:?0?#?Proxy?frontend?executor?size.?The?default?value?is?0,?which?means?let?Netty?decide.
???#?Available?options?of?proxy?backend?executor?suitable:?OLAP(default),?OLTP.?The?OLTP?option?may?reduce?time?cost?of?writing?packets?to?client,?but?it?may?increase?the?latency?of?SQL?execution
???#?and?block?other?clients?if?client?connections?are?more?than?`proxy-frontend-executor-size`,?especially?executing?slow?SQL.
?proxy-backend-executor-suitable:?OLAP
?proxy-frontend-max-connections:?0?#?Less?than?or?equal?to?0?means?no?limitation.
?sql-federation-enabled:?false
???#?Available?proxy?backend?driver?type:?JDBC?(default),?ExperimentalVertx
?proxy-backend-driver-type:?JDBC
需要注意,如果啟動(dòng)單機(jī) ShardingSphere-Proxy,后續(xù)需要 Proxy 配置變更,建議將 mode.overwrite 設(shè)置為 true,這樣 ShardingSphere-Proxy 在啟動(dòng)時(shí)就會(huì)重新加載元數(shù)據(jù)。
3. 啟動(dòng) ShardingSphere-Proxy
執(zhí)行啟動(dòng)命令:sh bin/start.sh。默認(rèn)啟動(dòng)端口 3307,可以通過啟動(dòng)腳本命令追加參數(shù)的方式替換端口:sh bin/start.sh 3308。
查看 ShardingSphere-Proxy 是否啟動(dòng)成功,執(zhí)行查看日志命令:tail -100f logs/stdout.log。如最后一行出現(xiàn)下述信息,即為啟動(dòng)成功:
[INFO?]?xxx-xx-xx?xx:xx:xx.xxx?[main]?o.a.s.p.frontend.ShardingSphereProxy?-?ShardingSphere-Proxy?Standalone?mode?started?successfully
四、場(chǎng)景實(shí)踐
本章節(jié)從實(shí)戰(zhàn)場(chǎng)景的前提出發(fā),通過 ShardingSphere-Proxy 完成上述需求。

1. 初始化數(shù)據(jù)庫表
#?CREATE?DATABASE
CREATE?DATABASE?user_sharding_0;
CREATE?DATABASE?user_sharding_1;
#?CREATE?TABLE
use?user_sharding_0;
CREATE?TABLE?`t_user_0`?(
?`id`?bigint?(20)?NOT?NULL,
?`user_id`?bigint?(20)?NOT?NULL,
?`create_date`?datetime?DEFAULT?NULL,
?PRIMARY?KEY?(`id`))?ENGINE?=?InnoDB?DEFAULT?CHARSET?=?latin1;
CREATE?TABLE?`t_user_1`?(
?`id`?bigint?(20)?NOT?NULL,
?`user_id`?bigint?(20)?NOT?NULL,
?`create_date`?datetime?DEFAULT?NULL,
?PRIMARY?KEY?(`id`))?ENGINE?=?InnoDB?DEFAULT?CHARSET?=?latin1;
use?user_sharding_1;
CREATE?TABLE?`t_user_0`?(
?`id`?bigint?(20)?NOT?NULL,
?`user_id`?bigint?(20)?NOT?NULL,
?`create_date`?datetime?DEFAULT?NULL,
?PRIMARY?KEY?(`id`))?ENGINE?=?InnoDB?DEFAULT?CHARSET?=?latin1;
CREATE?TABLE?`t_user_1`?(
?`id`?bigint?(20)?NOT?NULL,
?`user_id`?bigint?(20)?NOT?NULL,
?`create_date`?datetime?DEFAULT?NULL,
?PRIMARY?KEY?(`id`))?ENGINE?=?InnoDB?DEFAULT?CHARSET?=?latin1;
2. 初始化 Proxy 分片配置
schemaName:?sharding_db
dataSources:
??ds_0:
????url:?jdbc:mysql://127.0.0.1:3306/user_sharding_0?serverTimezone=UTC&useSSL=false
????username:?root
????password:?root
????connectionTimeoutMilliseconds:?30000
????idleTimeoutMilliseconds:?60000
????maxLifetimeMilliseconds:?1800000
????maxPoolSize:?50
????minPoolSize:?1
??ds_1:
????url:?jdbc:mysql://127.0.0.1:3306/user_sharding_1?serverTimezone=UTC&useSSL=false
????username:?root
????password:?root
????connectionTimeoutMilliseconds:?30000
????idleTimeoutMilliseconds:?60000
????maxLifetimeMilliseconds:?1800000
????maxPoolSize:?50
????minPoolSize:?1
rules:
-?!SHARDING
??tables:
????t_user:
??????actualDataNodes:?ds_${0..1}.t_user_${0..1}
??????tableStrategy:
????????standard:
??????????shardingColumn:?user_id
??????????shardingAlgorithmName:?t_user_inline
??????keyGenerateStrategy:
????????column:?user_id
????????keyGeneratorName:?snowflake
??bindingTables:
????-?t_user
??defaultDatabaseStrategy:
????standard:
??????shardingColumn:?user_id
??????shardingAlgorithmName:?database_inline
??defaultTableStrategy:
????none:
??shardingAlgorithms:
????database_inline:
??????type:?INLINE
??????props:
????????algorithm-expression:?ds_${user_id?%?2}
????t_user_inline:
??????type:?INLINE
??????props:
????????algorithm-expression:?t_user_${user_id?%?2}
??keyGenerators:
????snowflake:
??????type:?SNOWFLAKE
3. 分片測(cè)試
使用 MySQL 終端命令連接 ShardingSphere-Proxy 服務(wù)端。如果 Docker 部署的數(shù)據(jù)庫,需要加上 -h 本機(jī) ip。因?yàn)槿萜鲀?nèi)訪問 127.0.0.1 不通。
#?將?{xx}?替換為實(shí)際參數(shù)
mysql?-h?{ip}?-u?{username}?-p{password}?-P?3307
#?示例命令
mysql?-h?127.0.0.1?-u?root?-proot?-P?3307
ShardingSphere-Proxy 支持 Navicat MySQL、DataGrip、WorkBench、TablePlus 等數(shù)據(jù)庫管理工具連接。
連接成功后,查詢代理數(shù)據(jù)庫,與配置文件中一致。
mysql>?show?databases;
+-------------+
|?schema_name?|
+-------------+
|?sharding_db?|
+-------------+
1?row?in?set?(0.02?sec)
執(zhí)行新增 t_user 語句,插入 6 條用戶數(shù)據(jù),創(chuàng)建時(shí)間 2021 年 3 條,2022 年 3 條。
mysql>?use?sharding_db;
mysql>?INSERT?INTO?t_user?(id,?user_id,?create_date)?values(1,?1,?'2021-01-01?00:00:00'),?(2,?2,?'2021-01-01?00:00:00'),?(3,?3,?'2021-01-01?00:00:00'),?(4,?4,?'2022-01-01?00:00:00'),?(5,?5,?'2022-02-01?00:00:00'),?(6,?6,?'2022-03-01?00:00:00');
Query?OK,?6?rows?affected?(0.16?sec)
mysql>?select?*?from?t_user;
+----+---------+---------------------+
|?id?|?user_id?|?create_date?????????|
+----+---------+---------------------+
|??2?|???????2?|?2021-01-01?00:00:00?|
|??4?|???????4?|?2022-01-01?00:00:00?|
|??6?|???????6?|?2022-03-01?00:00:00?|
|??1?|???????1?|?2021-01-01?00:00:00?|
|??3?|???????3?|?2021-01-01?00:00:00?|
|??5?|???????5?|?2022-02-01?00:00:00?|
+----+---------+---------------------+
此時(shí)數(shù)據(jù)分別散落在 user_sharding_0 和 user_sharding_1 庫。
回到最初的問題,如何定位數(shù)據(jù)信息。因?yàn)?ShardingSphere-Proxy 已經(jīng)將表進(jìn)行了邏輯聚合,所以直接查詢就好。
mysql>?select?*?from?t_user?where?user_id?=?1;
+----+---------+---------------------+
|?id?|?user_id?|?create_date?????????|
+----+---------+---------------------+
|??1?|???????1?|?2021-01-01?00:00:00?|
+----+---------+---------------------+
1?row?in?set?(0.01?sec)
第二個(gè)問題,查詢 2022 年用戶增長數(shù)量以及用戶情況。
mysql>?select?count(*)?from?t_user?where?create_date?>?'2022-00-00?00:00:00';
+----------+
|?count(*)?|
+----------+
|????????3?|
+----------+
1?row?in?set?(0.10?sec)
mysql>?select?*?from?t_user?where?create_date?>?'2022-00-00?00:00:00';
+----+---------+---------------------+
|?id?|?user_id?|?create_date?????????|
+----+---------+---------------------+
|??4?|???????4?|?2022-01-01?00:00:00?|
|??6?|???????6?|?2022-01-01?00:00:00?|
|??5?|???????5?|?2022-01-01?00:00:00?|
+----+---------+---------------------+
3?rows?in?set?(0.02?sec)
第三個(gè)問題同上。
五、最后總結(jié)
文章通過圖文并茂的方式幫助大家過了一遍 ShardingSphere-Proxy 的基本概念,引申出了分庫分表后產(chǎn)生的實(shí)際運(yùn)維場(chǎng)景,并演示了如何通過 ShardingSphere-Proxy 解決相關(guān)問題。
相信大家看完對(duì) ShardingSphere-Proxy 有了更深入的認(rèn)識(shí)。首先要明白 ShardingSphere-Proxy 的定位是協(xié)助開發(fā)運(yùn)維的產(chǎn)品,掌握 ShardingSphere-JDBC 和 ShardingSphere-Proxy 有哪些區(qū)別,以及理解兩者的優(yōu)缺點(diǎn)和實(shí)現(xiàn)方式是怎么樣的。在這個(gè)基礎(chǔ)上去閱讀兩者的源碼,也就更容易理解了。
六、巨人的肩膀
Apache ShardingSphere 官網(wǎng)[4]
打造基于 PostgreSQL/openGauss 的分布式數(shù)據(jù)庫解決方案[5]
參考
下載頁面: https://shardingsphere.apache.org/document/current/cn/downloads/
[2]mysql-connector-java-5.1.47.jar: https://repo1.maven.org/maven2/mysql/mysql-connector-java/5.1.47/mysql-connector-java-5.1.47.jar
[3]mysql-connector-java-8.0.11.jar: https://repo1.maven.org/maven2/mysql/mysql-connector-java/8.0.11/mysql-connector-java-8.0.11.jar
[4]Apache ShardingSphere 官網(wǎng): https://shardingsphere.apache.org/
[5]打造基于 PostgreSQL/openGauss 的分布式數(shù)據(jù)庫解決方案: https://community.sphere-ex.com/t/topic/497
