摘要
范围(range)分表也需要确切(precise)分表策略,这点很重要。 确切分表根据分表字段确定数据落在哪一个库。 范围分表策略可以根据分表字段的上下限决定从哪些表去查找数据。
数据库脚本
DROP TABLE IF EXISTS `t_order201909`;
CREATE TABLE `t_order201909` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`user_id` bigint(32) NULL DEFAULT NULL,
`order_id` bigint(32) NULL DEFAULT NULL,
`title` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,
`content` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,
`create_time` datetime(0) NULL DEFAULT NULL,
`update_time` datetime(0) NULL DEFAULT NULL,
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 4 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Dynamic;
SET FOREIGN_KEY_CHECKS = 1;
DROP TABLE IF EXISTS `t_order201910`;
CREATE TABLE `t_order201910` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`user_id` bigint(32) NULL DEFAULT NULL,
`order_id` bigint(32) NULL DEFAULT NULL,
`title` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,
`content` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,
`create_time` datetime(0) NULL DEFAULT NULL,
`update_time` datetime(0) NULL DEFAULT NULL,
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 8 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Dynamic;
SET FOREIGN_KEY_CHECKS = 1;
实践
确切分表(Precise分表)
分表策略
package com.zero.sharding.shardingrule;
import java.time.format.DateTimeFormatter;
import java.util.Collection;
import java.util.Date;
import java.util.Locale;
import org.apache.shardingsphere.api.sharding.standard.PreciseShardingAlgorithm;
import org.apache.shardingsphere.api.sharding.standard.PreciseShardingValue;
import org.joda.time.LocalDate;
import lombok.extern.slf4j.Slf4j;
/**
* @author Michael Feng
* @date 2019年9月19日
* @description
*/
@Slf4j
public class DatePreciseShardingAlgorithm implements PreciseShardingAlgorithm<Date> {
private static DateTimeFormatter sdf = DateTimeFormatter.ofPattern("yyyyMM", Locale.CHINA);
@Override
public String doSharding(Collection<String> availableTargetNames, PreciseShardingValue<Date> shardingValue) {
String loginTableName = shardingValue.getLogicTableName();
Date createTime = shardingValue.getValue();
String yyyyMM= "201909";
try{
yyyyMM = LocalDate.fromDateFields(createTime).toString("yyyyMM", Locale.CHINA);
}catch(Exception e){
log.error("解析创建时间异常,分表失败,进入默认表");
}
return loginTableName+yyyyMM;
}
}
Springboot配置
#数据源
spring.shardingsphere.datasource.names=sharding0,sharding1
#默认数据源
spring.shardingsphere.sharding.default-data-source-name=sharding0
# 显示sql
spring.shardingsphere.props.sql.show=true
#sharding0数据源配置
spring.shardingsphere.datasource.sharding0.type=com.alibaba.druid.pool.DruidDataSource
spring.shardingsphere.datasource.sharding0.driver-class-name=com.mysql.cj.jdbc.Driver
spring.shardingsphere.datasource.sharding0.url=jdbc:mysql://139.196.229.195:3306/sharding0?useUnicode=true&characterEncoding=utf8&autoReconnect=true&allowMultiQueries=true&serverTimezone=Asia/Shanghai&zeroDateTimeBehavior=CONVERT_TO_NULL
spring.shardingsphere.datasource.sharding0.username=root
spring.shardingsphere.datasource.sharding0.password=%Pan120%
#sharding1 数据源配置
spring.shardingsphere.datasource.sharding1.type=com.alibaba.druid.pool.DruidDataSource
spring.shardingsphere.datasource.sharding1.driver-class-name=com.mysql.cj.jdbc.Driver
spring.shardingsphere.datasource.sharding1.url=jdbc:mysql://139.196.229.195:3306/sharding1?useUnicode=true&characterEncoding=utf8&autoReconnect=true&allowMultiQueries=true&serverTimezone=Asia/Shanghai&zeroDateTimeBehavior=CONVERT_TO_NULL
spring.shardingsphere.datasource.sharding1.username=root
spring.shardingsphere.datasource.sharding1.password=%Pan120%
# 分库配置
spring.shardingsphere.sharding.default-database-strategy.inline.sharding-column=user_id
spring.shardingsphere.sharding.default-database-strategy.inline.algorithm-expression=sharding$->{user_id % 2}
#确切水平分表
spring.shardingsphere.sharding.tables.t_order.table-strategy.standard.sharding-column=create_time
spring.shardingsphere.sharding.tables.t_order.table-strategy.standard.precise-algorithm-class-name=com.zero.sharding.shardingrule.DatePreciseShardingAlgorithm
测试示例
@Test
public void testHorizonShardingWrite() throws ParseException{
Order order = new Order();
order.setUserId(0l);
// order.setOrderId(0l);
order.setTitle("测试,userId:"+order.getUserId() + " orderId:" + order.getOrderId());
order.setContent(order.getTitle());
order.setCreateTime(DateUtils.parseDate("20191018", "yyyyMMdd"));
Assert.assertEquals(1,orderMapper.insert(order));
}
@Test
public void testHorizonShardingRead() throws ParseException{
OrderExample ex = new OrderExample();
ex.createCriteria().andUserIdEqualTo(0l).andCreateTimeEqualTo(DateUtils.parseDate("20191018", "yyyyMMdd"));
List<Order> orders = orderMapper.selectByExample(ex);
orders.stream().forEach(o->{
System.out.println("userId:"+o.getUserId() + " orderId:" + o.getOrderId());
});
}
范围分表 (range)
范围分表策略需要结合确切分表策略一起使用。范围分表策略可以确定范围检索涉及到哪些库表,确切分表策略可以根据分表字段确定具体入哪个表。
分表策略
DatePreciseShardingAlgorithm
package com.zero.sharding.shardingrule;
import java.time.format.DateTimeFormatter;
import java.util.Collection;
import java.util.Date;
import java.util.Locale;
import org.apache.shardingsphere.api.sharding.standard.PreciseShardingAlgorithm;
import org.apache.shardingsphere.api.sharding.standard.PreciseShardingValue;
import org.joda.time.LocalDate;
import lombok.extern.slf4j.Slf4j;
/**
* @author Michael Feng
* @date 2019年9月19日
* @description
*/
@Slf4j
public class DatePreciseShardingAlgorithm implements PreciseShardingAlgorithm<Date> {
private static DateTimeFormatter sdf = DateTimeFormatter.ofPattern("yyyyMM", Locale.CHINA);
@Override
public String doSharding(Collection<String> availableTargetNames, PreciseShardingValue<Date> shardingValue) {
String loginTableName = shardingValue.getLogicTableName();
Date createTime = shardingValue.getValue();
String yyyyMM= "201909";
try{
yyyyMM = LocalDate.fromDateFields(createTime).toString("yyyyMM", Locale.CHINA);
}catch(Exception e){
log.error("解析创建时间异常,分表失败,进入默认表");
}
return loginTableName+yyyyMM;
}
}
DateRangeShardingAlgorithm
package com.zero.sharding.shardingrule;
import java.util.Collection;
import java.util.Date;
import java.util.Locale;
import java.util.concurrent.atomic.AtomicInteger;
import org.apache.commons.lang.time.DateUtils;
import org.apache.shardingsphere.api.sharding.standard.RangeShardingAlgorithm;
import org.apache.shardingsphere.api.sharding.standard.RangeShardingValue;
import org.joda.time.LocalDate;
import com.google.common.collect.Range;
import com.google.common.collect.Sets;
import lombok.extern.slf4j.Slf4j;
import net.minidev.json.JSONObject;
/**
* @author Michael Feng
* @date 2019年9月19日
* @description
*/
@Slf4j
public class DateRangeShardingAlgorithm implements RangeShardingAlgorithm<Date> {
@Override
public Collection<String> doSharding(Collection<String> availableTargetNames,
RangeShardingValue<Date> shardingValue) {
Collection<String> tableSet = Sets.newConcurrentHashSet();
String logicTableName = shardingValue.getLogicTableName();
Range<Date> dates = shardingValue.getValueRange();
Date lowDate = dates.lowerEndpoint();
Date upperDate = dates.upperEndpoint();
AtomicInteger i = new AtomicInteger(0);
while(DateUtils.addDays(lowDate, i.get()).compareTo(upperDate)<=0){
tableSet.add(logicTableName+LocalDate.fromDateFields(DateUtils.addDays(lowDate, i.getAndAdd(1))).toString("yyyyMM", Locale.CHINA) );
}
return tableSet;
}
}
Springboot配置
#数据源
spring.shardingsphere.datasource.names=sharding0,sharding1
#默认数据源
spring.shardingsphere.sharding.default-data-source-name=sharding0
# 显示sql
spring.shardingsphere.props.sql.show=true
#sharding0数据源配置
spring.shardingsphere.datasource.sharding0.type=com.alibaba.druid.pool.DruidDataSource
spring.shardingsphere.datasource.sharding0.driver-class-name=com.mysql.cj.jdbc.Driver
spring.shardingsphere.datasource.sharding0.url=jdbc:mysql://139.196.229.195:3306/sharding0?useUnicode=true&characterEncoding=utf8&autoReconnect=true&allowMultiQueries=true&serverTimezone=Asia/Shanghai&zeroDateTimeBehavior=CONVERT_TO_NULL
spring.shardingsphere.datasource.sharding0.username=root
spring.shardingsphere.datasource.sharding0.password=%Pan120%
#sharding1 数据源配置
spring.shardingsphere.datasource.sharding1.type=com.alibaba.druid.pool.DruidDataSource
spring.shardingsphere.datasource.sharding1.driver-class-name=com.mysql.cj.jdbc.Driver
spring.shardingsphere.datasource.sharding1.url=jdbc:mysql://139.196.229.195:3306/sharding1?useUnicode=true&characterEncoding=utf8&autoReconnect=true&allowMultiQueries=true&serverTimezone=Asia/Shanghai&zeroDateTimeBehavior=CONVERT_TO_NULL
spring.shardingsphere.datasource.sharding1.username=root
spring.shardingsphere.datasource.sharding1.password=%Pan120%
# 分库配置
spring.shardingsphere.sharding.default-database-strategy.inline.sharding-column=user_id
spring.shardingsphere.sharding.default-database-strategy.inline.algorithm-expression=sharding$->{user_id % 2}
#范围水平分表
spring.shardingsphere.sharding.tables.t_order.table-strategy.standard.precise-algorithm-class-name=com.zero.sharding.shardingrule.DatePreciseShardingAlgorithm
spring.shardingsphere.sharding.tables.t_order.table-strategy.standard.sharding-column=create_time
spring.shardingsphere.sharding.tables.t_order.table-strategy.standard.range-algorithm-class-name=com.zero.sharding.shardingrule.DateRangeShardingAlgorithm
测试示例
@Test
public void testHorizonShardingWrite() throws ParseException{
Order order = new Order();
order.setUserId(0l);
// order.setOrderId(0l);
order.setTitle("测试,userId:"+order.getUserId() + " orderId:" + order.getOrderId());
order.setContent(order.getTitle());
order.setCreateTime(DateUtils.parseDate("20191018", "yyyyMMdd"));
Assert.assertEquals(1,orderMapper.insert(order));
}
@Test
public void testHorizonShardingRead() throws ParseException{
OrderExample ex = new OrderExample();
ex.createCriteria().andUserIdEqualTo(0l).andCreateTimeEqualTo(DateUtils.parseDate("20191018", "yyyyMMdd"));
List<Order> orders = orderMapper.selectByExample(ex);
orders.stream().forEach(o->{
System.out.println("userId:"+o.getUserId() + " orderId:" + o.getOrderId());
});
}
@Test
public void testHorizonShardingRangeRead() throws ParseException{
OrderExample ex = new OrderExample();
ex.createCriteria().andUserIdEqualTo(0l).andCreateTimeBetween(DateUtils.parseDate("20190918", "yyyyMMdd"),DateUtils.parseDate("20191018", "yyyyMMdd"));
List<Order> orders = orderMapper.selectByExample(ex);
orders.stream().forEach(o->{
System.out.println("userId:"+o.getUserId() + " orderId:" + o.getOrderId());
});
}
总结
不总结