Sharding-JDBC資料分庫分表實踐(水平分表)
阿新 • • 發佈:2019-09-20
摘要
範圍(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());
});
}
總結
不總結