Sharding-JDBC数据分库分表实践(水平分表)

眉间皱痕 提交于 2019-11-30 03:48:59

摘要

范围(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());
		});
	}

总结

不总结

参考文档

分库分表-springboot配置

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!