ShardingSphere之ShardingProxy实战操作、分布式事务



简介

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
  • 4
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值