你还在用Mycat?来试试ShardingSphere-Proxy吧

在前面三期我们讲了有关ShardingSphere的东西:

标题 链接
【实战篇】Docker安装MySQL集群 juejin.cn/post/753828...
ShardingSphere-JDBC入门教程(上篇) juejin.cn/post/753981...
ShardingSphere-JDBC入门教程(下篇) juejin.cn/post/754455...

那么这一期我们来讲一下ShardingSphere家族的ShardingSphere-Proxy,这篇文章将作为该系列的最后一篇文章。

我们再来回顾一下前面学习的ShardingSphere-JDBC是什么:

他是一个 Java 框架,可以不用安装任何东西直接在pom文件里面引入即可。我们再来看一下ShardingSphere-Proxy:

定位为透明化的数据库代理端,提供封装了数据库二进制协议的服务端版本,用于完成对异构语言的支持。 目前提供 MySQL 和 PostgreSQL(兼容 openGauss 等基于 PostgreSQL 的数据库)版本,它可以使用任何兼容 MySQL/PostgreSQL 协议的访问客户端(如:MySQL Command Client, MySQL Workbench, Navicat 等)操作数据,对 DBA 更加友好。

  • 向应用程序完全透明,可直接当做 MySQL/PostgreSQL 使用;
  • 适用于任何兼容 MySQL/PostgreSQL 协议的的客户端。

在开发界中有一句话说的好:没有问题是封装一层解决不了的,如果有,那就再加一层。

他和我们之前学过的ShardingSphere-JDBC(以下简称jdbc)的区别就是ShardingSphere-Proxy(以下简称proxy) 是与程序是分离开来的,jdbc 则是在pom文件导入jar、写死在properties配置文件中的。而proxy则是在应用程序中什么都不用改,像正常连接mysql一样的连接proxy就好,大家可以把他认作就是一个mysql集群的代理数据库,我们只需要直接连接proxy,而剩下的操作都交给proxy去处理就好了。

安装proxy

这里我们使用docker进行安装,首先我们要准备一个mysql的驱动jar包:mysql的驱动下载

运行docker命令跑docker镜像:

diff 复制代码
docker run -d 
-v C:\docker\proxy\proxy-a\conf:/opt/shardingsphere-proxy/conf 
-v C:\docker\proxy\proxy-a\ext-lib:/opt/shardingsphere-proxy/ext-lib 
-e ES_JAVA_OPTS="-Xmx256m -Xms256m -Xmn128m" 
-p 3321:3307 
--name server-proxy-a 
apache/shardingsphere-proxy:5.1.1

将下载下来的mysql驱动复制到C:\docker\proxy\proxy-a\ext-lib目录下面,因为我们要连接的是mysql所以需要这个驱动,而PostgreSQL则是不用驱动的。

我们在 C:\docker\proxy\proxy-a\conf 创建一个目录下面创建一个 server.yaml 文件,内容如下:

yaml 复制代码
rules:
  - !AUTHORITY
    users:
      - root@%:root
    provider:
      type: ALL_PRIVILEGES_PERMITTED

props:
  sql-show: true

这里的作用是我们创建了一个拥有所有权限的root用户,密码也是root。下面那个就是显示sql。

之后重启容器就好了

shell 复制代码
docker restart server-proxy-a

重启完成之后我们就可以和mysql一样去连接proxy了:

但是这个时候如果我们点击这个连接就会报错:

上面显示没我们没有配置数据源或者规则配置不对,因为他本身是一个中间件,没有存储数据的功能,所以我们需要配置使得proxy能连接上我们的mysql数据库

垂直分片

在上一期我们配置了jdbc的垂直分片:

ShardingSphere-JDBC入门教程(上篇)

在上篇文章中垂直分片的mysql环境已经被我们搭建好了,所有我们直接使用该mysql环境,我们同样在 C:\docker\proxy\proxy-a\conf 创建一个目录下面创建一个 config-sharding.yaml 文件,内容如下:

yaml 复制代码
schemaName: sharding_db

dataSources:
  ds_0:
    url: jdbc:mysql://172.19.200.70:13306/d_user?serverTimezone=UTC&useSSL=false
    username: root
    password: 123456
    connectionTimeoutMilliseconds: 30000
    idleTimeoutMilliseconds: 60000
    maxLifetimeMilliseconds: 1800000
    maxPoolSize: 50
    minPoolSize: 1
  ds_1:
    url: jdbc:mysql://172.19.200.70:13307/d_order?serverTimezone=UTC&useSSL=false
    username: root
    password: 123456
    connectionTimeoutMilliseconds: 30000
    idleTimeoutMilliseconds: 60000
    maxLifetimeMilliseconds: 1800000
    maxPoolSize: 50
    minPoolSize: 1

rules:
  - !SHARDING
    tables:
      t_user:
        actualDataNodes: ds_0.t_user
      t_order:
        actualDataNodes: ds_1.t_order

可以看到这个里面的配置和我们之前的properties几乎一模一样,只不过位置从我们之前写在java程序的配置文件里面变为了写在proxy的配置文件里面了,对应用程序是无感的,但是这里有个小细节:ip不能和之前一样写127.0.0.1,要写本机的ip地址,如果知道容器的原理就很好懂了,因为要通过宿主机找其他容器的地址。

那么连接之后呢,我们发现没有报错了,多了一个叫做 sharding_db的数据库,而这个数据库名字的由来就是我们在配置文件里面写的 schemaName属性,我们点进去查看这个数据库,发现order表和user表赫然立在此处,这就是我们上次的数据表。

包括里面的数据都是存在的:

Java程序连接

其中程序我们还是用到了上一期文章里面的程序,唯二不同的是pom文件取消了 shardingsphere-jdbc-core-spring-boot-starter jar包的导入

和配置文件取消了一系列复杂的配置,就和直接连接mysql是一模一样的:

properties 复制代码
server.port=8083

#mysql数据库连接(proxy)
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.url=jdbc:mysql://127.0.0.1:3321/sharding_db?serverTimezone=GMT%2B8&useSSL=false
spring.datasource.username=root
spring.datasource.password=root

#mybatis日志
mybatis-plus.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl

没了??对,应用程序就是这么干净,然后我们在测试一下是否可以成功:

properties 复制代码
package com.masiyi.shardingsphere;

import com.masiyi.shardingsphere.entity.Order;
import com.masiyi.shardingsphere.entity.OrderItem;
import com.masiyi.shardingsphere.entity.User;
import com.masiyi.shardingsphere.mapper.OrderItemMapper;
import com.masiyi.shardingsphere.mapper.OrderMapper;
import com.masiyi.shardingsphere.mapper.UserMapper;
import com.masiyi.shardingsphere.vo.OrderVo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.math.BigDecimal;
import java.util.List;

@RestController
@RequestMapping
public class UserController {

    @Autowired
    private UserMapper userMapper;
    @Autowired
    private OrderMapper orderMapper;
    @Autowired
    private OrderItemMapper orderItemMapper;



    /**
     * 插入数据测试
     */
    @PostMapping("insert/database")
    public void testInsertOrderDatabaseStrategy(){

        for (long i = 0; i < 4; i++) {
            Order order = new Order();
            order.setOrderNo("2025120488992");
            order.setUserId(i + 1);
            order.setAmount(new BigDecimal(100));
            orderMapper.insert(order);
        }

    }

}

执行接口插入,查看数据库也是有数据的:

那么接下来我们就查看其他的两种配置

水平分片

没错,这个也是创建一个yaml文件,我们这次就叫 config-sharding-bindingTables.yaml,这里我们名字要以 config-xxx开头名字可以随便叫,因为proxy会读这个文件夹里面的 这两个文件 %SHARDINGSPHERE_PROXY_HOME%/conf/config-xxx.yaml%SHARDINGSPHERE_PROXY_HOME%/conf/server.yaml

yaml 复制代码
schemaName: sharding_db_1

dataSources:
  d_user:
    url: jdbc:mysql://172.19.200.70:13306/d_user?serverTimezone=UTC&useSSL=false
    username: root
    password: 123456
    connectionTimeoutMilliseconds: 30000
    idleTimeoutMilliseconds: 60000
    maxLifetimeMilliseconds: 1800000
    maxPoolSize: 50
    minPoolSize: 1
  d_order0:
    url: jdbc:mysql://172.19.200.70:13308/d_order?serverTimezone=UTC&useSSL=false
    username: root
    password: 123456
    connectionTimeoutMilliseconds: 30000
    idleTimeoutMilliseconds: 60000
    maxLifetimeMilliseconds: 1800000
    maxPoolSize: 50
    minPoolSize: 1
  d_order1:
    url: jdbc:mysql://172.19.200.70:13309/d_order?serverTimezone=UTC&useSSL=false
    username: root
    password: 123456
    connectionTimeoutMilliseconds: 30000
    idleTimeoutMilliseconds: 60000
    maxLifetimeMilliseconds: 1800000
    maxPoolSize: 50
    minPoolSize: 1

rules:
  - !SHARDING
    tables:
      t_user:
        actualDataNodes: d_user.t_user
      
      t_order:
        actualDataNodes: d_order${0..1}.t_order${0..1}
        databaseStrategy:
          standard:
            shardingColumn: f_user_id
            shardingAlgorithmName: alg_mod
        tableStrategy:
          standard:
            shardingColumn: f_order_no
            shardingAlgorithmName: alg_hash_mod
        keyGenerateStrategy:
          column: f_id
          keyGeneratorName: snowflake
      t_order_item:
        actualDataNodes: d_order${0..1}.t_order_item${0..1}
        databaseStrategy:
          standard:
            shardingColumn: f_user_id
            shardingAlgorithmName: alg_mod
        tableStrategy:
          standard:
            shardingColumn: f_order_no
            shardingAlgorithmName: alg_hash_mod
        keyGenerateStrategy:
          column: f_id
          keyGeneratorName: snowflake
    
    bindingTables:
      - t_order,t_order_item

    
    shardingAlgorithms:
      alg_inline_userid:
        type: INLINE
        props:
          algorithm-expression: d_order$->{f_user_id % 2}
      alg_mod:
        type: MOD
        props:
          sharding-count: 2
      alg_hash_mod:
        type: HASH_MOD
        props:
          sharding-count: 2
    
    keyGenerators:
      snowflake:
        type: SNOWFLAKE

那么配置之后呢,我们的proxy 里面就多了一个数据库 :sharding_db_1

配置文件改为连接改数据库之后写个接口测试一下:

java 复制代码
    /**
     * 测试关联表插入
     */
    @PostMapping("insert/orderAndOrderItem")
    public void testInsertOrderAndOrderItem(){

        for (long i = 1; i < 3; i++) {

            Order order = new Order();
            order.setOrderNo("wangfugui20250840" + i);
            order.setUserId(1L);
            orderMapper.insert(order);

            for (long j = 1; j < 3; j++) {
                OrderItem orderItem = new OrderItem();
                orderItem.setOrderNo("wangfugui20250840" + i);
                orderItem.setUserId(1L);
                orderItem.setPrice(new BigDecimal(10));
                orderItem.setCount(2);
                orderItemMapper.insert(orderItem);
            }
        }

        for (long i = 5; i < 7; i++) {

            Order order = new Order();
            order.setOrderNo("wangfugui20250840" + i);
            order.setUserId(2L);
            orderMapper.insert(order);

            for (long j = 1; j < 3; j++) {
                OrderItem orderItem = new OrderItem();
                orderItem.setOrderNo("wangfugui20250840" + i);
                orderItem.setUserId(2L);
                orderItem.setPrice(new BigDecimal(1));
                orderItem.setCount(3);
                orderItemMapper.insert(orderItem);
            }
        }

    }

可以看到数据成功插入:

读写分离

C:\docker\proxy\proxy-a\conf 创建一个目录下面创建一个 config-readwrite-splitting.yaml 文件,内容如下:

java 复制代码
schemaName: readwrite_splitting_db

dataSources:
  write_ds:
    url: jdbc:mysql://${IP}:3306/db_user?serverTimezone=UTC&useSSL=false
    username: root
    password: 123456
    connectionTimeoutMilliseconds: 30000
    idleTimeoutMilliseconds: 60000
    maxLifetimeMilliseconds: 1800000
    maxPoolSize: 50
    minPoolSize: 1
  read_ds_0:
    url: jdbc:mysql://${IP}:3307/db_user?serverTimezone=UTC&useSSL=false
    username: root
    password: 123456
    connectionTimeoutMilliseconds: 30000
    idleTimeoutMilliseconds: 60000
    maxLifetimeMilliseconds: 1800000
    maxPoolSize: 50
    minPoolSize: 1
  read_ds_1:
    url: jdbc:mysql://${IP}:3308/db_user?serverTimezone=UTC&useSSL=false
    username: root
    password: 123456
    connectionTimeoutMilliseconds: 30000
    idleTimeoutMilliseconds: 60000
    maxLifetimeMilliseconds: 1800000
    maxPoolSize: 50
    minPoolSize: 1

rules:
- !READWRITE_SPLITTING
  dataSources:
    readwrite_ds:
      type: Static
      props:
        write-data-source-name: write_ds
        read-data-source-names: read_ds_0,read_ds_1

ok啊,这就是本期博客的全部内容,我们使用下来发现,我们通过proxy配置的数据库和数据表全都是一个:

但是我们得知道,这个表实际是对应的两个库里面的两个表:

是不是觉得proxy很厉害,对于程序来说也只是一个地址,对于用户来说也是透明的(例如Navicat也是一个表和一个库,你不用关心底层是如何实现如何存储的),作者在之前写文章的时候也考虑过MyCat,但是这个现在的资料实在太少了,社区也不如proxy活跃,所以最终选择了 ShardingSphere 的产品。

相关推荐
一休哥助手9 小时前
HiMarket:开源AI中台革命——企业智能化的新基建
大数据·人工智能·开源
武子康9 小时前
大数据-86 Spark+Scala实现WordCount:大数据学习的入门实践
大数据·后端·spark
tonydf9 小时前
MinIO祭了,RustFS来了!
后端
卓伊凡9 小时前
苹果开发中什么是Storyboard?object-c 和swiftui 以及Storyboard到底有什么关系以及逻辑?优雅草卓伊凡
前端·后端
华仔啊9 小时前
前后端为个下拉框又吵起来了?一招优雅的 Java 枚举解法,让团队和谐开发!
java·后端
Charlo9 小时前
是什么让一个AI系统成为智能体(Agent)?
前端·后端
哈基米喜欢哈哈哈9 小时前
MongoDB入门
数据库·后端·mongodb
石小石Orz9 小时前
来自面试官给我的建议,我备受启发
前端·后端·面试
Java水解9 小时前
MySQL 高级查询:JOIN、子查询、窗口函数
后端·mysql