ShardingSphere是一套开源的分布式数据库中间件解决方案组成的生态圈,它由Sharding-JDBC、Sharding-Proxy和Sharding-Sidecar(计划中)这3款相互独立的产品组成。 他们均提供标准化的数据分片、分布式事务和数据库治理功能,可适用于如Java同构、异构语言、云原生等各种多样化的应用场景。
详细一点的介绍直接看官网:https://shardingsphere.apache.org/document/current/cn/overview/
逻辑表:水平拆分的数据库(表)的相同逻辑和数据结构表的总称。例:订单数据根据主键尾数拆分为2张表,分别是t_order_0到t_order_1,他们的逻辑表名为t_order。
真实表:在分片的数据库中真实存在的物理表。例:示例中的t_order_0到t_order_1
数据节点:数据分片的最小单元。由数据源名称和数据表组成,例:ds_0.t_order_0;ds_0.t_order_1;
绑定表:指分片规则一致的主表和子表。例如:t_order表和t_order_item表,均按照order_id分片,则此两张表互为绑定表关系。绑定表之间的多表关联查询不会出现笛卡尔积关联,关联查询效率将大大提升。
广播表:指所有的分片数据源中都存在的表,表结构和表中的数据在每个数据库中均完全一致。适用于数据量不大且需要与海量数据的表进行关联查询的场景。
SQL中如果无分片字段,将执行全路由,性能较差。 除了对单分片字段的支持,ShardingSphere也支持根据多个字段进行分片。
分片策略:包含分片键和分片算法,由于分片算法的独立性,将其独立抽离。真正可用于分片操作的是分片键 + 分片算法,也就是分片策略。
标准分片策略:对应StandardShardingStrategy,提供对SQL语句中的=, >, <, >=, <=, IN和BETWEEN AND的分片操作支持。
复合分片策略:对应ComplexShardingStrategy。提供对SQL语句中的=, >, <, >=, <=, IN和BETWEEN AND的分片操作支持。
行表达式分片策略:对应InlineShardingStrategy。使用Groovy的表达式,提供对SQL语句中的=和IN的分片操作支持,只支持单分片键。
对于简单的分片算法,可以通过简单的配置使用,从而避免繁琐的Java代码开发,如: t_user_$->{u_id % 8}
表示t_user表根据u_id模8,而分成8张表,表名称为t_user_0到t_user_7。
Hint分片策略:对应HintShardingStrategy。通过Hint指定分片值而非从SQL中提取分片值的方式进行分片的策略。
不分片策略:对应NoneShardingStrategy。
分片规则:分片规则配置的总入口。包含数据源配置、表配置、绑定表配置以及读写分离配置等。
方式一:基于配置文件集成,方便简单但是不够灵活
<!--主要有以下依赖,分库分表策略直接在application.properties做相关配置即可--> <dependency> <groupId>io.shardingsphere</groupId> <artifactId>sharding-jdbc-spring-boot-starter</artifactId> <version>3.1.0.M1</version> </dependency> <dependency> <groupId>io.shardingsphere</groupId> <artifactId>sharding-jdbc-spring-namespace</artifactId> <version>3.1.0.M1</version> </dependency>
方式二:这里我们主要基于java config的方式来集成到springboot中,更适合学习和理解
//相关依赖 <dependency> <groupId>io.shardingsphere</groupId> <artifactId>sharding-jdbc-core</artifactId> <version>3.1.0</version> </dependency> <!--<dependency> <groupId>io.shardingsphere</groupId> <artifactId>sharding-transaction-2pc-xa</artifactId> <version>3.1.0</version> </dependency>--> <dependency> <groupId>io.shardingsphere</groupId> <artifactId>sharding-jdbc-orchestration</artifactId> <version>3.1.0</version> </dependency> <dependency> <groupId>io.shardingsphere</groupId> <artifactId>sharding-orchestration-reg-zookeeper-curator</artifactId> <version>3.1.0</version> </dependency>
import javax.sql.DataSource; import java.lang.management.ManagementFactory; import java.sql.SQLException; import java.util.*; /** * @Author zhangboqing * @Date 2020/4/25 */ @Configuration @Slf4j public class ShardingSphereDataSourceConfig { @Bean("shardingDataSource") DataSource getShardingDataSource() throws SQLException { //初始化相关的分片规则配置信息控制机制 ShardingRuleConfiguration shardingRuleConfig = new ShardingRuleConfiguration(); // 设置相关的数据源 shardingRuleConfig.setDefaultDataSourceName("ds0"); // 设置相关的Order表的相关的规则信息配置机制 shardingRuleConfig.getTableRuleConfigs().add(getOrderTableRuleConfiguration()); // 设置相关的OrderItem表的相关的规则信息配置机制 shardingRuleConfig.getTableRuleConfigs().add(getOrderItemTableRuleConfiguration()); // 配置绑定表关系 shardingRuleConfig.getBindingTableGroups().add("t_order, t_order_item"); // 广播表操作机制 shardingRuleConfig.getBroadcastTables().add("t_config"); // 设置相关的分片机制策略(数据源分片策略机制控制) shardingRuleConfig.setDefaultDatabaseShardingStrategyConfig(new InlineShardingStrategyConfiguration("user_id", "ds${user_id % 2}")); // 设置相关的分片策略机制,子啊inline模式下(包含了两种模式) shardingRuleConfig.setDefaultTableShardingStrategyConfig(getShardingStrategyConfiguration()); // ShardingPropertiesConstant相关配置选项 Properties properties = new Properties(); //是否打印SQL解析和改写日志 properties.put("sql.show",true); //用于SQL执行的工作线程数量,为零则表示无限制 propertie.setProperty("executor.size","4"); //每个物理数据库为每次查询分配的最大连接数量 propertie.setProperty("max.connections.size.per.query","1"); //是否在启动时检查分表元数据一致性 propertie.setProperty("check.table.metadata.enabled","false"); //用户自定义属性 Map<String, Object> configMap = new HashMap<>(); configMap.put("effect","分库分表"); return ShardingDataSourceFactory.createDataSource(createDataSourceMap(), shardingRuleConfig, properties); } // 配置相关的分片策略机制 private ShardingStrategyConfiguration getShardingStrategyConfiguration(){ // 精确匹配 PreciseShardingAlgorithm<Long> preciseShardingAlgorithm = new PreciseShardingAlgorithm<Long>() { @Override public String doSharding(Collection<String> availableTargetNames, PreciseShardingValue<Long> shardingValue) { String prefix = shardingValue.getLogicTableName(); //逻辑表名称 Long orderId = shardingValue.getValue(); //订单编码 long index = orderId % 2; //订单表(分表路由索引) // t_order + "" + 0 = t_order0 String tableName = prefix + "" +index; // 精确查询、更新之类的,可以返回不存在表,进而给前端抛出异常和警告。 if (availableTargetNames.contains(tableName) == false) { LogUtils.error(log,"PreciseSharding","orderId:{},不存在对应的数据库表{}!", orderId, tableName); return availableTargetNames.iterator().next(); } return tableName; // return availableTargetNames.iterator().next(); } }; // 范围匹配 RangeShardingAlgorithm<Long> rangeShardingAlgorithm = new RangeShardingAlgorithm<Long>() { @Override public Collection<String> doSharding(Collection<String> availableTargetNames, RangeShardingValue<Long> shardingValue) { String prefix = shardingValue.getLogicTableName(); Collection<String> resList = new ArrayList<>(); // 获取相关的数据值范围 Range<Long> valueRange = shardingValue.getValueRange(); // 如果没有上限或者下限的没有,则直接返回所有的数据表 if (!valueRange.hasLowerBound() || !valueRange.hasUpperBound()) { return availableTargetNames; } // 获取下限数据范围 long lower = shardingValue.getValueRange().lowerEndpoint(); BoundType lowerBoundType = shardingValue.getValueRange().lowerBoundType(); // 获取下限数据范围 long upper = shardingValue.getValueRange().upperEndpoint(); BoundType upperBoundType = shardingValue.getValueRange().upperBoundType(); // 下限数据信息值 long startValue = lower; long endValue = upper; // 是否属于开区间(下限) if (lowerBoundType.equals(BoundType.OPEN)) { startValue++; //缩减范围1 } // 是否属于开区间(上限) if (upperBoundType.equals(BoundType.OPEN)) { endValue--; // 缩减范围1 } // 进行计算相关所需要是实体表 for (long i = startValue; i <= endValue ; i++) { long index = i % 2; String res = prefix + "" +index; // 精确查询、更新之类的,可以返回不存在表,进而给前端抛出异常和警告。 if (availableTargetNames.contains(res) == false) { LogUtils.error(log,"RangeSharding","orderId:{},不存在对应的数据库表{}!", i, res); }else{ resList.add(res); } } if (resList.size() == 0) { LogUtils.error(log,"RangeSharding","无法获取对应表,因此将对全表进行查询!orderId范围为:{}到{}",startValue,endValue); return availableTargetNames; } return resList; } }; // 设置相关整体的算法整合 ShardingStrategyConfiguration strategyConf = new StandardShardingStrategyConfiguration("order_id", preciseShardingAlgorithm, rangeShardingAlgorithm); return strategyConf; } // 获取相关的Order订单规则表配置信息控制配置控制机制 TableRuleConfiguration getOrderTableRuleConfiguration() { // 逻辑表 + 实际节点 :设置逻辑表与数据节点(数据分片的最小单位)的映射关系机制 TableRuleConfiguration result = new TableRuleConfiguration("t_order", "ds${0..1}.t_order${0..1}"); // 主键生成配置 result.setKeyGeneratorConfig(getKeyGeneratorConfigurationForTOrder()); return result; } //主键操作的生成策略 private KeyGeneratorConfiguration getKeyGeneratorConfigurationForTOrder() { Properties keyGeneratorProp = getKeyGeneratorProperties(); return new KeyGeneratorConfiguration("SNOWFLAKE", "order_id", keyGeneratorProp); } // 获取相关的Order订单规则表配置信息控制配置控制机制 TableRuleConfiguration getOrderItemTableRuleConfiguration() { TableRuleConfiguration result = new TableRuleConfiguration("t_order_item", "ds${0..1}.t_order_item${0..1}"); result.setKeyGeneratorConfig(getKeyGeneratorConfigurationForTOrderItem()); return result; } // 创建相关keyOrderItem机制控制操作 private KeyGeneratorConfiguration getKeyGeneratorConfigurationForTOrderItem() { Properties keyGeneratorProp = getKeyGeneratorProperties(); return new KeyGeneratorConfiguration("SNOWFLAKE", "id", keyGeneratorProp); } // 生成键值相关的generator的配置信息控制 private Properties getKeyGeneratorProperties() { Properties keyGeneratorProp = new Properties(); String distributeProcessIdentify = NetUtils.getLocalAddress() + ":" + getProcessId(); String workId = String.valueOf(convertString2Long(distributeProcessIdentify)); keyGeneratorProp.setProperty("worker.id", workId); LogUtils.info(log, "shardingsphere init", "shardingsphere work id raw string is {}, work id is {}", distributeProcessIdentify, workId); return keyGeneratorProp; } // 数据源相关配置机制 Map<String, DataSource> createDataSourceMap() { Map<String, DataSource> result = new HashMap<>(); result.put("ds0", DataSourceUtils.createDataSource("ds0")); result.put("ds1", DataSourceUtils.createDataSource("ds1")); return result; } // 常见相关的workerid和dataid对应相关的进程id private String getProcessId(){ String name = ManagementFactory.getRuntimeMXBean().getName(); String pid = name.split("@")[0]; return pid; } // 转换字符串成为相关的long类型 private Long convertString2Long(String str){ long hashCode = str.hashCode() + System.currentTimeMillis(); if(hashCode < 0){ hashCode = -hashCode; } return hashCode % (1L << 10); } }
/** * @Author zhangboqing * @Date 2020/4/23 */ @Configuration @MapperScan(basePackages = "com.zbq.springbootshardingjdbcjavaconfigdemo.dao",sqlSessionFactoryRef = "sqlSessionFactoryForShardingjdbc") public class ShardingsphereMybatisConfig { @Autowired @Qualifier("shardingDataSource") private DataSource dataSource; @Bean("sqlSessionFactoryForShardingjdbc") public SqlSessionFactory sqlSessionFactoryForShardingjdbc() throws Exception { SqlSessionFactoryBean sessionFactory = new SqlSessionFactoryBean(); sessionFactory.setDataSource(dataSource); // sessionFactory.setMapperLocations(new PathMatchingResourcePatternResolver(). // getResources("classpath*:**/*.xml")); sessionFactory.setTypeAliasesPackage("com.zbq.springbootshardingjdbcjavaconfigdemo.domain.entity"); org.apache.ibatis.session.Configuration configuration = new org.apache.ibatis.session.Configuration(); configuration.setMapUnderscoreToCamelCase(true); sessionFactory.setConfiguration(configuration); return sessionFactory.getObject(); } }
主要定制化配置事务操作可以空战未来的,为了未来的查询扩展XA
@Configuration @EnableTransactionManagement public class ShardingsphereTransactionConfig { @Bean @Autowired public PlatformTransactionManager shardingsphereTransactionManager(@Qualifier("shardingDataSource") DataSource dataSource) { return new DataSourceTransactionManager(dataSource); } }