ShardingSphere-Proxy:從實際場景出發(fā),快速上手
本篇文章主要從項目中實際場景出發(fā),講解分庫分表等功能在日常運(yùn)維中遇到的問題,以及 ShardingSphere-Proxy 對應(yīng)的解決方案,版本號:v5.1.0。
如無特別聲明,以下示例中的數(shù)據(jù)庫指 MySQL。
一、這個項目做什么
ShardingSphere-Proxy,可以讓用戶像使用原生數(shù)據(jù)庫一樣使用 Apache ShardingSphere。
了解一項技術(shù)的開始,一般從官網(wǎng)開始。先來看一看官網(wǎng)對 ShardingSphere-Proxy 的定義是什么樣的:
定位為透明化的數(shù)據(jù)庫代理端,提供封裝了數(shù)據(jù)庫二進(jìn)制協(xié)議的服務(wù)端版本,用于完成對異構(gòu)語言的支持。 目前提供 MySQL 和 PostgreSQL(兼容 openGauss 等基于 PostgreSQL 的數(shù)據(jù)庫)版本,它可以使用任何兼容 MySQL/PostgreSQL 協(xié)議的訪問客戶端(如:MySQL Command Client, MySQL Workbench, Navicat 等)操作數(shù)據(jù),對 DBA 更加友好。
先明確一個概念,ShardingSphere-Proxy 是一個服務(wù)進(jìn)程。從客戶端程序連接來說,它和 MySQL 數(shù)據(jù)庫并沒有什么區(qū)別。
二、為什么要用 Proxy
在做了分庫分表或其他規(guī)則的情況下,數(shù)據(jù)會分散到多個數(shù)據(jù)庫實例上,在管理上難免會有一些不便;或者使用非 Java 語言的開發(fā)者,需要 ShardingSphere 所提供的能力…… 以上這些情況,正是 ShardingSphere-Proxy 力所能及之處。
1. Proxy 應(yīng)用場景
日常工作中,大家使用 ShardingSphere-JDBC 進(jìn)行分庫分表的場景是比較多的。假設(shè)你有一張用戶表,通過用戶 ID 以 Hash 的方式進(jìn)行了水平分庫,那么此時客戶端連接數(shù)據(jù)庫的方式是這樣:
我們舉例工作中真實存在的幾個場景:
- 測試同學(xué)想看下用戶 ID 123456 的信息在數(shù)據(jù)庫表里情況,需要你提供下用戶在哪一張分表;
- 公司領(lǐng)導(dǎo)需要技術(shù)提供一份 2022 年用戶的增長總量以及用戶信息;
- 公司舉行 8 周年活動,需要技術(shù)提供一份注冊日期超過 8 周年的活躍老用戶名單。
因為數(shù)據(jù)分庫分表后,數(shù)據(jù)是散落在不同的庫表中,對于上述的場景實現(xiàn)并不容易;如果為了實現(xiàn)類似臨時需求,每次都需要開發(fā)代碼,顯得有些笨重。這個時候就需要文章主角 ShardingSphere-Proxy 登場了。
ShardingSphere-Proxy 隱藏了后端實際數(shù)據(jù)庫,對于客戶端來說就是在使用一個數(shù)據(jù)庫,不需要關(guān)心 ShardingSphere 如何協(xié)調(diào)背后的數(shù)據(jù)庫,對于使用非 Java 語言的開發(fā)者或 DBA 更友好。
比如說 t_user 在數(shù)據(jù)庫層面拆分為若干真實表:t_user_0 到 t_user_9,在客戶端操作 ShardingSphere-Proxy 的過程中,只會知道有一張 t_user 邏輯表,路由至真實表的過程都在 ShardingSphere-Proxy 內(nèi)部執(zhí)行。
- 邏輯表:相同結(jié)構(gòu)的水平拆分?jǐn)?shù)據(jù)庫(表)的邏輯名稱,是 SQL 中表的邏輯標(biāo)識。 例:用戶數(shù)據(jù)根據(jù)主鍵尾數(shù)拆分為 10 張表,分別是 t_user_0 到 t_user_9,他們的邏輯表名為 t_user。
- 真實表:在水平拆分的數(shù)據(jù)庫中真實存在的物理表。 即上個示例中的 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 是一個 Jar 包,底層通過重寫 JDBC 組件完成 SQL 解析、路由、改寫、執(zhí)行等流程;需要在項目中添加對應(yīng)功能的配置文件,對應(yīng)用有一定侵入性;
- ShardingSphere-Proxy 是一個進(jìn)程服務(wù),大部分情況下定位為輔助開發(fā)運(yùn)維的效率工具。它將自己偽裝為數(shù)據(jù)庫,應(yīng)用對接后對代碼是無侵入的;對 SQL 的執(zhí)行邏輯同 ShardingSphere-JDBC 一致,兩者復(fù)用同一內(nèi)核。
ShardingSphere-Proxy 既然對應(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ù)庫,又會發(fā)生一次網(wǎng)絡(luò) IO;
應(yīng)用調(diào)用鏈路多了一層,容易形成流量瓶頸,對應(yīng)用增加了潛在的風(fēng)險;一般來說,應(yīng)用程序會搭配 ShardingSphere-JDBC 使用。
當(dāng)然,ShardingSphere-JDBC 和 ShardingSphere-Proxy 可以進(jìn)行混合部署,ShardingSphere-JDBC 適用于 Java 開發(fā)的高性能的輕量級 OLTP 應(yīng)用,ShardingSphere-Proxy 適用于 OLAP 應(yīng)用以及對分片數(shù)據(jù)庫進(jìn)行管理和運(yùn)維的場景。
三、如何開始
ShardingSphere-Proxy 的啟動方式有三種:二進(jìn)制包、Docker、Helm,并分為單機(jī)部署和集群部署。文章以單機(jī)二進(jìn)制包的方式啟動。
- 通過 下載頁面[1] 獲取 ShardingSphere-Proxy 二進(jìn)制安裝包;
- 解壓縮后修改 conf/server.yaml 和以 config- 前綴開頭的文件,進(jìn)行分片、讀寫分離等規(guī)則配置;
- Linux 操作系統(tǒng)請運(yùn)行 bin/start.sh,Windows 操作系統(tǒng)請運(yùn)行 bin/start.bat 啟動 ShardingSphere-Proxy。
下載后的文件目錄如下:
├── LICENSE
├── NOTICE
├── README.txt
├── bin # 啟動停止腳本
├── conf # 服務(wù)配置,分庫分表、讀寫分離、數(shù)據(jù)加密等功能的配置文件
├── lib # Jar 包
└── licenses
1. 將 MySQL 的 JDBC 驅(qū)動復(fù)制到 ext-lib 包
下載驅(qū)動 mysql-connector-java-5.1.47.jar[2] 或者 mysql-connector-java-8.0.11.jar[3] 放入 ext-lib 包。因為初始目錄中并沒有 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
需要注意,如果啟動單機(jī) ShardingSphere-Proxy,后續(xù)需要 Proxy 配置變更,建議將 mode.overwrite 設(shè)置為 true,這樣 ShardingSphere-Proxy 在啟動時就會重新加載元數(shù)據(jù)。
3. 啟動 ShardingSphere-Proxy
執(zhí)行啟動命令:sh bin/start.sh。默認(rèn)啟動端口 3307,可以通過啟動腳本命令追加參數(shù)的方式替換端口:sh bin/start.sh 3308。
查看 ShardingSphere-Proxy 是否啟動成功,執(zhí)行查看日志命令:tail -100f logs/stdout.log。如最后一行出現(xiàn)下述信息,即為啟動成功:
[INFO ] xxx-xx-xx xx:xx:xx.xxx [main] o.a.s.p.frontend.ShardingSphereProxy - ShardingSphere-Proxy Standalone mode started successfully
四、場景實踐
本章節(jié)從實戰(zhàn)場景的前提出發(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. 分片測試
使用 MySQL 終端命令連接 ShardingSphere-Proxy 服務(wù)端。如果 Docker 部署的數(shù)據(jù)庫,需要加上 -h 本機(jī) ip。因為容器內(nèi)訪問 127.0.0.1 不通。
# 將 {xx} 替換為實際參數(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)建時間 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ù)據(jù)分別散落在 user_sharding_0 和 user_sharding_1 庫。
回到最初的問題,如何定位數(shù)據(jù)信息。因為 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)
第二個問題,查詢 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)
第三個問題同上。
五、最后總結(jié)
文章通過圖文并茂的方式幫助大家過了一遍 ShardingSphere-Proxy 的基本概念,引申出了分庫分表后產(chǎn)生的實際運(yùn)維場景,并演示了如何通過 ShardingSphere-Proxy 解決相關(guān)問題。
相信大家看完對 ShardingSphere-Proxy 有了更深入的認(rèn)識。首先要明白 ShardingSphere-Proxy 的定位是協(xié)助開發(fā)運(yùn)維的產(chǎn)品,掌握 ShardingSphere-JDBC 和 ShardingSphere-Proxy 有哪些區(qū)別,以及理解兩者的優(yōu)缺點(diǎn)和實現(xiàn)方式是怎么樣的。在這個基礎(chǔ)上去閱讀兩者的源碼,也就更容易理解了。
參考
[1]下載頁面: 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