01、主要概念
增强了JDBC的功能,使JDBC具有分库分表的作用。比如:SQL的解析、路由、执行和结果的处理等等。
02、框架:
逻辑表会在SQL解析和路由时被替换成真实的表名。
分片键不一定是主键,也不一定有业务含义。
03、核心概念
数据节点、逻辑表、真实表、动态表、广播表、绑定表、分片键
【1】、动态表
对于逻辑表的分表
【2】、广播表
全局表,每个分片的数据库都包含有该表。
【3】、绑定表
主表和字表的绑定关系应该在同一个数据库,负责无法关联出数据的完整性。
04、编写流程
- 【1】、配置数据源-->>配置多个数据源
- 【2】、定义表的分表规则
- 【3】、配置分库+分表规则
- 【4】、加载分片规则
- 【5】、获取数据源
原生代码案例
package com.gupaoedu.jdbc;
import io.shardingsphere.api.config.rule.ShardingRuleConfiguration;
import io.shardingsphere.api.config.rule.TableRuleConfiguration;
import io.shardingsphere.api.config.strategy.InlineShardingStrategyConfiguration;
import io.shardingsphere.shardingjdbc.api.ShardingDataSourceFactory;
import org.apache.commons.dbcp.BasicDataSource;
import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;
/**
* @Author: wangyognxun
* @Date: 2019/10/21 11:16
* @Description:sharding-jdbc
*/
public class ShardJDBCTest {
public static void main(String[] args) throws SQLException {
// 配置真实数据源
Map<String, DataSource> dataSourceMap = new HashMap<>();
// 配置第一个数据源
BasicDataSource dataSource1 = new BasicDataSource();
dataSource1.setDriverClassName("com.mysql.jdbc.Driver");
dataSource1.setUrl("jdbc:mysql://localhost:3306/shard0");
dataSource1.setUsername("root");
dataSource1.setPassword("123456");
dataSourceMap.put("ds0", dataSource1);
// 配置第二个数据源
BasicDataSource dataSource2 = new BasicDataSource();
dataSource2.setDriverClassName("com.mysql.jdbc.Driver");
dataSource2.setUrl("jdbc:mysql://localhost:3306/shard1");
dataSource2.setUsername("root");
dataSource2.setPassword("123456");
dataSourceMap.put("ds1", dataSource2);
// 配置Order表规则
TableRuleConfiguration orderTableRuleConfig = new TableRuleConfiguration();
orderTableRuleConfig.setLogicTable("order");
orderTableRuleConfig.setActualDataNodes("ds${0..1}.order${0..1}");
// 配置分库 + 分表策略
orderTableRuleConfig.setDatabaseShardingStrategyConfig(new InlineShardingStrategyConfiguration("order_id", "ds${order_id % 2}"));
orderTableRuleConfig.setTableShardingStrategyConfig(new InlineShardingStrategyConfiguration("order_id", "order${order_id % 2}"));
// 配置分片规则
ShardingRuleConfiguration shardingRuleConfig = new ShardingRuleConfiguration();
shardingRuleConfig.getTableRuleConfigs().add(orderTableRuleConfig);
Map<String,Object> map = new HashMap<>();
// 获取数据源对象
DataSource dataSource = ShardingDataSourceFactory.createDataSource(dataSourceMap, shardingRuleConfig,map, new Properties());
String sql = "SELECT * from order WHERE user_id=?";
try (
Connection conn = dataSource.getConnection();
PreparedStatement preparedStatement = conn.prepareStatement(sql)) {
preparedStatement.setInt(1, 2673);
System.out.println();
try (ResultSet rs = preparedStatement.executeQuery()) {
while(rs.next()) {
// %2结果,路由到 shard1.order1
System.out.println("order_id---------"+rs.getInt(1));
System.out.println("user_id---------"+rs.getInt(2));
System.out.println("create_time---------"+rs.getTime(3));
System.out.println("total_price---------"+rs.getInt(4));
}
}
}
}
}
05、总结:
分片策略5种:
行内、标准、复合、Hint、无。
06、雪花算法(snowFlake)
07、spring的配置
# MyBatis配置
mybatis.mapper-locations=classpath:mapper/*.xml
mybatis.config-location=classpath:mybatis-config.xml
# 数据源配置
sharding.jdbc.datasource.names=ds0,ds1
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
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.default-database-strategy.inline.sharding-column=user_id
sharding.jdbc.config.sharding.default-database-strategy.inline.algorithm-expression=ds${user_id % 2}
# 分库算法 user_info,多库分表
# 单库内没有分表,注释了分表策略
sharding.jdbc.config.sharding.tables.user_info.actual-data-nodes=ds$->{0..1}.user_info
sharding.jdbc.config.sharding.tables.user_info.databaseStrategy.inline.shardingColumn=user_id
sharding.jdbc.config.sharding.tables.user_info.databaseStrategy.inline.algorithm-expression=ds${user_id % 2}
# 分库算法 t_order 多库分表
sharding.jdbc.config.sharding.tables.t_order.databaseStrategy.inline.shardingColumn=order_id
sharding.jdbc.config.sharding.tables.t_order.databaseStrategy.inline.algorithm-expression=ds${order_id % 2}
sharding.jdbc.config.sharding.tables.t_order.actual-data-nodes=ds$->{0..1}.t_order
# 分库算法 t_order_item 多库分表
sharding.jdbc.config.sharding.tables.t_order_item.databaseStrategy.inline.shardingColumn=order_id
sharding.jdbc.config.sharding.tables.t_order_item.databaseStrategy.inline.algorithm-expression=ds${order_id % 2}
sharding.jdbc.config.sharding.tables.t_order_item.actual-data-nodes=ds$->{0..1}.t_order_item
# 绑定表规则列表,防止关联查询出现笛卡尔积
sharding.jdbc.config.sharding.binding-tables[0]=t_order,t_order_item
# 广播表
sharding.jdbc.config.sharding.broadcast-tables=t_config
08、分片策略【重点】
【1】、行表达式分片策略
InlineShardingStrategy类。只支持单分片键,提供对=和IN操作的支持。行内表达式的配置比较简单。
${begin..end}表示范围区间
${[unit1,unit2,unit_x]}表示枚举值
t_user_$->{u_id%8}表示t_user表根据u_id模8,而分成8张表,表名称为t_user_0到t_user_7。
【2】、标准分片策略
对应StandardShardingStrategy类
标准分片策略只支持单分片键,提供了提供PreciseShardingAlgorithm和RangeShardingAlgorithm两个分片算法,分别对应于SQL语句中的=,IN和BETWEEN、AND。
如果要使用标准分片策略,必须要实现PreciseShardingAlgorithm,用来处理=和IN的分片。
RangeShardingAlgorithm是可选的。如果没有实现,SQL语句会发到所有的数据节点上执行。
sharding.jdbc.config.sharding.tables.user_info.databaseStrategy.standard.shardingColumn=user_id
sharding.jdbc.config.sharding.tables.user_info.databaseStrategy.standard.preciseAlgorithmClassName=com.gupaoedu.config.DBShardAlgo
sharding.jdbc.config.sharding.tables.user_info.tableStrategy.standard.shardingColumn=user_id
sharding.jdbc.config.sharding.tables.user_info.tableStrategy.standard.preciseAlgorithmClassName=com.gupaoedu.config.TblPreShardAlgo
sharding.jdbc.config.sharding.tables.user_info.tableStrategy.standard.rangeAlgorithmClassName=com.gupaoedu.config.TblRangeShardAlgo
import io.shardingsphere.api.algorithm.sharding.PreciseShardingValue;
import io.shardingsphere.api.algorithm.sharding.standard.PreciseShardingAlgorithm;
import java.util.Collection;
/**
* 数据库分库的策略,根据分片键,返回数据库名称
*/
public class DBShardAlgo implements PreciseShardingAlgorithm<Long> {
@Override
public String doSharding(Collection<String> collection, PreciseShardingValue<Long> preciseShardingValue) {
String db_name="ds";
Long num= preciseShardingValue.getValue()%2;
db_name=db_name + num;
System.out.println("----------------db_name:" + db_name);
for (String each : collection) {
System.out.println("ds:" + each);
if (each.equals(db_name)) {
return each;
}
}
throw new IllegalArgumentException();
}
}
import io.shardingsphere.api.algorithm.sharding.PreciseShardingValue;
import io.shardingsphere.api.algorithm.sharding.standard.PreciseShardingAlgorithm;
import java.util.Collection;
/**
* 等值查询使用的分片算法,包括in
*/
public class TblPreShardAlgo implements PreciseShardingAlgorithm<Long> {
@Override
public String doSharding(Collection<String> availableTargetNames, PreciseShardingValue<Long> shardingColumn) {
// 不分表
System.out.println("-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-availableTargetNames:" + availableTargetNames);
for (String tbname : availableTargetNames) {
return tbname;
}
throw new IllegalArgumentException();
}
}
import com.google.common.collect.Range;
import io.shardingsphere.api.algorithm.sharding.RangeShardingValue;
import io.shardingsphere.api.algorithm.sharding.standard.RangeShardingAlgorithm;
import java.util.Collection;
import java.util.LinkedHashSet;
/**
* 范围查询所使用的分片算法
*/
public class TblRangeShardAlgo implements RangeShardingAlgorithm<Long> {
public Collection<String> doSharding(Collection<String> availableTargetNames, RangeShardingValue<Long> rangeShardingValue) {
System.out.println("范围-*-*-*-*-*-*-*-*-*-*-*---------------"+availableTargetNames);
System.out.println("范围-*-*-*-*-*-*-*-*-*-*-*---------------"+rangeShardingValue);
Collection<String> collect = new LinkedHashSet<>();
Range<Long> valueRange = rangeShardingValue.getValueRange();
for (Long i = valueRange.lowerEndpoint(); i <= valueRange.upperEndpoint(); i++) {
// 不分表
for (String each : availableTargetNames) {
collect.add(each);
/* if (each.endsWith(i % availableTargetNames.size() + "")) {
collect.add(each);
}*/
}
}
return collect;
}
}
【3】、复合分片策略
对应ComplexShardingStrategy类
支持等值查询和范围查询
【4】、Hint分片策略
通过Hint而非SQL解析的方式分片的策略。
【5】、不分片策略
对应NoneShardingStrategy。不分片的策略。
08、分片算法
【1】、精确分片算法
用于处理使用单一键作为分片键的=与IN进行分片的场景。需要配合StandardShardingStrategy使用。
【2】、范围分片算法
用于处理使用单一键作为分片键的BETWEENAND进行分片的场景。需要配合StandardShardingStrategy使用。
如果不配置范围分片算法,范围查询默认会路由到所有节点。
【3】、复合分片算法
用于处理使用多键作为分片键进行分片的场景,包含多个分片键的逻辑较复杂,需要应用开发者自行处理其中的复杂度。需要配合ComplexShardingStrategy使用。
【4】、Hint分片算法
用于处理使用Hint行分片的场景。需要配合HintShardingStrategy使用。
09、Shareing-JDBC的工作流程
SQL解析=>执行器优化=>SQL路由=>SQL改写=>SQL执行=>结果归并。
10、Sharding-JDBC实现原理
替换数据的4大对象:DataSource、Connection、Statement(PS)、ResulstSet。
11、与Mycat对比
Sharding-JDBC | Mycat | |
工作层面 | JDBC协议 | MySQL协议/JDBC协议 |
运行方式 | Jar包,客户端 | 独立服务,服务端 |
开发方式 | 代码/配置改动 | 连接地址(数据源) |
运维方式 | 无 | 管理独立服务,运维成本高 |
性能 | 多线程并发按操作,性能高 | 独立服务+网络开销,存在性能损失风险 |
功能范围 | 协议层面 | 包括分布式事务、数据迁移等 |
适用操作 | OLTP | OLTP+OLAP |
支持数据库 | 基于JDBC协议的数据库 | MySQL和其他支持JDBC协议的数据库 |
支持语言 | Java项目中使用 | 支持JDBC协议的语言 |