因为APP用户的快速增长和业务的快速发展,表数据必须也是快速的增加的,这样会导致查询效率的低下,那么我们有什么办法呢,那当然是使用分布式数据库中间件了,利用它来做数据库的读写分离,分表分库等等,市面上也已经有很多这种中间件,大概分为Client模式和Proxy模式这两种。因为网上的文章写得都不怎么详细,所以我自己利用一个星期的零碎时间去看官方的使用手册然后自己开始尝试,还有此次我将尝试的是ShardingSphere的Sharding-JDBC,是属于Client模式。
简介
Sharding-Sphere是一套开源的分布式数据库中间件解决方案组成的生态圈,它由Sharding-JDBC、Sharding-Proxy和Sharding-Sidecar这3款相互独立的产品组成。他们均提供标准化的数据分片、读写分离、柔性事务和数据治理功能,可适用于如Java同构、异构语言、容器、云原生等各种多样化的应用场景。
官网
https://shardingsphere.apache.org
Github
https://github.com/apache/incubator-shardingsphere
三大核心模块分别是Sharding-JDBC、Sharding-Proxy和Sharding-Sidecar。
Sharding-JDBC
定位为轻量级Java框架,在Java的JDBC层提供的额外服务。 它使用客户端直连数据库,以jar包形式提供服务,无需额外部署和依赖,可理解为增强版的JDBC驱动,完全兼容JDBC和各种ORM框架。
Sharding-Proxy
定位为透明化的数据库代理端,提供封装了数据库二进制协议的服务端版本,用于完成对异构语言的支持。 目前先提供MySQL版本,它可以使用任何兼容MySQL协议的访问客户端(如:MySQL Command Client, MySQL Workbench等)操作数据,对DBA更加友好。
Sharding-Sidecar
定位为Kubernetes或Mesos的云原生数据库代理,以DaemonSet的形式代理所有对数据库的访问。 通过无中心、零侵入的方案提供与数据库交互的的啮合层,即Database Mesh,又可称数据网格。
下面的例子主要是展示的是实体、配置文件和分片算法,其他的代码详情可以拉我github上面的项目来看看: https://github.com/Howinfun/Test-ShardingSphere.git
如果想详细了解Sharding-JDBC的使用方法,可以看官方文档:https://shardingsphere.apache.org/document/current/cn/quick-start/
实体[xmjbq_user]:
/**
* @author howinfun
* @version 1.0
* @desc 用户
* @date 2018/12/10
* @company XMJBQ
*/
@Data
@Table(name="xmjbq_user")
public class XmjbqUser implements Serializable {
@Id
private String id;
private String userName; // 昵称
private String realName; // 真实姓名
private String identify; // 身份证号码
private String phone; // 手机号码
}
配置文件application-sharding-tables.properties:
# 分表不分库(只有一个数据源)
sharding.jdbc.datasource.names=ds0
# 数据源
sharding.jdbc.datasource.ds0.type=com.alibaba.druid.pool.DruidDataSource
sharding.jdbc.datasource.ds0.driver-class-name=com.mysql.jdbc.Driver
sharding.jdbc.datasource.ds0.url=jdbc:mysql://localhost:3306/ds0
sharding.jdbc.datasource.ds0.username=root
sharding.jdbc.datasource.ds0.password=123456
#### 分表策略 ######
##### 数据节点(ds0库里头有两个xmjbq_user表,一个是xmjbq_user0、一个是xmjbq_user1) #####
sharding.jdbc.config.sharding.tables.xmjbq_user.actual-data-nodes=ds0.xmjbq_user$->{0..1}
#用于单分片键的标准分片场景
#分片列名称
sharding.jdbc.config.sharding.tables.xmjbq_user.table-strategy.standard.sharding-column=id
#精确分片算法类名称,用于=和IN。该类需实现PreciseShardingAlgorithm接口并提供无参数的构造器
sharding.jdbc.config.sharding.tables.xmjbq_user.table-strategy.standard.precise-algorithm-class-name=com.hyf.shardingsphere.utils.XmjbqUserTablesPreciseShardingAlgorithm
因为这里实体的ID为字符串,所以分片算法就不使用行表达式了,我们自己实现PreciseShardingAlgorithm接口来搞自己的分片算法:
package com.hyf.shardingsphere.utils;
import io.shardingsphere.api.algorithm.sharding.PreciseShardingValue;
import io.shardingsphere.api.algorithm.sharding.standard.PreciseShardingAlgorithm;
import java.util.Collection;
/**
* @author howinfun
* @version 1.0
* @desc 分表:精确分片算法,用于=和IN
* @date 2019/2/14
* @company XMJBQ
*/
public class XmjbqUserTablesPreciseShardingAlgorithm implements PreciseShardingAlgorithm {
/**
*
* @param collection 表名集合
* @param preciseShardingValue 分片列
* @return
*/
@Override
public String doSharding(Collection collection, PreciseShardingValue preciseShardingValue) {
// 分片字段值
String value = preciseShardingValue.getValue();
// 现在算法是:%2 求余如果是0则xmjbq_user0,如果是1则xmjbq_user1。但是由于id是字符串而且是很长的,所以截取最后一位然后转为Integer类型再求余
value = value.substring(value.length()-1,value.length());
Integer number = Integer.valueOf(value);
int result = number % 2;
for (String s : collection) {
if(s.endsWith(result+"")){
return s;
}
}
throw new UnsupportedOperationException();
}
}
测试结果:当id%2的余数为0,数据则插入到表ds0.xmjbq_user0中,如果余数为1则插入到表ds0.xmjbq_user1中。
配置文件application-sharding-databases.properties:
# 分库不分表(两个数据源)
sharding.jdbc.datasource.names=ds0,ds1
# 数据源1
sharding.jdbc.datasource.ds0.type=com.alibaba.druid.pool.DruidDataSource
sharding.jdbc.datasource.ds0.driver-class-name=com.mysql.jdbc.Driver
sharding.jdbc.datasource.ds0.url=jdbc:mysql://localhost:3306/ds0
sharding.jdbc.datasource.ds0.username=root
sharding.jdbc.datasource.ds0.password=123456
# 数据源2
sharding.jdbc.datasource.ds1.type=com.alibaba.druid.pool.DruidDataSource
sharding.jdbc.datasource.ds1.driver-class-name=com.mysql.jdbc.Driver
sharding.jdbc.datasource.ds1.url=jdbc:mysql://localhost:3306/ds1
sharding.jdbc.datasource.ds1.username=root
sharding.jdbc.datasource.ds1.password=123456
#### 分库策略 ######
##### 数据节点(两个数据源都存在同样的表xmjbq_user) #####
sharding.jdbc.config.sharding.tables.xmjbq_user.actual-data-nodes=ds$->{0..1}.xmjbq_user
#用于单分片键的标准分片场景
#分片列名称
sharding.jdbc.config.sharding.tables.xmjbq_user.database-strategy.standard.sharding-column=id
#精确分片算法类名称,用于=和IN。该类需实现PreciseShardingAlgorithm接口并提供无参数的构造器
sharding.jdbc.config.sharding.tables.xmjbq_user.database-strategy.standard.precise-algorithm-class-name=com.hyf.shardingsphere.utils.XmjbqUserDataBasesPreciseShardingAlgorithm
分片算法同样是我们自己实现PreciseShardingAlgorithm接口:
package com.hyf.shardingsphere.utils;
import io.shardingsphere.api.algorithm.sharding.PreciseShardingValue;
import io.shardingsphere.api.algorithm.sharding.standard.PreciseShardingAlgorithm;
import java.util.Collection;
/**
* @author howinfun
* @version 1.0
* @desc 分库:精确分片算法,用于=和IN
* @date 2019/2/14
* @company XMJBQ
*/
public class XmjbqUserDataBasesPreciseShardingAlgorithm implements PreciseShardingAlgorithm {
/**
*
* @param collection 库名集合
* @param preciseShardingValue 分片列
* @return
*/
@Override
public String doSharding(Collection collection, PreciseShardingValue preciseShardingValue) {
// 分片字段值
String value = preciseShardingValue.getValue();
// 现在算法是:%2 求余如果是0则ds0.xmjbq_user,如果是1则ds0.xmjbq_user。但是由于id是字符串而且是很长的,所以截取最后一位然后转为Integer类型再求余
value = value.substring(value.length()-1,value.length());
Integer number = Integer.valueOf(value);
int result = number % 2;
for (String s : collection) {
if(s.endsWith(result+"")){
return s;
}
}
throw new UnsupportedOperationException();
}
}
测试结果:当id%2的余数为0,数据则插入到表ds0.xmjbq_user中,如果余数为1则插入到表ds1.xmjbq_user中。
分库的分片字段是id,分表的分片字段是phone,当然了,分表分库的分片字段都是一样也可以。
配置文件application-sharding-databases-tables.properties:
# 分表分库(两个数据源,每个数据源两张表)
sharding.jdbc.datasource.names=ds0,ds1
# 数据源1
sharding.jdbc.datasource.ds0.type=com.alibaba.druid.pool.DruidDataSource
sharding.jdbc.datasource.ds0.driver-class-name=com.mysql.jdbc.Driver
sharding.jdbc.datasource.ds0.url=jdbc:mysql://localhost:3306/ds0
sharding.jdbc.datasource.ds0.username=root
sharding.jdbc.datasource.ds0.password=123456
# 数据源2
sharding.jdbc.datasource.ds1.type=com.alibaba.druid.pool.DruidDataSource
sharding.jdbc.datasource.ds1.driver-class-name=com.mysql.jdbc.Driver
sharding.jdbc.datasource.ds1.url=jdbc:mysql://localhost:3306/ds1
sharding.jdbc.datasource.ds1.username=root
sharding.jdbc.datasource.ds1.password=123456
##### 数据节点 #####
sharding.jdbc.config.sharding.tables.xmjbq_user.actual-data-nodes=ds$->{0..1}.xmjbq_user$->{0..1}
#### 分库策略(我们尝试一下默认配置,即如果没有给指定表配置分库配置,就使用这个) ######
#分片列名称
sharding.jdbc.config.sharding.default-database-strategy.standard.sharding-column=id
#精确分片算法类名称,用于=和IN。该类需实现PreciseShardingAlgorithm接口并提供无参数的构造器
sharding.jdbc.config.sharding.default-database-strategy.standard.precise-algorithm-class-name=com.hyf.shardingsphere.utils.XmjbqUserDataBasesPreciseShardingAlgorithm
#### 分表策略(给每个表指定) ##########
#用于单分片键的标准分片场景
#分片列名称
sharding.jdbc.config.sharding.tables.xmjbq_user.table-strategy.standard.sharding-column=phone
#精确分片算法类名称,用于=和IN。该类需实现PreciseShardingAlgorithm接口并提供无参数的构造器
sharding.jdbc.config.sharding.tables.xmjbq_user.table-strategy.standard.precise-algorithm-class-name=com.hyf.shardingsphere.utils.XmjbqUserTablesPreciseShardingAlgorithm
分库的分片算法和分表的分片算法都是实现PreciseShardingAlgorithm接口:
package com.hyf.shardingsphere.utils;
import io.shardingsphere.api.algorithm.sharding.PreciseShardingValue;
import io.shardingsphere.api.algorithm.sharding.standard.PreciseShardingAlgorithm;
import java.util.Collection;
/**
* @author howinfun
* @version 1.0
* @desc 分库:精确分片算法,用于=和IN
* @date 2019/2/14
* @company XMJBQ
*/
public class XmjbqUserDataBasesPreciseShardingAlgorithm implements PreciseShardingAlgorithm {
/**
*
* @param collection 库名集合
* @param preciseShardingValue 分片列
* @return
*/
@Override
public String doSharding(Collection collection, PreciseShardingValue preciseShardingValue) {
// 分片字段值
String value = preciseShardingValue.getValue();
// 现在算法是:%2 求余如果是0则ds0.xmjbq_user,如果是1则ds0.xmjbq_user。但是由于id是字符串而且是很长的,所以进行截取然后转为Integer类型再求余
value = value.substring(value.length()-6,value.length()-3);
Integer number = Integer.valueOf(value);
int result = number % 2;
for (String s : collection) {
if(s.endsWith(result+"")){
return s;
}
}
throw new UnsupportedOperationException();
}
}
package com.hyf.shardingsphere.utils;
import io.shardingsphere.api.algorithm.sharding.PreciseShardingValue;
import io.shardingsphere.api.algorithm.sharding.standard.PreciseShardingAlgorithm;
import java.util.Collection;
/**
* @author howinfun
* @version 1.0
* @desc 分表:精确分片算法,用于=和IN
* @date 2019/2/14
* @company XMJBQ
*/
public class XmjbqUserTablesPreciseShardingAlgorithm implements PreciseShardingAlgorithm {
/**
*
* @param collection 表名集合
* @param preciseShardingValue 分片列
* @return
*/
@Override
public String doSharding(Collection collection, PreciseShardingValue preciseShardingValue) {
// 分片字段值
String value = preciseShardingValue.getValue();
// 现在算法是:%2 求余如果是0则xmjbq_user0,如果是1则xmjbq_user1。但是由于phone是字符串,所以进行截取然后转为Integer类型再求余
value = value.substring(value.length()-1,value.length());
Integer number = Integer.valueOf(value);
int result = number % 2;
for (String s : collection) {
if(s.endsWith(result+"")){
return s;
}
}
throw new UnsupportedOperationException();
}
public static void main(String[] args) {
System.out.println(234324235%2);
System.out.println(234324234%2);
System.out.println(234324231%2);
}
}
测试结果:如果id%2的余数为0,则数据库ds0,余数为1则数据库ds1;如果phone%2的余数为0,则表xmjbq_user0,余数为1则表xmjbq_user1。
为什么我会选择Sharding-JDBC,是因为它定位为轻量级Java框架,在Java的JDBC层提供的额外服务。 它使用客户端直连数据库,以jar包形式提供服务,无需额外部署和依赖,可理解为增强版的JDBC驱动,完全兼容JDBC和各种ORM框架,这样子在原来的项目上增加分表分库就是不这么难了,改架构也是变得非常简单的事情。当然了,ShardingSphere3.x有三大部分,此次综合到各种考虑只使用到其中的一种,以后有机会得尝尝其它的方式。
最后感谢你观看此文章,如有疑问欢迎评论~