Sharding-JDBC自定义分片算法StandardShardingAlgorithm按年分表实践

  |   0 评论   |   0 浏览

目前我这个项目的需求比较特殊,是一个收集股票相关数据的场景,拿日线数据举例,目前5千只股票,每年240个交易日左右,也就是1年120万条数据,10年1200万条数据,20年2400万条数据,虽然数据增长慢,但是为了提升查询性能,这个表也要进行分表,好了,问题来了,怎么分?按股票的TS代码来分还是按年分?这里其实要考虑后续查询场景,能简单的定义按代码或者是按年,后面的查询的时候要带上分片键才能走分表,否则就会走广播,得不偿失,我们查日线或者日统计数据的时候,基本都是基于时间进行查询的,所以这里只能根据交易时间字段来分表,但是又出现一个问题,按年分的话,1年120万条数据会不会少?mysql数据库一张表1000万以内查的话,走索引还是很快的,这样,按5年分一张表,这样每张分表600万左右的数据,还是很合理。那按交易时间的年,每5年一张分表怎么分呢?下面就是具体的实现:

先定义一个分表后缀枚举类:

public enum YearRange {
	Y2015("20150101", "20191231", "2015"), Y2020("20200101", "20241231", "2020"), 
Y2025("20250101", "20291231", "2025"), Y2030("20300101", "20341231", "2030");

	private String begin;

	private String end;

	private String year;

	private YearRange(String begin, String end, String year) {
		this.begin = begin;
		this.end = end;
		this.year = year;
	}

	public String getBegin() {
		return begin;
	}

	public String getEnd() {
		return end;
	}

	public String getYear() {
		return year;
	}

}

其中year定义了表名后缀,begin和end定义了判断交易时间是否属于这个范围,这是一个完全闭合的范围。

下面定义切片算法YearTableShardingAlgorithm:

public class YearTableShardingAlgorithm implements StandardShardingAlgorithm<String> {

	@Override
	public String doSharding(Collection<String> availableTargetNames, PreciseShardingValue<String> shardingValue) {
		String date = shardingValue.getValue();
		if (date == null) {
			throw new UnsupportedOperationException("Date column[" + shardingValue.getColumnName() + "] value can not be null");
		}
		String year = date.substring(0, 4);
		String suffix = getSuffix(year);
		for (String name : availableTargetNames) {
			if (name.endsWith(suffix)) {
				return name;
			}
		}
		throw new UnsupportedOperationException("Can not found available target name for suffix" + suffix);
	}

	@Override
	public Collection<String> doSharding(Collection<String> availableTargetNames, RangeShardingValue<String> shardingValue) {
		Range<String> dates = shardingValue.getValueRange();
		YearRange[] yrs = YearRange.values();
		Set<String> suffix = Sets.newHashSet();
		for (YearRange yr : yrs) {
			if (dates.isConnected(Range.closed(yr.getBegin(), yr.getEnd()))) {
				suffix.add(yr.getYear());
			}
		}
		if (!suffix.isEmpty()) {
			return availableTargetNames.parallelStream().filter(n -> {
				String s = n.substring(n.length() - 4);
				return suffix.contains(s);
			}).collect(Collectors.toList());
		}
		throw new UnsupportedOperationException("Can not found available target name for range[" + dates.toString() + "]");
	}

	private String getSuffix(String year) {
		int endWith = Integer.valueOf(year.substring(3));
		if (endWith >= 5) {
			endWith = 5;
		} else {
			endWith = 0;
		}
		return year.substring(0, 3) + endWith;
	}

	@Override
	public void init(Properties props) {
	}

}

下面是Sharding的配置:

1684482441629757440.png

1684482967943606272.png

Sharding提供的自定义算法实现起来还是非常灵活的,能够自己去定义分片键和表后缀的实现逻辑,可以应对很多场景了。


标题:Sharding-JDBC自定义分片算法StandardShardingAlgorithm按年分表实践
作者:michael
地址:https://blog.junxworks.cn/articles/2021/09/23/1632376141483.html