文章目录
- 简介
- 基础使用
- 部署ShardingProxy
- 配置分库分表策略
- 分布式事务机制
- 介绍
- XA事务Demo
- 使用另外两种XA事务管理器
简介
ShardingSphere的两个核心产品分别为ShardingJDBC和ShardingProxy。前文已经详细介绍了ShardingJDBC的具体使用,接下来介绍服务端的分库分表ShardingProxy。
定位为一个透明化的数据库代理,目前提供MySQL和PostgreSQL协议,透明化数据库操作。简单理解就是,他会部署成一个MySQL或者PostgreSQL的数据库服务,应用程序只需要像操作单个数据库一样去访问ShardingSphere-proxy,由ShardingProxy去完成分库分表功能。
ShardingProxy的主要功能如下:
-
配合 ORM 框架使用更友好
当使用 ShardingSphere-JDBC 时,需要在代码中直接编写分库分表的逻辑,如果使用 ORM 框架,会产生冲突。ShardingSphere-Proxy 作为服务端中间件,可以无缝对接 ORM 框架。
-
对 DBA 更加友好
ShardingSphere-Proxy 作为服务端代理,对 DBA 完全透明。DBA 可以直接操作原始数据源,而不需要了解 ShardingSphere 的功能和 API。这简化了 DBA 的工作,也不会产生额外学习成本。
-
避免在项目中侵入分库分表逻辑
使用 ShardingSphere-JDBC,需要在业务代码中编写分库分表规则配置,这会使代码显得繁琐,且一旦规则变更,需要修改大量代码。ShardingSphere-Proxy 通过外部配置实现规则管理,可以避免这种情况。
-
提供分布式事务支持
ShardingSphere-Proxy 可以与第三方事务管理器对接,提供对分布式数据库的分布式事务支持。而 ShardingSphere-JDBC 仅支持本地事务。
-
实现无中心化数据治理
通过 ShardingSphere-Proxy,可以将多个数据源注册到同一个代理服务中,实现跨数据源的数据治理、监控和报警等功能。这有利于大规模微服务系统的运维。
基础使用
部署ShardingProxy
ShardingProxy 5.2.1版本下载
直接下载地址
ShardingProxy在windows和Linux上提供了一套统一的部署发布包。下载apache-shardingsphere-5.2.1-shardingsphere-proxy-bin.tar.gz文件后,直接进行解压
解压完成后,我们需要把MySQL的JDBC驱动包mysql-connector-java-8.0.15.jar手动复制到%SHARDINGSPHERE_PROXY_HOME%/ext-lib
目录下,ext-lib目录需要我们自己创建,所有扩展的jar包都丢在这里面。ShardingProxy默认只附带了PostgreSQL的JDBC驱动包,而不包含MySQL的JDBC驱动包。
接下来进入conf目录,进行相关的配置,常见的几个配置文件如下所示
每个配置文件里都给出了配置的示例,照着改改就行。再加上我们学习过ShardingJDBC,所以基本上是无门槛看懂这些配资
先打开server.yaml,更改其中的配置
mode:
type: standalone # 我这里先以单机启动
# type: Cluster
# repository:
# type: ZooKeeper
# props:
# namespace: governance_ds
# server-lists: localhost:2181
# retryIntervalMilliseconds: 500
# timeToLiveSeconds: 60
# maxRetries: 3
# operationTimeoutMilliseconds: 500
rules:
- !AUTHORITY
users: # 连接用户名密码
- root@%:root
- sharding@:sharding
provider: # 权限
type: ALL_PERMITTED
- !TRANSACTION # 分布式事务
defaultType: XA
providerType: Atomikos
- !SQL_PARSER
sqlCommentParseEnabled: true
sqlStatementCache:
initialCapacity: 2000
maximumSize: 65535
parseTreeCache:
initialCapacity: 128
maximumSize: 1024
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-hint-enabled: false # 是否允许hint
sql-show: false # 是否打印日志
check-table-metadata-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
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.
# Available sql federation type: NONE (default), ORIGINAL, ADVANCED
sql-federation-type: NONE
# Available proxy backend driver type: JDBC (default), ExperimentalVertx
proxy-backend-driver-type: JDBC
proxy-mysql-default-version: 8.0.15 # 修改成我们自己的版本
proxy-default-port: 3307 # 连接端口
proxy-netty-backlog: 1024 # Proxy netty backlog.
-
mode部分的配置就是ShardingProxy的启动方式,默认是集群方式启动的,配置中心使用的是zookeeper,我们也可以先暂时改为单机启动
-
rules
- AUTHORITY部分就是登录用户名密码以及权限,前文就介绍过ShardingProxy是一个数据库代代理,我们可以把它当成一个数据库,这里就是该数据库的登录用户
- TRANSACTION部分分布式事务控制器,下文会详细介绍
-
props部分配置服务端的一些参数
-
max-connections-size-per-query参数是对于ShardingProxy最重要的优化参数,表示一个数据库连接对象最大执行的sql数。在上一章节介绍ShardingSphere的执行引擎以及结果归并时介绍到了,
如果只需要一个数据库连接就能完成相关操作就是内存限制模式,结果归并时是流式分组归并,查询相对慢,结果归并效率相对高;如果需要多个数据库连接才能完成相关操作就是连接限制模式,结果归并时是内存分组归并,因为多个连接同时查询所以查询相对快,结果归并效率相对慢。
举例:通过sql解析我知道了一次业务请求需要执行5条sql,但是我max-connections-size-per-query参数设置了1就表示一个连接对象只能执行一条sql,那么就需要5个连接对象,如果max-connections-size-per-query参数设置了5,就表示我只需要一个连接对象
-
proxy-mysql-default-version表示ShardingProxy所模拟的MySQL服务版本。为了与之前的示例兼容,我们这里可以将它改成8.0.15版本。
-
proxy-default-port表示模拟的MySQL服务的端口。 最好和本机mysql真实启动端口区分开,避免端口冲突
-
接下来运行bin/start.bat进行启动
进行连接测试
配置分库分表策略
我们此时连接,发下会报错,因为我们还没有配置相关的分库分表路由策略
接下来操作conf/config-sharding.yaml文件,该文件中上方是postgresql配置,下方是mysql配置。具体的配置内容就和ShardingJDBC一样,具体配置如下
# 指定数据库名,也就是连接ShardingProxy时 使用的是哪一个数据库
databaseName: sharding_hs_db
dataSources:
ds_0:
url: jdbc:mysql://localhost:3306/sharding_sphere1?serverTimezone=UTC&useSSL=false
username: root
password: 1234
connectionTimeoutMilliseconds: 30000
idleTimeoutMilliseconds: 60000
maxLifetimeMilliseconds: 1800000
maxPoolSize: 50
minPoolSize: 1
ds_1:
url: jdbc:mysql://localhost:3306/sharding_sphere2?serverTimezone=UTC&useSSL=false
username: root
password: 1234
connectionTimeoutMilliseconds: 30000
idleTimeoutMilliseconds: 60000
maxLifetimeMilliseconds: 1800000
maxPoolSize: 50
minPoolSize: 1
rules:
- !SHARDING
tables:
# 逻辑表
sys_user:
actualDataNodes: ds_${0..1}.sys_user${1..2}
# 分表策略
tableStrategy:
standard:
shardingColumn: uid
shardingAlgorithmName: sys_user_tab_alg
# 分布式主键生成策略
keyGenerateStrategy:
column: uid
keyGeneratorName: alg_snowflake
# 默认分库策略
defaultDatabaseStrategy:
standard:
shardingColumn: uid
shardingAlgorithmName: database_inline
# 默认分表策略
defaultTableStrategy:
none:
# 分片策略
shardingAlgorithms:
database_inline:
type: INLINE
props:
algorithm-expression: ds_${uid % 2}
sys_user_tab_alg:
type: INLINE
props:
algorithm-expression: sys_user$->{((uid+1)%4).intdiv(2)+1}
# 分布式主键生成策略
keyGenerators:
alg_snowflake:
type: COSID_SNOWFLAKE
接下来再重启ShardingProxy服务,在进行连接
多数据库,出现为分片数据表同名情况
这里有一个小问题,如下图所示,我两个真实数据库中都存在user_info1 user_info2数据表,我没有在ShardingProxy中配置user_info数据表相关的分片规则,此时只有一个sys_user表的分片规则,在ShardingProxy中保存是user_info1 user_info2数据表的内容只有下面两个数据库的其中一个数据库中的内容。
真实库中,存在真实表和分片逻辑表同名情况
直接访问ShardingProxy的sys_user逻辑表,只会查询到对应分片路由规则真实表数据,而不会存在下方uid=1026的数据
分布式事务机制
介绍
开发者手册
用户手册
在ShardingProxy的conf/server.yaml配置文件中,有以下的默认配置项
ShardingProxy默认是使用的XA方式来保证的分布式事务,而且默认使用的是Atomikos框架。
我们从开发者手册可以得知,目前它支持三种XA分布式事务管理器
全限定类名:org.apache.shardingsphere.transaction.xa.spi.XATransactionManagerProvider
XA 分布式事务管理器,已知实现
配置标识 | 详细说明 | 类名 |
---|---|---|
Atomikos | 基于 Atomikos 的 XA 分布式事务管理器 | AtomikosTransactionManagerProvider |
Narayana | 基于 Narayana 的 XA 分布式事务管理器 | NarayanaXATransactionManagerProvider |
Bitronix | 基于 Bitronix 的 XA 分布式事务管理器 | BitronixXATransactionManagerProvider |
XA事务Demo
引入Maven依赖
<!--XA 分布式事务 -->
<dependency>
<groupId>org.apache.shardingsphere</groupId>
<artifactId>shardingsphere-transaction-xa-core</artifactId>
<version>5.2.1</version>
<exclusions>
<exclusion>
<artifactId>transactions-jdbc</artifactId>
<groupId>com.atomikos</groupId>
</exclusion>
<exclusion>
<artifactId>transactions-jta</artifactId>
<groupId>com.atomikos</groupId>
</exclusion>
</exclusions>
</dependency>
<!-- 版本滞后了 -->
<dependency>
<artifactId>transactions-jdbc</artifactId>
<groupId>com.atomikos</groupId>
<version>5.0.8</version>
</dependency>
<dependency>
<artifactId>transactions-jta</artifactId>
<groupId>com.atomikos</groupId>
<version>5.0.8</version>
</dependency>
<!-- 使用XA事务时,可以引入其他几种事务管理器 -->
<!-- <dependency>-->
<!-- <groupId>org.apache.shardingsphere</groupId>-->
<!-- <artifactId>shardingsphere-transaction-xa-bitronix</artifactId>-->
<!-- <version>5.2.1</version>-->
<!-- </dependency>-->
<!-- <dependency>-->
<!-- <groupId>org.apache.shardingsphere</groupId>-->
<!-- <artifactId>shardingsphere-transaction-xa-narayana</artifactId>-->
<!-- <version>5.2.1</version>-->
<!-- </dependency>-->
配置事务管理器
@Configuration
@EnableTransactionManagement
public class TransactionConfiguration {
@Bean
public PlatformTransactionManager txManager(final DataSource dataSource) {
return new DataSourceTransactionManager(dataSource);
}
}
然后就可以写一个示例
public class MySQLXAConnectionTest {
public static void main(String[] args) throws SQLException {
//true表示打印XA语句,,用于调试
boolean logXaCommands = true;
// 获得资源管理器操作接口实例 RM1
Connection conn1 = DriverManager.getConnection("jdbc:mysql://localhost:3306/coursedb?serverTimezone=UTC", "root", "root");
XAConnection xaConn1 = new MysqlXAConnection((com.mysql.cj.jdbc.JdbcConnection) conn1, logXaCommands);
XAResource rm1 = xaConn1.getXAResource();
// 获得资源管理器操作接口实例 RM2
Connection conn2 = DriverManager.getConnection("jdbc:mysql://localhost:3306/coursedb2?serverTimezone=UTC", "root", "root");
XAConnection xaConn2 = new MysqlXAConnection((com.mysql.cj.jdbc.JdbcConnection) conn2, logXaCommands);
XAResource rm2 = xaConn2.getXAResource();
// AP请求TM执行一个分布式事务,TM生成全局事务id
byte[] gtrid = "g12345".getBytes();
int formatId = 1;
try {
// ==============分别执行RM1和RM2上的事务分支====================
// TM生成rm1上的事务分支id
byte[] bqual1 = "b00001".getBytes();
Xid xid1 = new MysqlXid(gtrid, bqual1, formatId);
// 执行rm1上的事务分支
rm1.start(xid1, XAResource.TMNOFLAGS);//One of TMNOFLAGS, TMJOIN, or TMRESUME.
PreparedStatement ps1 = conn1.prepareStatement("INSERT INTO `dict` VALUES (1, 'T', '测试1');");
ps1.execute();
rm1.end(xid1, XAResource.TMSUCCESS);
// TM生成rm2上的事务分支id
byte[] bqual2 = "b00002".getBytes();
Xid xid2 = new MysqlXid(gtrid, bqual2, formatId);
// 执行rm2上的事务分支
rm2.start(xid2, XAResource.TMNOFLAGS);
PreparedStatement ps2 = conn2.prepareStatement("INSERT INTO `dict` VALUES (2, 'F', '测试2');");
ps2.execute();
rm2.end(xid2, XAResource.TMSUCCESS);
// ===================两阶段提交================================
// phase1:询问所有的RM 准备提交事务分支
int rm1_prepare = rm1.prepare(xid1);
int rm2_prepare = rm2.prepare(xid2);
// phase2:提交所有事务分支
boolean onePhase = false; //TM判断有2个事务分支,所以不能优化为一阶段提交
if (rm1_prepare == XAResource.XA_OK
&& rm2_prepare == XAResource.XA_OK
) {//所有事务分支都prepare成功,提交所有事务分支
rm1.commit(xid1, onePhase);
rm2.commit(xid2, onePhase);
} else {//如果有事务分支没有成功,则回滚
rm1.rollback(xid1);
rm1.rollback(xid2);
}
} catch (XAException e) {
// 如果出现异常,也要进行回滚
e.printStackTrace();
}
}
}
这其中,XA标准规范了事务XID的格式。有三个部分: gtrid [, bqual [, formatID ]] 其中
- gtrid 是一个全局事务标识符 global transaction identifier
- bqual 是一个分支限定符 branch qualifier 。如果没有提供,会使用默认值就是一个空字符串。
- formatID 是一个数字,用于标记gtrid和bqual值的格式,这是一个正整数,最小为0,默认值就是1。
但是使用XA事务时需要注意以下几点:
- XA事务无法自动提交
- XA事务效率非常低下,全局事务的状态都需要持久化。性能非常低下,通常耗时能达到本地事务的10倍。
- XA事务在提交前出现故障的话,很难将问题隔离开。
使用另外两种XA事务管理器
我们从开发者手册可以得知,在ShardingProxy5.2.1版本中它支持三种XA分布式事务管理器。如果不想使用默认的Atomikos,而去使用Narayana,具体的操作如下所示:
全限定类名:org.apache.shardingsphere.transaction.xa.spi.XATransactionManagerProvider
XA 分布式事务管理器,已知实现
配置标识 | 详细说明 | 类名 |
---|---|---|
Atomikos | 基于 Atomikos 的 XA 分布式事务管理器 | AtomikosTransactionManagerProvider |
Narayana | 基于 Narayana 的 XA 分布式事务管理器 | NarayanaXATransactionManagerProvider |
Bitronix | 基于 Bitronix 的 XA 分布式事务管理器 | BitronixXATransactionManagerProvider |
我们微服务项目中,默认情况下都是没有这个包的
<dependency>
<groupId>org.apache.shardingsphere</groupId>
<artifactId>shardingsphere-jdbc-core-spring-boot-starter</artifactId>
<version>5.2.1</version>
</dependency>
我们可以通过下载ShardingProxy的源码包,从源码包中找上上方对应的NarayanaXATransactionManagerProvider,然后查看这个类所在是pom文件,最终找到pom依赖
<!-- <dependency>-->
<!-- <groupId>org.apache.shardingsphere</groupId>-->
<!-- <artifactId>shardingsphere-transaction-xa-bitronix</artifactId>-->
<!-- <version>5.2.1</version>-->
<!-- </dependency>-->
<!-- <dependency>-->
<!-- <groupId>org.apache.shardingsphere</groupId>-->
<!-- <artifactId>shardingsphere-transaction-xa-narayana</artifactId>-->
<!-- <version>5.2.1</version>-->
<!-- </dependency>-->
我们在微服务中导入上面对应的依赖,再从本地maven仓库中拿到对应的jar包,丢到%SHARDINGSPHERE_PROXY_HOME%/ext-lib
目录下
在server.yaml中就可以将事务的Provider配置成Narayana
下面这个Narayana字符串是通过源码NarayanaXATransactionManagerProvider类的getType()方法中定义的
rules:
- !TRANSACTION
defaultType: XA
providerType: Narayana