一、介绍
Sharding-JDBC是一个开源的分布式数据库中间件,它无需额外部署和依赖,旧代码迁移成本几乎为零。Sharding-JDBC作为面向开发的微服务云原生基础类库,完整的实现了分库分表、读写分离和分布式主键功能,
并初步实现了柔性事务。服务云原生基础类库,完整的实现了分库分表、读写分离和分布式主键功能,并初步实现了柔性事务。
官网:http://shardingsphere.io/document/current/cn/overview/
github源码:https://github.com/shardingjdbc/sharding-jdbc
官方demo:https://github.com/shardingjdbc/sharding-jdbc-example
二、 为什么选择sharding-jdbc
目前基础服务有很多业务都涉及到不少数据库拆分的问题,目前行业中比较主流的方式:
1、代理模式中间件(例如阿里开源的cobar,以及进化版的mycat)
2、client中间件模式(例如阿里的TDDL,以及我们选择的Sharding-JDBC)
3、自己业务端查询时动态指定数据源和表信息(spring的动态数据等)
我们为什么会选择Sharding-JDBC呢?
Sharding-JDBC直接封装JDBC API,可以理解为增强版的JDBC驱动,旧代码迁移成本几乎为零:
可适用于任何基于java的ORM框架,如:JPA, Hibernate, Mybatis, Spring JDBC Template或直接使用JDBC。
可基于任何第三方的数据库连接池,如:DBCP, C3P0, BoneCP, Druid等。
理论上可支持任意实现JDBC规范的数据库。目前支持MySQL,Oracle,SQLServer和PostgreSQL。
Sharding-JDBC定位为轻量级java框架,使用客户端直连数据库,以jar包形式提供服务,未使用中间层,无需额外部署,无其他依赖,DBA也无需改变原有的运维方式。采用”半理解”理念的SQL解析引擎,以达到性能与兼容性的最大平衡。
Sharding-JDBC功能灵活且全面:
分片策略灵活,可支持=,BETWEEN,IN等多维度分片,也可支持多分片键共用。
SQL解析功能完善,支持聚合,分组,排序,Limit,TOP等查询,并且支持Binding Table以及笛卡尔积的表查询。
支持柔性事务(目前仅最大努力送达型)。
支持读写分离。
支持分布式生成全局主键。
Sharding-JDBC配置多样:
可支持YAML和Spring命名空间配置
灵活多样的inline方式
三、 使用Sharding-jdbc 我们要关注的几个点
选择合理的分片键
需保证数据尽量均匀分配,以免出现热点数据的问题
需保证大部分查询用到分片键值作为查询条件 以免出现全库扫描的问题
分库后面临的问题
事务的取舍
查询的修改
数据库链接的浪费
四、 Sharding-jdbc 怎么使用
采用Java代码配置的方式
加载多数据,配置分库分表策略
例如:
@Configuration
public class ShardingConfig {
@Bean
public PaginationInterceptor paginationInterceptor() {
return new PaginationInterceptor();
}
@ConfigurationProperties(ignoreUnknownFields = false, prefix = "datasource.ds0")
@Bean(name = "ds0")
public DataSource dataSource0() {
return DataSourceBuilder.create().type(HikariDataSource.class).build();
}
@ConfigurationProperties(ignoreUnknownFields = false, prefix = "datasource.ds1")
@Bean(name = "ds1")
public DataSource dataSource1() {
return DataSourceBuilder.create().type(HikariDataSource.class).build();
}
@Bean
@Primary
public DataSource getDataSource(@Qualifier("ds0") DataSource ds0,
@Qualifier("ds1") DataSource ds1) throws SQLException {
ShardingRuleConfiguration shardingRuleConfig = new ShardingRuleConfiguration();
shardingRuleConfig.getTableRuleConfigs().add(getOrderTableRuleConfiguration());
shardingRuleConfig.getTableRuleConfigs().add(getOrderItemTableRuleConfiguration());
shardingRuleConfig.getBindingTableGroups().add("t_order, t_order_item");
shardingRuleConfig.setDefaultDatabaseShardingStrategyConfig(
new StandardShardingStrategyConfiguration("user_id",
new DatabaseShardingAlgorithm()));
shardingRuleConfig.setDefaultTableShardingStrategyConfig(
new StandardShardingStrategyConfiguration("user_id",
new TablePreciseShardingAlgorithm(),
new TableRangeShardingAlgorithm()));
Map dataSourceMap = new HashMap<>();
dataSourceMap.put("ds_2017", ds0);
dataSourceMap.put("ds_2018", ds1);
Properties properties = new Properties();
properties.setProperty("sql.show", Boolean.TRUE.toString());
// RegistryCenterConfiguration regConfig = new RegistryCenterConfiguration();
// regConfig.setServerLists("localhost:2181");
// regConfig.setNamespace("orchestration-sharding-data-source");
// OrchestrationConfiguration orchConfig = new
// OrchestrationConfiguration("sharding-jdbc-orchestration", regConfig, false);
// return OrchestrationShardingDataSourceFactory
// .createDataSource(dataSourceMap, shardingRuleConfig, new ConcurrentHashMap(), new
// Properties(), orchConfig);
return ShardingDataSourceFactory.createDataSource(dataSourceMap, shardingRuleConfig,
new ConcurrentHashMap(), properties);
}
@Bean(name = "shardSqlSessionFactory")
public SqlSessionFactory shardSqlSessionFactory() throws Exception {
SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
bean.setDataSource(getDataSource(dataSource0(), dataSource1()));
MybatisConfiguration configuration = new MybatisConfiguration();
configuration.setJdbcTypeForNull(JdbcType.NULL);
configuration.setMapUnderscoreToCamelCase(true);
configuration.setCacheEnabled(false);
bean.setConfiguration(configuration);
bean.setPlugins(new Interceptor[] {paginationInterceptor()});
bean.setMapperLocations(new PathMatchingResourcePatternResolver()
.getResources("classpath:config/mybatis/mapper/*.xml"));
return bean.getObject();
}
@Bean(name = "shardTransactionManager")
public DataSourceTransactionManager shardTransactionManager() throws Exception {
return new DataSourceTransactionManager(getDataSource(dataSource0(), dataSource1()));
}
@Bean(name = "shardSqlSessionTemplate")
public JdbcTemplate shardSqlSessionTemplate() throws Exception {
return new JdbcTemplate(getDataSource(dataSource0(), dataSource1()));
}
private static TableRuleConfiguration getOrderTableRuleConfiguration() {
TableRuleConfiguration result = new TableRuleConfiguration();
result.setLogicTable("t_order");
result.setActualDataNodes("ds_${2017..2018}.t_order_${[0, 1]}");
//result.setKeyGeneratorColumnName("order_id");
result.setDatabaseShardingStrategyConfig(new ComplexShardingStrategyConfiguration("order_id,create_time",
new ComplexKeysDataShardingAlgorithm()));
// result.setTableShardingStrategyConfig(new StandardShardingStrategyConfiguration("user_id",
// new TablePreciseShardingAlgorithm()));
return result;
}
private static TableRuleConfiguration getOrderItemTableRuleConfiguration() {
TableRuleConfiguration result = new TableRuleConfiguration();
result.setLogicTable("t_order_item");
result.setActualDataNodes("ds_${2017..2018}.t_order_item_${[0, 1]}");
return result;
}
具体分片策略会根据order表业务的分析 再采用哪种分片方式
五、 如何使用
配置完成以后在业务代码中使用和之前的使用方式没有改变,数据源的路由和表名字的动态修改都是由框架完成。mapper文件,还是像以前一样使用和书写