一、ShardingSphere简介
Apache ShardingSphere 是一套开源的分布式数据库解决方案组成的生态圈,它由 JDBC、Proxy 和 Sidecar(规划中)这 3 款既能够独立部署,又支持混合部署配合使用的产品组成。 它们均提供标准化的数据水平扩展、分布式事务和分布式治理等功能,可适用于如 Java 同构、异构语言、云原生等各种多样化的应用场景。
以下是Sharding Sphere在Github上的star数:
Apache ShardingSphere 旨在充分合理地在分布式的场景下利用关系型数据库的计算和存储能力,而并非实现一个全新的关系型数据库。 关系型数据库当今依然占有巨大市场份额,是企业核心系统的基石,未来也难于撼动,我们更加注重在原有基础上提供增量,而非颠覆。
Apache ShardingSphere 5.x 版本开始致力于可插拔架构,项目的功能组件能够灵活的以可插拔的方式进行扩展。 目前,数据分片、读写分离、数据加密、影子库压测等功能,以及 MySQL、PostgreSQL、SQLServer、Oracle 等 SQL 与协议的支持,均通过插件的方式织入项目。 开发者能够像使用积木一样定制属于自己的独特系统。Apache ShardingSphere 目前已提供数十个 SPI 作为系统的扩展点,仍在不断增加中。
Sharding-JDBC
定位为轻量级 Java 框架,在 Java 的 JDBC 层提供的额外服务。 它使用客户端直连数据库,以 jar 包形式提供服务,无需额外部署和依赖,可理解为增强版的 JDBC 驱动,完全兼容 JDBC 和各种 ORM 框架。
适用于任何基于 JDBC 的 ORM 框架,如:JPA, Hibernate, Mybatis, Spring JDBC Template 或直接使用 JDBC。
支持任何第三方的数据库连接池,如:DBCP, C3P0, BoneCP, Druid, HikariCP 等。
支持任意实现 JDBC 规范的数据库,目前支持 MySQL,Oracle,SQLServer,PostgreSQL 以及任何遵循 SQL92 标准的数据库。
Sharding-Proxy
定位为透明化的数据库代理端,提供封装了数据库二进制协议的服务端版本,用于完成对异构语言的支持。 目前提供 MySQL 和 PostgreSQL 版本,它可以使用任何兼容 MySQL/PostgreSQL 协议的访问客户端(如:MySQL Command Client, MySQL Workbench, Navicat 等)操作数据,对 DBA 更加友好。
向应用程序完全透明,可直接当做 MySQL/PostgreSQL 使用。
适用于任何兼容 MySQL/PostgreSQL 协议的的客户端。
二、ShardingJDBC实战
2.1 基础环境搭建
springboot整合mybatis-plus为例,添加依赖
<!--mybatis-plus依赖-->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.4.2</version>
</dependency>
<!--sharding-jdbc依赖-->
<dependency>
<groupId>org.apache.shardingsphere</groupId>
<artifactId>sharding-jdbc-spring-boot-starter</artifactId>
<version>4.1.1</version>
</dependency>
<!--分布式事务XA支持-->
<dependency>
<groupId>org.apache.shardingsphere</groupId>
<artifactId>sharding-transaction-xa-core</artifactId>
<version>4.1.1</version>
</dependency>
其中,sharding-transaction-xa-core
是分布式事务的支持,如果不需要使用XA做分布式事务,则可以不用添加。
数据库
2.1.1 数据源配置
#打印sql
spring.shardingsphere.props.sql.show=true
#datasource
spring.shardingsphere.datasource.names=ds0,ds1
spring.shardingsphere.datasource.ds0.type=com.zaxxer.hikari.HikariDataSource
spring.shardingsphere.datasource.ds0.driver-class-name=com.mysql.jdbc.Driver
spring.shardingsphere.datasource.ds0.jdbc-url=jdbc:mysql://localhost:3306/heima1?useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTC
spring.shardingsphere.datasource.ds0.username=root
spring.shardingsphere.datasource.ds0.password=123456
spring.shardingsphere.datasource.ds1.type=com.zaxxer.hikari.HikariDataSource
spring.shardingsphere.datasource.ds1.driver-class-name=com.mysql.jdbc.Driver
spring.shardingsphere.datasource.ds1.jdbc-url=jdbc:mysql://localhost:3306/heima2?useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTC
spring.shardingsphere.datasource.ds1.username=root
spring.shardingsphere.datasource.ds1.password=123456
spring.shardingsphere.datasource.names
指定当前的数据源名称,比如这里的ds0
、ds1
就是配置了两个数据源。然后接下来分别对两个数据源配置如下几个内容
-
type
数据库连接池 -
driver-class-name
数据库驱动 -
jdbc-url
数据库连接地址 -
username
账号名称 -
password
密码
2.1.2 分库配置
#sharding-database
spring.shardingsphere.sharding.tables.position.database-strategy.inline.sharding-column=id
spring.shardingsphere.sharding.tables.position.database-strategy.inline.algorithm-expression=ds$->{id % 2}
这里提到一个关键词inline
行表达式,行表达式的使用非常直观,只需要在配置中使用 ${ expression }
或$->{ expression }
标识行表达式即可。 行表达式的内容使用的是 Groovy 的语法,Groovy 能够支持的所有操作,行表达式均能够支持。例如:
-
${begin..end}
表示范围区间 -
${[unit1, unit2, unit_x]}
表示枚举值
所以上述sharding-column=id
指的是指定当前分库时需要依赖的分片键是id
,algorithm-expression=ds$->{id % 2}
指的是分片算法是将id取模之后,和ds进行拼接:
比如查询的id如果是1000,1000在对2取模之后会变成ds0
,然后sql语句就会去ds0
这个数据源中执行。
2.1.3 实体类定义
package com.brianxia.shardingjdbcmybatis.entity;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import javax.persistence.*;
import java.io.Serializable;
@TableName("position")
@Data
public class Position implements Serializable {
@TableId(value = "id",type = IdType.INPUT)
private Long id;
private String name;
private String salary;
private String city;
}
2.1.4 mapper接口添加
package com.brianxia.shardingjdbcmybatis.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.brianxia.shardingjdbcmybatis.entity.BOrder;
import com.brianxia.shardingjdbcmybatis.entity.Position;
import org.apache.ibatis.annotations.Mapper;
/**
* @author brianxia
* @version 1.0
* @date 2021/5/25 15:41
*/
@Mapper
public interface PositionMapper extends BaseMapper<Position> {
}
三、 测试
package com.brianxia.shardingjdbcmybatis;
import com.brianxia.shardingjdbcmybatis.entity.Position;
import com.brianxia.shardingjdbcmybatis.mapper.PositionMapper;
import org.junit.jupiter.api.RepeatedTest;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
/**
* @author brianxia
* @version 1.0
* @date 2021/5/27 18:45
*/
@SpringBootTest
public class BaseTest {
@Autowired
private PositionMapper positionMapper;
@Test
public void add(){
for (long i = 0; i < 100; i++) {
Position position = new Position();
position.setId(i);
positionMapper.insert(position);
}
}
}
首先使用自定义的id进行数据库的插入,执行日志:
2021-05-27 18:49:42.394 INFO 21560 --- [ main] ShardingSphere-SQL : Logic SQL: INSERT INTO position ( id ) VALUES ( ? )
2021-05-27 18:49:42.394 INFO 21560 --- [ main] ShardingSphere-SQL : SQLStatement: InsertStatementContext(super=CommonSQLStatementContext(sqlStatement=org.apache.shardingsphere.sql.parser.sql.statement.dml.InsertStatement@20c3b34b, tablesContext=org.apache.shardingsphere.sql.parser.binder.segment.table.TablesContext@ff21443), tablesContext=org.apache.shardingsphere.sql.parser.binder.segment.table.TablesContext@ff21443, columnNames=[id], insertValueContexts=[InsertValueContext(parametersCount=1, valueExpressions=[ParameterMarkerExpressionSegment(startIndex=40, stopIndex=40, parameterMarkerIndex=0)], parameters=[97])], generatedKeyContext=Optional[GeneratedKeyContext(columnName=id, generated=false, generatedValues=[97])])
2021-05-27 18:49:42.394 INFO 21560 --- [ main] ShardingSphere-SQL : Actual SQL: ds1 ::: INSERT INTO position ( id ) VALUES (?) ::: [97]
2021-05-27 18:49:42.398 INFO 21560 --- [ main] ShardingSphere-SQL : Logic SQL: INSERT INTO position ( id ) VALUES ( ? )
2021-05-27 18:49:42.398 INFO 21560 --- [ main] ShardingSphere-SQL : SQLStatement: InsertStatementContext(super=CommonSQLStatementContext(sqlStatement=org.apache.shardingsphere.sql.parser.sql.statement.dml.InsertStatement@20c3b34b, tablesContext=org.apache.shardingsphere.sql.parser.binder.segment.table.TablesContext@6253e59a), tablesContext=org.apache.shardingsphere.sql.parser.binder.segment.table.TablesContext@6253e59a, columnNames=[id], insertValueContexts=[InsertValueContext(parametersCount=1, valueExpressions=[ParameterMarkerExpressionSegment(startIndex=40, stopIndex=40, parameterMarkerIndex=0)], parameters=[98])], generatedKeyContext=Optional[GeneratedKeyContext(columnName=id, generated=false, generatedValues=[98])])
2021-05-27 18:49:42.398 INFO 21560 --- [ main] ShardingSphere-SQL : Actual SQL: ds0 ::: INSERT INTO position ( id ) VALUES (?) ::: [98]
2021-05-27 18:49:42.402 INFO 21560 --- [ main] ShardingSphere-SQL : Logic SQL: INSERT INTO position ( id ) VALUES ( ? )
2021-05-27 18:49:42.402 INFO 21560 --- [ main] ShardingSphere-SQL : SQLStatement: InsertStatementContext(super=CommonSQLStatementContext(sqlStatement=org.apache.shardingsphere.sql.parser.sql.statement.dml.InsertStatement@20c3b34b, tablesContext=org.apache.shardingsphere.sql.parser.binder.segment.table.TablesContext@12f8682a), tablesContext=org.apache.shardingsphere.sql.parser.binder.segment.table.TablesContext@12f8682a, columnNames=[id], insertValueContexts=[InsertValueContext(parametersCount=1, valueExpressions=[ParameterMarkerExpressionSegment(startIndex=40, stopIndex=40, parameterMarkerIndex=0)], parameters=[99])], generatedKeyContext=Optional[GeneratedKeyContext(columnName=id, generated=false, generatedValues=[99])])
2021-05-27 18:49:42.402 INFO 21560 --- [ main] ShardingSphere-SQL : Actual SQL: ds1 ::: INSERT INTO position ( id ) VALUES (?) ::: [99]
可以看到,奇数的id已经被送到了ds1
库中,偶数的id被送到了dis0
库中。
四、 总结
通过简单的配置ShardingSphere已经完成了分库操作,下一小节中我们会使用ShardingSphere进行分库+分表,同时解决主键ID生成的问题。