ShardingSphere5学习笔记

文章目录

  • 一、简介
    • 1、介绍
    • 2、读写分离架构
      • 2.1 理论介绍
      • 2.2 CAP理论
    • 3、数据库分片架构
      • 3.1 垂直分片
      • 3.2 水平分片
    • 4、ShardingSphere介绍
      • 4.1 简介
      • 4.2 ShardingSphere-JDBC
      • 4.3 ShardingSphere-Proxy
  • 二、MySQL主从配置
    • 1、MySQL主从同步原理
    • 2、主服务器准备
    • 3、从服务器准备
    • 4、启动主从同步
    • 5、常见问题
      • 5.1 示例一
      • 5.2 示例二
  • 三、ShardingSphere-JDBC
    • 1、ShardingSphere-JDBC读写分离
      • 1.1 环境与项目创建
      • 1.2 配置文件
      • 1.3 测试
    • 2、ShardingSphere-JDBC垂直分片
      • 2.1 环境准备
      • 2.2 程序实现
      • 2.3 测试垂直分片
  • 四、ShardingSphere-JDBC水平分片(常用)
    • 1、环境准备
    • 2、基本水平分片
      • 2.1 基本配置
      • 2.2 数据源配置
      • 2.3 标椎分片表配置
      • 2.4 行表达式
      • 2.5 分片算法配置
      • 2.6 分布式序列算法
    • 3、多表关联
    • 4、绑定表
    • 5、广播表
      • 5.1 广播表介绍
      • 5.2 创建广播表
      • 5.3 程序实现
      • 5.4 测试
  • 五、ShardingSphere-Proxy
    • 1、环境搭建与安装
      • 1.1 获取包
      • 1.2 使用二进制发布包安装
      • 1.3 使用Docker安装
    • 2、ShardingSphere-Proxy读写分离
      • 2.1 修改配置文件
      • 2.2 实时查看日志
      • 2.3 远程访问测试
      • 2.4 应用程序访问Proxy
    • 3、ShardingSphere-Proxy垂直分片
    • 4、ShardingSphere-Proxy水平分片

一、简介

1、介绍

本文属于改参考文章的升级版,部分内容可以参考:ShardingSphere数据库中间件基础学习

互联网业务兴起之后,海量用户加上海量数据的特点,单个数据库服务器已经难以满足业务需要,必须考虑数据库集群的方式来提升性能。高性能数据库集群的第一种方式是"读写分离",第二种方式是"数据库分片";

读写分离和数据分片具体的实现方式一般有两种: 程序代码封装中间件封装,中间件软件有**Apache ShardingSphere(程序级别和中间件级别)**和MyCat(数据库中间件)

2、读写分离架构

2.1 理论介绍

**读写分离原理:**读写分离的基本原理是将数据库读写操作分散到不同的节点上

读写分离的基本实现:

  • 主库负责处理事务性的增删改操作,从库负责处理查询操作,能够有效的避免由数据更新导致的行锁,使得整个系统的查询性能得到极大的改善。

  • 读写分离是根据 SQL 语义的分析将读操作和写操作分别路由至主库与从库

  • 通过一主多从的配置方式,可以将查询请求均匀的分散到多个数据副本,能够进一步的提升系统的处理能力。

  • 使用多主多从的方式,不但能够提升系统的吞吐量,还能够提升系统的可用性,可以达到在任何一个数据库宕机,甚至磁盘物理损坏的情况下仍然不影响系统的正常运行。

2.2 CAP理论

CAP 定理(CAP theorem)又被称作布鲁尔定理(Brewer’s theorem),是加州大学伯克利分校的计算机科学家埃里克·布鲁尔(Eric Brewer)在 2000 年的 ACM PODC 上提出的一个猜想。在一个分布式系统中,当涉及读写操作时,只能保证**一致性(Consistence)、可用性(Availability)、分区容错性(Partition Tolerance)**三者中的两个,另外一个必须被牺牲。

  • C 一致性(Consistency):对某个指定的客户端来说,读操作保证能够返回最新的写操作结果

  • A 可用性(Availability):非故障的节点在合理的时间内返回合理的响应(不是错误和超时的响应)

  • P 分区容忍性(Partition Tolerance):当出现网络分区后(可能是丢包,也可能是连接中断,还可能是拥塞),系统能够继续“履行职责”

CAP特点:

  • CP:为了保证一致性,当发生分区现象后,N1 节点上的数据已经更新到 y,但由于 N1 和 N2 之间的复制通道中断,数据 y 无法同步到 N2,N2 节点上的数据还是 x。这时客户端 C 访问 N2 时,N2 需要返回 Error,提示客户端 C“系统现在发生了错误”,这种处理方式违背了可用性(Availability)的要求,因此 CAP 三者只能满足 CP

  • AP:为了保证可用性,当发生分区现象后,N1 节点上的数据已经更新到 y,但由于 N1 和 N2 之间的复制通道中断,数据 y 无法同步到 N2,N2 节点上的数据还是 x。这时客户端 C 访问 N2 时,N2 将当前自己拥有的数据 x 返回给客户端 C 了,而实际上当前最新的数据已经是 y 了,这就不满足一致性(Consistency)的要求了,因此 CAP 三者只能满足 AP。注意:这里 N2 节点返回 x,虽然不是一个“正确”的结果,但是一个“合理”的结果,因为 x 是旧的数据,并不是一个错乱的值,只是不是最新的数据而已

CAP 理论中的 C 在实践中是不可能完美实现的,在数据复制的过程中,节点N1 和节点 N2 的数据并不一致(强一致性)。即使无法做到强一致性,但应用可以采用适合的方式达到最终一致性。具有如下特点:

  • 基本可用(Basically Available):分布式系统在出现故障时,允许损失部分可用性,即保证核心可用。

  • 软状态(Soft State):允许系统存在中间状态,而该中间状态不会影响系统整体可用性。这里的中间状态就是 CAP 理论中的数据不一致。

  • 最终一致性(Eventual Consistency):系统中的所有数据副本经过一定时间后,最终能够达到一致的状态。

3、数据库分片架构

  • 读写分离的问题

    读写分离分散了数据库读写操作的压力,但没有分散存储压力,为了满足业务数据存储的需求,就需要将存储分散到多台数据库服务器上

  • 数据分片

    将存放在单一数据库中的数据分散地存放至多个数据库或表中,以达到提升性能瓶颈以及可用性的效果。 数据分片的有效手段是对关系型数据库进行分库和分表。数据分片的拆分方式又分为垂直分片和水平分片

3.1 垂直分片

  • 垂直分库

    按照业务拆分的方式称为垂直分片,又称为纵向拆分,它的核心理念是专库专用。 在拆分之前,一个数据库由多个数据表构成,每个表对应着不同的业务。而拆分之后,则是按照业务将表进行归类,分布到不同的数据库中,从而将压力分散至不同的数据库。 垂直拆分可以缓解数据量和访问量带来的问题,但无法根治。如果垂直拆分之后,表中的数据量依然超过单节点所能承载的阈值,则需要水平分片来进一步处理

  • 垂直分表

    垂直分表适合将表中某些不常用的列,或者是占了大量空间的列拆分出去。假设我们是一个婚恋网站,用户在筛选其他用户的时候,主要是用 age 和 sex 两个字段进行查询,而 nickname 和 description 两个字段主要用于展示,一般不会在业务查询中用到。description 本身又比较长,因此我们可以将这两个字段独立到另外一张表中,这样在查询 age 和 sex 时,就能带来一定的性能提升。垂直分表引入的复杂性主要体现在表操作的数量要增加。

3.2 水平分片

阿里巴巴Java开发手册:【推荐】单表行数超过 500 万行或者单表容量超过 2GB,才推荐进行分库分表。说明:如果预计三年后的数据量根本达不到这个级别,请不要在创建表时就分库分表

水平分片又称为横向拆分。 相对于垂直分片,它不再将数据根据业务逻辑分类,而是通过某个字段(或某几个字段),根据某种规则将数据分散至多个库或表中,每个分片仅包含数据的一部分。 例如:根据主键分片,偶数主键的记录放入 0 库(或表),奇数主键的记录放入 1 库(或表)。单表进行切分后,是否将多个表分散在不同的数据库服务器中,可以根据实际的切分效果来确定。

  • 水平分表:单表切分为多表后,新的表即使在同一个数据库服务器中,也可能带来可观的性能提升,如果性能能够满足业务要求,可以不拆分到多台数据库服务器,毕竟业务分库也会引入很多复杂性;

  • 水平分库:如果单表拆分为多表后,单台服务器依然无法满足性能要求,那就需要将多个表分散在不同的数据库服务器中。

4、ShardingSphere介绍

4.1 简介

官网:https://shardingsphere.apache.org/index_zh.html
文档:https://shardingsphere.apache.org/document/5.1.1/cn/overview/

Apache ShardingSphere 由 JDBC、Proxy 和 Sidecar(规划中)这 3 款既能够独立部署,又支持混合部署配合使用的产品组成。

4.2 ShardingSphere-JDBC

程序代码封装。定位为轻量级 Java 框架,在 Java 的 JDBC 层提供的额外服务。 它使用客户端直连数据库,以 jar 包形式提供服务,无需额外部署和依赖,可理解为增强版的 JDBC 驱动,完全兼容 JDBC 和各种 ORM 框架
ShardingSphere5学习笔记_第1张图片

4.3 ShardingSphere-Proxy

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

ShardingSphere5学习笔记_第2张图片

二、MySQL主从配置

1、MySQL主从同步原理

详细也可以参考上一篇ShardingSphere文章
Docker参考文章:docker学习笔记

基本原理:

slave会从master读取binlog来进行数据同步

具体步骤:

  • step1:master将数据改变记录到二进制日志(binary log)中。

  • step2: 当slave上执行 start slave 命令之后,slave会创建一个 IO 线程用来连接master,请求master中的binlog。

  • step3:当slave连接master时,master会创建一个 log dump 线程,用于发送 binlog 的内容。在读取 binlog 的内容的操作中,会对主节点上的 binlog 加锁,当读取完成并发送给从服务器后解锁。

  • step4:IO 线程接收主节点 binlog dump 进程发来的更新之后,保存到 中继日志(relay log) 中。

  • step5:slave的SQL线程,读取relay log日志,并解析成具体操作,从而实现主从操作一致,最终数据一致。

2、主服务器准备

step1:docker中创建并启动MySQL主服务器端口3306

docker run -d \
-p 3306:3306 \
-v /data/mysql/master/conf:/etc/mysql/conf.d \
-v /data/master/data:/var/lib/mysql \
-e MYSQL_ROOT_PASSWORD=123456 \
--name mysql-master \
mysql:8.0.29

step2:创建MySQL主服务器配置文件,默认情况下MySQL的binlog日志是自动开启的,可以通过如下配置定义一些可选配置

vim /data/mysql/master/conf/my.cnf


[mysqld]
# 服务器唯一id,默认值1
server-id=1
# 设置日志格式,默认值ROW
binlog_format=STATEMENT
# 二进制日志名,默认binlog
# log-bin=binlog
# 设置需要复制的数据库,默认复制全部数据库,优先级比binlog-do-db高
#binlog-do-db=mytestdb
# 设置不需要复制的数据库,不配置binlog-do-db这个才生效
#binlog-ignore-db=mysql
#binlog-ignore-db=infomation_schema

最后重启

docker restart mysql-master

binlog格式说明:

  • binlog_format=STATEMENT:日志记录的是主机数据库的写指令,性能高,但是now()之类的函数以及获取系统参数的操作会出现主从数据不同步的问题。

  • binlog_format=ROW(默认):日志记录的是主机数据库的写后的数据,批量操作时性能较差,解决now()或者 user()或者 @@hostname 等操作在主从机器上不一致的问题。

  • binlog_format=MIXED:是以上两种level的混合使用,有函数用ROW,没函数用STATEMENT,但是无法识别系统变量

step3:使用命令行登录MySQL主服务器

#进入容器:env LANG=C.UTF-8 避免容器中显示中文乱码
docker exec -it mysql-master env LANG=C.UTF-8 /bin/bash
#进入容器内的mysql命令行
mysql -uroot -p
#修改默认密码校验方式
ALTER USER 'root'@'%' IDENTIFIED WITH mysql_native_password BY '123456';

step4:主机中创建slave用户

-- 创建slave用户
CREATE USER 'slave'@'%';
-- 设置密码
ALTER USER 'slave'@'%' IDENTIFIED WITH mysql_native_password BY '123456';
-- 授予复制权限
GRANT REPLICATION SLAVE ON *.* TO 'slave'@'%';
-- 刷新权限
FLUSH PRIVILEGES;

step5:主机中查询master状态

执行完此步骤后不要再操作主服务器MYSQL,防止主服务器状态值变化,记下FilePosition的值

SHOW MASTER STATUS;

3、从服务器准备

这里可以配置多台从机slave1、slave2…

step1:在docker中创建并启动MySQL从服务器:端口3307

docker run -d \
-p 3307:3306 \
-v /data/mysql/slave1/conf:/etc/mysql/conf.d \
-v //mysql/slave1/data:/var/lib/mysql \
-e MYSQL_ROOT_PASSWORD=123456 \
--name mysql-slave1 \
mysql:8.0.29

step2:创建MySQL从服务器配置文件,最后重启MySQL容器

vim /data/mysql/slave1/conf/my.cnf

[mysqld]
# 服务器唯一id,每台服务器的id必须不同,如果配置其他从机,注意修改id
server-id=2
# 中继日志名,默认xxxxxxxxxxxx-relay-bin
#relay-log=relay-bin

step3:使用命令行登录MySQL从服务器

#进入容器:
docker exec -it mysql-slave1 env LANG=C.UTF-8 /bin/bash
#进入容器内的mysql命令行
mysql -uroot -p
#修改默认密码校验方式
ALTER USER 'root'@'%' IDENTIFIED WITH mysql_native_password BY '123456';

step4:在从机上配置主从关系,在从机上执行以下SQL操作

CHANGE MASTER TO MASTER_HOST='192.168.249.135',MASTER_USER='slave',MASTER_PASSWORD='123456', MASTER_PORT=3306,MASTER_LOG_FILE='binlog.000003',MASTER_LOG_POS=1616; 

4、启动主从同步

启动从机的复制功能,执行SQL;当Slave_IO_Running: YesSlave_SQL_Running: Yes都为Yes时启动成功

START SLAVE;
-- 查看状态(不需要分号)
SHOW SLAVE STATUS\G

停止与重置

-- 在从机上执行。功能说明:停止I/O 线程和SQL线程的操作。
stop slave; 

-- 在从机上执行。功能说明:用于删除SLAVE数据库的relaylog日志文件,并重新启用新的relaylog文件。
reset slave;

-- 在主机上执行。功能说明:删除所有的binglog日志文件,并将日志索引文件清空,重新开始所有新的日志文件。
-- 用于第一次进行搭建主从库时,进行主库binlog初始化工作;
reset master;

测试,在主机中执行以下SQL,在从机中查看数据库、表和数据是否已经被同步

CREATE DATABASE db_user;
USE db_user;
CREATE TABLE t_user (
 id BIGINT AUTO_INCREMENT,
 uname VARCHAR(30),
 PRIMARY KEY (id)
);
INSERT INTO t_user(uname) VALUES('shawn');
-- 动态获取主机名字
INSERT INTO t_user(uname) VALUES(@@hostname);

5、常见问题

5.1 示例一

启动主从同步后,常见错误是Slave_IO_Running: No 或者 Connecting 的情况,此时查看下方的 Last_IO_ERROR错误日志,根据日志中显示的错误信息在网上搜索解决方案即可

典型的错误例如:Last_IO_Error: Got fatal error 1236 from master when reading data from binary log: 'Client requested master to start replication from position > file size'

解决方案:

-- 在从机停止slave
SLAVE STOP;

-- 在主机查看mater状态
SHOW MASTER STATUS;
-- 在主机刷新日志
FLUSH LOGS;
-- 再次在主机查看mater状态(会发现File和Position发生了变化)
SHOW MASTER STATUS;
-- 修改从机连接主机的SQL,并重新连接即可

5.2 示例二

启动docker容器后提示 WARNING: IPv4 forwarding is disabled. Networking will not work.此错误,虽然不影响主从同步的搭建,但是如果想从远程客户端通过以下方式连接docker中的MySQL则没法连接

解决方案:

#修改配置文件,这是Centos的解决方案,原理是这样
vim /usr/lib/sysctl.d/00-system.conf
#追加
net.ipv4.ip_forward=1
#接着重启网络
systemctl restart network

三、ShardingSphere-JDBC

1、ShardingSphere-JDBC读写分离

https://shardingsphere.apache.org/document/5.1.1/cn/user-manual/shardingsphere-jdbc/spring-boot-starter/mode/

1.1 环境与项目创建

添加依赖

<dependencies>
    <dependency>
      <groupId>org.springframework.bootgroupId>
      <artifactId>spring-boot-starter-webartifactId>
    dependency>

    <dependency>
      <groupId>org.apache.shardingspheregroupId>
      <artifactId>shardingsphere-jdbc-core-spring-boot-starterartifactId>
      <version>5.1.1version>
    dependency>

    <dependency>
      <groupId>mysqlgroupId>
      <artifactId>mysql-connector-javaartifactId>
      <scope>runtimescope>
    dependency>

    <dependency>
      <groupId>com.baomidougroupId>
      <artifactId>mybatis-plus-boot-starterartifactId>
      <version>3.5.1version>
    dependency>

    <dependency>
      <groupId>org.projectlombokgroupId>
      <artifactId>lombokartifactId>
      <optional>trueoptional>
    dependency>

    <dependency>
      <groupId>org.springframework.bootgroupId>
      <artifactId>spring-boot-starter-testartifactId>
      <scope>testscope>
      <exclusions>
        <exclusion>
          <groupId>org.junit.vintagegroupId>
          <artifactId>junit-vintage-engineartifactId>
        exclusion>
      exclusions>
    dependency>
  dependencies>

创建实体类

@TableName("t_user")
@Data
public class User {
    @TableId(type = IdType.AUTO)
    private Long id;
    private String uname;
}

创建Mapper

@Mapper
public interface UserMapper extends BaseMapper<User> {
}

1.2 配置文件

# 应用名称
spring.application.name=sharging-jdbc-demo
# 开发环境设置
spring.profiles.active=dev
# 内存模式,正式环境一般是集群模式
spring.shardingsphere.mode.type=Memory

# 配置真实数据源
spring.shardingsphere.datasource.names=master,slave1

# 配置第 1 个数据源
spring.shardingsphere.datasource.master.type=com.zaxxer.hikari.HikariDataSource
spring.shardingsphere.datasource.master.driver-class-name=com.mysql.jdbc.Driver
spring.shardingsphere.datasource.master.jdbc-url=jdbc:mysql://192.168.249.135:3306/db_user
spring.shardingsphere.datasource.master.username=root
spring.shardingsphere.datasource.master.password=123456

# 配置第 2 个数据源
spring.shardingsphere.datasource.slave1.type=com.zaxxer.hikari.HikariDataSource
spring.shardingsphere.datasource.slave1.driver-class-name=com.mysql.jdbc.Driver
spring.shardingsphere.datasource.slave1.jdbc-url=jdbc:mysql://192.168.249.135:3307/db_user
spring.shardingsphere.datasource.slave1.username=root
spring.shardingsphere.datasource.slave1.password=123456


# 读写分离类型,如: Static,Dynamic
spring.shardingsphere.rules.readwrite-splitting.data-sources.myds.type=Static
# 写数据源名称
spring.shardingsphere.rules.readwrite-splitting.data-sources.myds.props.write-data-source-name=master
# 读数据源名称,多个从数据源用逗号分隔
spring.shardingsphere.rules.readwrite-splitting.data-sources.myds.props.read-data-source-names=slave1

# 负载均衡算法名称
spring.shardingsphere.rules.readwrite-splitting.data-sources.myds.load-balancer-name=alg_round

# 负载均衡算法配置
# 负载均衡算法类型
spring.shardingsphere.rules.readwrite-splitting.load-balancers.alg_round.type=ROUND_ROBIN
spring.shardingsphere.rules.readwrite-splitting.load-balancers.alg_random.type=RANDOM
spring.shardingsphere.rules.readwrite-splitting.load-balancers.alg_weight.type=WEIGHT
spring.shardingsphere.rules.readwrite-splitting.load-balancers.alg_weight.props.slave1=1
#spring.shardingsphere.rules.readwrite-splitting.load-balancers.alg_weight.props.slave2=2

# 打印SQl
spring.shardingsphere.props.sql-show=true

1.3 测试

  • 读写分离测试

测试插入,通过日志可以看到Logic SQL逻辑SQl,因为应用程序将请求发送给ShardingSphere后并不关心是插入到哪台服务器,比如这里只是单纯把请求发送给myds的服务器组,由ShardingSphere的配置决定哪台是写服务器,最终进行SQL插入

@SpringBootTest
public class ReadwriteTest {

    @Autowired
    private UserMapper userMapper;

    /**
     * 写入数据的测试
     */
    @Test
    public void testInsert(){

        User user = new User();
        user.setUname("顺丰");
        userMapper.insert(user);
    }

}
  • 事务测试

为了保证主从库间的事务一致性,避免跨服务的分布式事务,ShardingSphere-JDBC的主从模型中,事务中的数据读写均用主库

  • 不添加@Transactional:insert对主库操作,select对从库操作

  • 添加@Transactional:则insert和select均对主库操作

  • **注意:**在JUnit环境下的@Transactional注解,默认情况下就会对事务进行回滚(即使在没加注解@Rollback,也会对事务回滚)

/**
 * 事务测试
 */
@Transactional//开启事务
@Test
public void testTrans(){

    User user = new User();
    user.setUname("圆通");
    userMapper.insert(user);

    List<User> users = userMapper.selectList(null);
}
  • 负载均衡测试
/**
 * 读数据测试
 */
@Test
public void testSelectAll(){
    List<User> users = userMapper.selectList(null);
    List<User> users = userMapper.selectList(null);//执行第二次测试负载均衡
    users.forEach(System.out::println);
}

2、ShardingSphere-JDBC垂直分片

https://shardingsphere.apache.org/document/5.1.1/cn/user-manual/shardingsphere-jdbc/spring-boot-starter/rules/sharding/

2.1 环境准备

创建server-user容器和server-order容器

docker run -d \
-p 3301:3306 \
-v /data/server/user/conf:/etc/mysql/conf.d \
-v /data/server/user/data:/var/lib/mysql \
-e MYSQL_ROOT_PASSWORD=123456 \
--name server-user \
mysql:8.0.29

docker run -d \
-p 3302:3306 \
-v /data/server/order/conf:/etc/mysql/conf.d \
-v /data/server/order/data:/var/lib/mysql \
-e MYSQL_ROOT_PASSWORD=123456 \
--name server-order \
mysql:8.0.29

登录MySQL服务器修改一下密码

#进入容器:
docker exec -it server-user env LANG=C.UTF-8 /bin/bash
#进入容器内的mysql命令行
mysql -uroot -p
#修改默认密码插件
ALTER USER 'root'@'%' IDENTIFIED WITH mysql_native_password BY '123456';

在两个库分布创建数据表

-- 这是server-user
CREATE DATABASE db_user;
USE db_user;
CREATE TABLE t_user (
 id BIGINT AUTO_INCREMENT,
 uname VARCHAR(30),
 PRIMARY KEY (id)
);

-- 这是server-order
CREATE DATABASE db_order;
USE db_order;
CREATE TABLE t_order (
  id BIGINT AUTO_INCREMENT,
  order_no VARCHAR(30),
  user_id BIGINT,
  amount DECIMAL(10,2),
  PRIMARY KEY(id) 
);

2.2 程序实现

创建实体类

@TableName("t_order")
@Data
public class Order {
    @TableId(type = IdType.AUTO)
    private Long id;
    private String orderNo;
    private Long userId;
    private BigDecimal amount;
}

创建Mapper

@Mapper
public interface OrderMapper extends BaseMapper<Order> {
}

配置垂直分片

# 应用名称
spring.application.name=sharding-jdbc-demo
# 环境设置
spring.profiles.active=dev

# 配置真实数据源
spring.shardingsphere.datasource.names=server-user,server-order

# 配置第 1 个数据源
spring.shardingsphere.datasource.server-user.type=com.zaxxer.hikari.HikariDataSource
spring.shardingsphere.datasource.server-user.driver-class-name=com.mysql.jdbc.Driver
spring.shardingsphere.datasource.server-user.jdbc-url=jdbc:mysql://192.168.249.135:3301/db_user
spring.shardingsphere.datasource.server-user.username=root
spring.shardingsphere.datasource.server-user.password=123456

# 配置第 2 个数据源
spring.shardingsphere.datasource.server-order.type=com.zaxxer.hikari.HikariDataSource
spring.shardingsphere.datasource.server-order.driver-class-name=com.mysql.jdbc.Driver
spring.shardingsphere.datasource.server-order.jdbc-url=jdbc:mysql://192.168.249.135:3302/db_order
spring.shardingsphere.datasource.server-order.username=root
spring.shardingsphere.datasource.server-order.password=123456

# 标准分片表配置(数据节点)
# spring.shardingsphere.rules.sharding.tables..actual-data-nodes=值
# 值由数据源名 + 表名组成,以小数点分隔。
# :逻辑表名
spring.shardingsphere.rules.sharding.tables.t_user.actual-data-nodes=server-user.t_user
spring.shardingsphere.rules.sharding.tables.t_order.actual-data-nodes=server-order.t_order


# 打印SQL
spring.shardingsphere.props.sql-show=true


2.3 测试垂直分片

测试通过日志可以发现程序写的是逻辑表名,会通过Sharding配置文件自动路由到对应的数据源下的数据表

@SpringBootTest
public class ShardingTest {

    @Autowired
    private UserMapper userMapper;

    @Autowired
    private OrderMapper orderMapper;

    /**
     * 垂直分片:插入数据测试
     */
    @Test
    public void testInsertOrderAndUser(){

        User user = new User();
        user.setUname("强哥");
        userMapper.insert(user);

        Order order = new Order();
        order.setOrderNo("SHAWN001");
        order.setUserId(user.getId());
        order.setAmount(new BigDecimal(100));
        orderMapper.insert(order);

    }

    /**
     * 垂直分片:查询数据测试
     */
    @Test
    public void testSelectFromOrderAndUser(){
        User user = userMapper.selectById(1L);
        Order order = orderMapper.selectById(1L);
    }
}

如果运行报错Public Key Retrieval is not allowed,是因为ShardingSphere-JDBC远程连接的方式默认的密码加密规则是:mysql_native_password。因此需要在服务器端修改服务器的密码加密规则,如下:

ALTER USER 'root'@'%' IDENTIFIED WITH mysql_native_password BY '123456';

四、ShardingSphere-JDBC水平分片(常用)

1、环境准备

创建两个Mysql容器

docker run -d \
-p 3310:3306 \
-v /data/server/order0/conf:/etc/mysql/conf.d \
-v /data/server/order0/data:/var/lib/mysql \
-e MYSQL_ROOT_PASSWORD=123456 \
--name server-order0 \
mysql:8.0.29


docker run -d \
-p 3311:3306 \
-v /data/server/order1/conf:/etc/mysql/conf.d \
-v /data/server/order1/data:/var/lib/mysql \
-e MYSQL_ROOT_PASSWORD=123456 \
--name server-order1 \
mysql:8.0.29

登录MySQL服务器操作

#进入容器:
docker exec -it server-order1 env LANG=C.UTF-8 /bin/bash
#进入容器内的mysql命令行
mysql -uroot -p
#修改默认密码插件
ALTER USER 'root'@'%' IDENTIFIED WITH mysql_native_password BY '123456';

创建数据库:和server-order1相同注意:水平分片的id需要在业务层实现,不能依赖数据库的主键自增

CREATE DATABASE db_order;
USE db_order;
CREATE TABLE t_order0 (
  id BIGINT,
  order_no VARCHAR(30),
  user_id BIGINT,
  amount DECIMAL(10,2),
  PRIMARY KEY(id) 
);
CREATE TABLE t_order1 (
  id BIGINT,
  order_no VARCHAR(30),
  user_id BIGINT,
  amount DECIMAL(10,2),
  PRIMARY KEY(id) 
);

2、基本水平分片

2.1 基本配置

#========================基本配置
# 应用名称
spring.application.name=sharging-jdbc-demo
# 开发环境设置
spring.profiles.active=dev
# 内存模式
spring.shardingsphere.mode.type=Memory
# 打印SQl
spring.shardingsphere.props.sql-show=true

2.2 数据源配置

#========================数据源配置
# 配置真实数据源
spring.shardingsphere.datasource.names=server-user,server-order0,server-order1

# 配置第 1 个数据源
spring.shardingsphere.datasource.server-user.type=com.zaxxer.hikari.HikariDataSource
spring.shardingsphere.datasource.server-user.driver-class-name=com.mysql.jdbc.Driver
spring.shardingsphere.datasource.server-user.jdbc-url=jdbc:mysql://192.168.249.135:3301/db_user
spring.shardingsphere.datasource.server-user.username=root
spring.shardingsphere.datasource.server-user.password=123456

# 配置第 2 个数据源
spring.shardingsphere.datasource.server-order0.type=com.zaxxer.hikari.HikariDataSource
spring.shardingsphere.datasource.server-order0.driver-class-name=com.mysql.jdbc.Driver
spring.shardingsphere.datasource.server-order0.jdbc-url=jdbc:mysql://192.168.249.135:3310/db_order
spring.shardingsphere.datasource.server-order0.username=root
spring.shardingsphere.datasource.server-order0.password=123456

# 配置第 3 个数据源
spring.shardingsphere.datasource.server-order1.type=com.zaxxer.hikari.HikariDataSource
spring.shardingsphere.datasource.server-order1.driver-class-name=com.mysql.jdbc.Driver
spring.shardingsphere.datasource.server-order1.jdbc-url=jdbc:mysql://192.168.249.135:3311/db_order
spring.shardingsphere.datasource.server-order1.username=root
spring.shardingsphere.datasource.server-order1.password=123456

2.3 标椎分片表配置

#========================标准分片表配置(数据节点配置)
# spring.shardingsphere.rules.sharding.tables..actual-data-nodes=值
# 值由数据源名 + 表名组成,以小数点分隔。多个表以逗号分隔,支持 inline 表达式。
# :逻辑表名
spring.shardingsphere.rules.sharding.tables.t_user.actual-data-nodes=server-user.t_user
spring.shardingsphere.rules.sharding.tables.t_order.actual-data-nodes=server-order0.t_order0,server-order0.t_order1,server-order1.t_order0,server-order1.t_order1

修改Order实体类的主键策略:

//@TableId(type = IdType.AUTO)//依赖数据库的主键自增策略
@TableId(type = IdType.ASSIGN_ID)//分布式id

测试:保留上面配置中的一个分片表节点分别进行测试,检查每个分片节点是否可用

/**
 * 水平分片:插入数据测试
 */
@Test
public void testInsertOrder(){

    Order order = new Order();
    order.setOrderNo("SHAWN001");
    order.setUserId(1L);
    order.setAmount(new BigDecimal(100));
    orderMapper.insert(order);
}

2.4 行表达式

https://shardingsphere.apache.org/document/5.1.1/cn/features/sharding/concept/inline-expression/

#========================标准分片表配置(数据节点配置)
# spring.shardingsphere.rules.sharding.tables..actual-data-nodes=值
# 值由数据源名 + 表名组成,以小数点分隔。多个表以逗号分隔,支持 inline 表达式。
# :逻辑表名
spring.shardingsphere.rules.sharding.tables.t_user.actual-data-nodes=server-user.t_user
spring.shardingsphere.rules.sharding.tables.t_order.actual-data-nodes=server-order$->{0..1}.t_order$->{0..1}

2.5 分片算法配置

https://shardingsphere.apache.org/document/5.1.1/cn/user-manual/shardingsphere-jdbc/builtin-algorithm/sharding/

  • 水平分库

分片规则:order表中user_id为偶数时,数据插入server-order0服务器user_id为奇数时,数据插入server-order1服务器。这样分片的好处是,同一个用户的订单数据,一定会被插入到同一台服务器上,查询一个用户的订单时效率较高。

#------------------------分库策略
# 分片列名称
spring.shardingsphere.rules.sharding.tables.t_order.database-strategy.standard.sharding-column=user_id
# 分片算法名称
spring.shardingsphere.rules.sharding.tables.t_order.database-strategy.standard.sharding-algorithm-name=alg_inline_userid

#------------------------分片算法配置
# 行表达式分片算法
# 分片算法类型
spring.shardingsphere.rules.sharding.sharding-algorithms.alg_inline_userid.type=INLINE
# 分片算法属性配置
spring.shardingsphere.rules.sharding.sharding-algorithms.alg_inline_userid.props.algorithm-expression=server-order$->{user_id % 2}

# 取模分片算法
# 分片算法类型
spring.shardingsphere.rules.sharding.sharding-algorithms.alg_mod.type=MOD
# 分片算法属性配置
spring.shardingsphere.rules.sharding.sharding-algorithms.alg_mod.props.sharding-count=2

因为只是暂时设置了分库,但未分表,先设置只在 t_order0表上进行测试

xxx.actual-data-nodes=server-order$->{0..1}.t_order0

测试:可以分别测试行表达式分片算法和取模分片算法

/**
 * 水平分片:分库插入数据测试
 */
@Test
public void testInsertOrderDatabaseStrategy(){

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

}

水平分表

分片规则:order表中order_no的哈希值为偶数时,数据插入对应服务器的t_order0表order_no的哈希值为奇数时,数据插入对应服务器的t_order1表。因为order_no是字符串形式,因此不能直接取模。

#------------------------分表策略
# 分片列名称
spring.shardingsphere.rules.sharding.tables.t_order.table-strategy.standard.sharding-column=order_no
# 分片算法名称
spring.shardingsphere.rules.sharding.tables.t_order.table-strategy.standard.sharding-algorithm-name=alg_hash_mod


#------------------------分片算法配置
# 哈希取模分片算法
# 分片算法类型
spring.shardingsphere.rules.sharding.sharding-algorithms.alg_hash_mod.type=HASH_MOD
# 分片算法属性配置
spring.shardingsphere.rules.sharding.sharding-algorithms.alg_hash_mod.props.sharding-count=2

测试前不要忘记将如下节点改回原来的状态

xxx.actual-data-nodes=server-order$->{0..1}.t_order$->{0..1}

测试

/**
     * 水平分片:分表插入数据测试
     */
@Test
public void testInsertOrderTableStrategy(){

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

        Order order = new Order();
        order.setOrderNo("ATGUIGU" + i);
        order.setUserId(1L);
        order.setAmount(new BigDecimal(100));
        orderMapper.insert(order);
    }

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

        Order order = new Order();
        order.setOrderNo("ATGUIGU" + i);
        order.setUserId(2L);
        order.setAmount(new BigDecimal(100));
        orderMapper.insert(order);
    }
}

/**
     * 测试哈希取模
     */
@Test
public void testHash(){

    //注意hash取模的结果是整个字符串hash后再取模,和数值后缀是奇数还是偶数无关
    System.out.println("ATGUIGU001".hashCode() % 2);
    System.out.println("ATGUIGU0011".hashCode() % 2);
}

查询测试

/**
 * 水平分片:查询所有记录
 * 查询了两个数据源,每个数据源中使用UNION ALL连接两个表
 */
@Test
public void testShardingSelectAll(){

    List<Order> orders = orderMapper.selectList(null);
    orders.forEach(System.out::println);
}

/**
 * 水平分片:根据user_id查询记录
 * 查询了一个数据源,每个数据源中使用UNION ALL连接两个表
 */
@Test
public void testShardingSelectByUserId(){

    QueryWrapper<Order> orderQueryWrapper = new QueryWrapper<>();
    orderQueryWrapper.eq("user_id", 1L);
    List<Order> orders = orderMapper.selectList(orderQueryWrapper);
    orders.forEach(System.out::println);
}

2.6 分布式序列算法

https://shardingsphere.apache.org/document/5.1.1/cn/features/sharding/concept/key-generator/

水平分片需要关注全局序列,因为不能简单的使用基于数据库的主键自增。这里有两种方案:一种是基于MyBatisPlus的id策略;一种是ShardingSphere-JDBC的全局序列配置

基于MyBatisPlus的id策略,将Order类的id设置成如下形式

@TableId(type = IdType.ASSIGN_ID)
private Long id;

基于ShardingSphere-JDBC的全局序列配置,和前面的MyBatisPlus的策略二选一

#------------------------分布式序列策略配置
# 分布式序列列名称
spring.shardingsphere.rules.sharding.tables.t_order.key-generate-strategy.column=id
# 分布式序列算法名称
spring.shardingsphere.rules.sharding.tables.t_order.key-generate-strategy.key-generator-name=alg_snowflake

# 分布式序列算法配置
# 分布式序列算法类型
spring.shardingsphere.rules.sharding.key-generators.alg_snowflake.type=SNOWFLAKE
# 分布式序列算法属性配置
#spring.shardingsphere.rules.sharding.key-generators.alg_snowflake.props.xxx=

此时,需要将实体类中的id策略修改成以下形式:

//当配置了shardingsphere-jdbc的分布式序列时,自动使用shardingsphere-jdbc的分布式序列
//当没有配置shardingsphere-jdbc的分布式序列时,自动依赖数据库的主键自增策略
@TableId(type = IdType.AUTO)

3、多表关联

创建关联表,在server-order0、server-order1服务器中分别创建两张订单详情表t_order_item0、t_order_item1。我们希望同一个用户的订单表和订单详情表中的数据都在同一个数据源中,避免跨库关联,因此这两张表我们使用相同的分片策略。那么在t_order_item中我们也需要创建order_nouser_id这两个分片键

CREATE TABLE t_order_item0(
    id BIGINT,
    order_no VARCHAR(30),
    user_id BIGINT,
    price DECIMAL(10,2),
    `count` INT,
    PRIMARY KEY(id)
);

CREATE TABLE t_order_item1(
    id BIGINT,
    order_no VARCHAR(30),
    user_id BIGINT,
    price DECIMAL(10,2),
    `count` INT,
    PRIMARY KEY(id)
);

创建实体类

@TableName("t_order_item")
@Data
public class OrderItem {
    //当配置了shardingsphere-jdbc的分布式序列时,自动使用shardingsphere-jdbc的分布式序列
    @TableId(type = IdType.AUTO)
    private Long id;
    private String orderNo;
    private Long userId;
    private BigDecimal price;
    private Integer count;
}

创建Mapper

@Mapper
public interface OrderItemMapper extends BaseMapper<OrderItem> {

}

配置关联表

#------------------------标准分片表配置(数据节点配置)
spring.shardingsphere.rules.sharding.tables.t_order_item.actual-data-nodes=server-order$->{0..1}.t_order_item$->{0..1}

#------------------------分库策略
# 分片列名称
spring.shardingsphere.rules.sharding.tables.t_order_item.database-strategy.standard.sharding-column=user_id
# 分片算法名称
spring.shardingsphere.rules.sharding.tables.t_order_item.database-strategy.standard.sharding-algorithm-name=alg_mod

#------------------------分表策略
# 分片列名称
spring.shardingsphere.rules.sharding.tables.t_order_item.table-strategy.standard.sharding-column=order_no
# 分片算法名称
spring.shardingsphere.rules.sharding.tables.t_order_item.table-strategy.standard.sharding-algorithm-name=alg_hash_mod

#------------------------分布式序列策略配置
# 分布式序列列名称
spring.shardingsphere.rules.sharding.tables.t_order_item.key-generate-strategy.column=id
# 分布式序列算法名称
spring.shardingsphere.rules.sharding.tables.t_order_item.key-generate-strategy.key-generator-name=alg_snowflake

测试插入数据

/**
 * 测试关联表插入
 */
@Test
public void testInsertOrderAndOrderItem(){

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

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

        for (long j = 1; j < 3; j++) {
            OrderItem orderItem = new OrderItem();
            orderItem.setOrderNo("ATGUIGU" + 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("ATGUIGU" + i);
        order.setUserId(2L);
        orderMapper.insert(order);

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

}

4、绑定表

创建VO对象

@Data
public class OrderVo {
    private String orderNo;
    private BigDecimal amount;
}

添加Mapper方法

@Mapper
public interface OrderMapper extends BaseMapper<Order> {

    @Select({"SELECT o.order_no, SUM(i.price * i.count) AS amount",
            "FROM t_order o JOIN t_order_item i ON o.order_no = i.order_no",
            "GROUP BY o.order_no"})
    List<OrderVo> getOrderAmount();

}

测试关联查询

/**
 * 测试关联表查询
*/
@Test
public void testGetOrderAmount(){

    List<OrderVo> orderAmountList = orderMapper.getOrderAmount();
    orderAmountList.forEach(System.out::println);
}

配置绑定表,在原来水平分片配置的基础上添加如下配置

#------------------------绑定表
spring.shardingsphere.rules.sharding.binding-tables[0]=t_order,t_order_item

配置完绑定表后再次进行关联查询的测试:

  • **如果不配置绑定表:测试的结果为8个SQL。**多表关联查询会出现笛卡尔积关联。

  • 如果配置绑定表:测试的结果为4个SQL。 多表关联查询不会出现笛卡尔积关联,关联查询效率将大大提升。

绑定表:指分片规则一致的一组分片表。 使用绑定表进行多表关联查询时,必须使用分片键进行关联,否则会出现笛卡尔积关联或跨库关联,从而影响查询效率。

5、广播表

https://shardingsphere.apache.org/document/5.1.1/cn/user-manual/shardingsphere-jdbc/spring-boot-starter/rules/sharding/

5.1 广播表介绍

指所有的分片数据源中都存在的表,表结构及其数据在每个数据库中均完全一致。 适用于数据量不大且需要与海量数据的表进行关联查询的场景,例如:字典表。

广播具有以下特性:

  • 插入、更新操作会实时在所有节点上执行,保持各个分片的数据一致性

  • 查询操作,只从一个节点获取

  • 可以跟任何一个表进行 JOIN 操作

5.2 创建广播表

在server-order0、server-order1和server-user服务器中分别创建t_dict表

CREATE TABLE t_dict(
    id BIGINT,
    dict_type VARCHAR(200),
    PRIMARY KEY(id)
);

5.3 程序实现

创建实体类

@TableName("t_dict")
@Data
public class Dict {
    //可以使用MyBatisPlus的雪花算法
    @TableId(type = IdType.ASSIGN_ID)
    private Long id;
    private String dictType;
}

创建Mapper

@Mapper
public interface DictMapper extends BaseMapper<Dict> {
}

配置广播表

#数据节点可不配置,默认情况下,向所有数据源广播
spring.shardingsphere.rules.sharding.tables.t_dict.actual-data-nodes=server-user.t_dict,server-order$->{0..1}.t_dict

# 广播表
spring.shardingsphere.rules.sharding.broadcast-tables[0]=t_dict

5.4 测试

@Autowired
private DictMapper dictMapper;

/**
 * 广播表:每个服务器中的t_dict同时添加了新数据
 */
@Test
public void testBroadcast(){

    Dict dict = new Dict();
    dict.setDictType("type1");
    dictMapper.insert(dict);
}

/**
 * 查询操作,只从一个节点获取数据
 * 随机负载均衡规则
 */
@Test
public void testSelectBroadcast(){

    List<Dict> dicts = dictMapper.selectList(null);
    dicts.forEach(System.out::println);
}

五、ShardingSphere-Proxy

1、环境搭建与安装

https://shardingsphere.apache.org/document/5.1.1/cn/quick-start/shardingsphere-proxy-quick-start/

1.1 获取包

目前 ShardingSphere-Proxy 提供了 3 种获取方式:

  • 二进制发布包

  • Docker

  • Helm

1.2 使用二进制发布包安装

二进制包既可以Linux系统运行,又可以在windows系统运行

step1:解压二进制包

apache-shardingsphere-5.1.1-shardingsphere-proxy-bin.tar.gz

windows:使用解压软件解压文件

Linux:将文件上传至/opt目录,并解压

tar -zxvf apache-shardingsphere-5.1.1-shardingsphere-proxy-bin.tar.gz

step2:MySQL驱动

mysql-connector-java-8.0.22.jar,将MySQl驱动放至解压目录中的ext-lib目录

spte3:修改配置conf/server.yaml

rules:
  - !AUTHORITY
    users:
      - root@%:root
    provider:
      type: ALL_PRIVILEGES_PERMITTED

props:
  sql-show: true

spte4:启动ShardingSphere-Proxy

Linux 操作系统请运行 bin/start.sh;Windows 操作系统请运行 bin/start.bat;指定端口号和配置文件目录:bin/start.bat ${proxy_port} ${proxy_conf_directory}

step5:远程连接ShardingSphere-Proxy

mysql -h192.168.100.1 -P3307 -uroot -p

step6:访问测试

show databases;

1.3 使用Docker安装

step1:启动Docker容器,若容器内存太小,创建容器的时候使用JVM参数限制

docker run -d \
-v /data/server/proxy-a/conf:/opt/shardingsphere-proxy/conf \
-v /data/server/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

step2:上传MySQL驱动

将MySQL驱动上传至/data/server/proxy-a/ext-lib目录

spte3:修改配置server.yaml

rules:
  - !AUTHORITY
    users:
      - root@%:root
    provider:
      type: ALL_PRIVILEGES_PERMITTED

props:
  sql-show: true

将配置文件上传至/data/server/proxy-a/conf目录

spte4:重启容器

docker restart server-proxy-a

step5:远程连接ShardingSphere-Proxy

ShardingSphere-Proxy容器中默认情况下没有mysql命令行客户端的安装,因此需要远程访问

mysql -h192.168.249.135 -P3321 -uroot -p

step6:访问测试

show databases;

2、ShardingSphere-Proxy读写分离

2.1 修改配置文件

修改配置config-readwrite-splitting.yaml,ShardingSphere-JDBC的配置是ShardingSphere-Proxy的子集,因此可以复用

# 逻辑名字
schemaName: readwrite_splitting_db

dataSources:
  write_ds:
    url: jdbc:mysql://192.168.249.135: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://192.168.249.135: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://192.168.249.135: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

将配置文件上传至/data/server/proxy-a/conf目录,最后重启容器

2.2 实时查看日志

可以通过这种方式查看服务器中输出的SQL语句

docker exec -it server-proxy-a env LANG=C.UTF-8 /bin/bash
tail -f /opt/shardingsphere-proxy/logs/stdout.log 

2.3 远程访问测试

mysql> show databases;
mysql> use readwrite_splitting_db;
mysql> show tables;
mysql> select * from t_user;
mysql> select * from t_user;
mysql> insert into t_user(uname) values('wang5');

2.4 应用程序访问Proxy

添加依赖

<dependencies>
    <dependency>
        <groupId>org.springframework.bootgroupId>
        <artifactId>spring-boot-starter-webartifactId>
    dependency>

    <dependency>
        <groupId>mysqlgroupId>
        <artifactId>mysql-connector-javaartifactId>
        <scope>runtimescope>
    dependency>

    <dependency>
        <groupId>com.baomidougroupId>
        <artifactId>mybatis-plus-boot-starterartifactId>
        <version>3.5.1version>
    dependency>

    <dependency>
        <groupId>org.projectlombokgroupId>
        <artifactId>lombokartifactId>
        <optional>trueoptional>
    dependency>

    <dependency>
        <groupId>org.springframework.bootgroupId>
        <artifactId>spring-boot-starter-testartifactId>
        <scope>testscope>
        <exclusions>
            <exclusion>
                <groupId>org.junit.vintagegroupId>
                <artifactId>junit-vintage-engineartifactId>
            exclusion>
        exclusions>
    dependency>
dependencies>

创建实体类

@TableName("t_user")
@Data
public class User {
    @TableId(type = IdType.AUTO)
    private Long id;
    private String uname;
}

创建Mapper

@Mapper
public interface UserMapper extends BaseMapper<User> {
}

配置数据源

# 应用名称
spring.application.name=sharding-proxy-demo
# 开发环境设置
spring.profiles.active=dev

#mysql数据库连接(proxy)
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.url=jdbc:mysql://192.168.249.135:3321/readwrite_splitting_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

测试

@SpringBootTest
class ShardingProxyDemoApplicationTests {

    @Autowired
    private UserMapper userMapper;

    /**
     * 读数据测试
     */
    @Test
    public void testSelectAll(){
        List<User> users = userMapper.selectList(null);
        users.forEach(System.out::println);
    }
}

3、ShardingSphere-Proxy垂直分片

修改配置config-sharding.yaml

schemaName: sharding_db

dataSources:
  ds_0:
    url: jdbc:mysql://192.168.249.135:3301/db_user?serverTimezone=UTC&useSSL=false
    username: root
    password: 123456
    connectionTimeoutMilliseconds: 30000
    idleTimeoutMilliseconds: 60000
    maxLifetimeMilliseconds: 1800000
    maxPoolSize: 50
    minPoolSize: 1
  ds_1:
    url: jdbc:mysql://192.168.249.135:3302/db_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

实时查看日志,可以通过这种方式查看服务器中输出的SQL语句

docker exec -it server-proxy-a env LANG=C.UTF-8 /bin/bash
tail -f /opt/shardingsphere-proxy/logs/stdout.log 

远程访问测试

mysql> show databases;
mysql> use sharding_db;
mysql> show tables;
mysql> select * from t_order;
mysql> select * from t_user;

4、ShardingSphere-Proxy水平分片

修改配置config-sharding.yaml

schemaName: sharding_db

dataSources:
  ds_user:
    url: jdbc:mysql://192.168.249.135:3301/db_user?serverTimezone=UTC&useSSL=false
    username: root
    password: 123456
    connectionTimeoutMilliseconds: 30000
    idleTimeoutMilliseconds: 60000
    maxLifetimeMilliseconds: 1800000
    maxPoolSize: 50
    minPoolSize: 1
  ds_order0:
    url: jdbc:mysql://192.168.249.135:3310/db_order?serverTimezone=UTC&useSSL=false
    username: root
    password: 123456
    connectionTimeoutMilliseconds: 30000
    idleTimeoutMilliseconds: 60000
    maxLifetimeMilliseconds: 1800000
    maxPoolSize: 50
    minPoolSize: 1
  ds_order1:
    url: jdbc:mysql://192.168.249.135:3311/db_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_user.t_user

    t_order:
      actualDataNodes: ds_order${0..1}.t_order${0..1}
      databaseStrategy:
        standard:
          shardingColumn: user_id
          shardingAlgorithmName: alg_mod
      tableStrategy:
        standard:
          shardingColumn: order_no
          shardingAlgorithmName: alg_hash_mod
      keyGenerateStrategy:
        column: id
        keyGeneratorName: snowflake
    t_order_item:
      actualDataNodes: ds_order${0..1}.t_order_item${0..1}
      databaseStrategy:
        standard:
          shardingColumn: user_id
          shardingAlgorithmName: alg_mod
      tableStrategy:
        standard:
          shardingColumn: order_no
          shardingAlgorithmName: alg_hash_mod
      keyGenerateStrategy:
        column: id
        keyGeneratorName: snowflake

  bindingTables:
    - t_order,t_order_item


  broadcastTables:
    - t_dict

  shardingAlgorithms:
    alg_inline_userid:
      type: INLINE
      props:
        algorithm-expression: server-order$->{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

实时查看日志,可以通过这种方式查看服务器中输出的SQL语句

docker exec -it server-proxy-a env LANG=C.UTF-8 /bin/bash
tail -f /opt/shardingsphere-proxy/logs/stdout.log 

远程访问测试

mysql> show databases;
mysql> use sharding_db;
mysql> show tables;
mysql> select * from t_order; --测试水平分片
mysql> select * from t_dict; --测试广播表

参考

https://shardingsphere.apache.org/document/5.1.1/cn/overview/

https://www.bilibili.com/video/BV1ta411g7Jf/

你可能感兴趣的:(#,中间件,1024程序员节,Sharding,Sphere)