<!--sharding-jdbc --> <dependency> <groupId>io.shardingsphere</groupId> <artifactId>sharding-jdbc-spring-boot-starter</artifactId> <version>3.1.0</version> </dependency> <!--sharding-jdbc结束-->
sharding-jdbc 按月份分表须要本身实现。须要实现两个接口PreciseShardingAlgorithm,RangeShardingAlgorithm(还没实现)。并在配置文件里添加实现路径
以下:com.simianBook.conf.TimeShardingTableAlgorithm
那么yml 里的配置路径以下java
sharding: jdbc: datasource: names: user-0,user-1 user-0: #springboot 在yml 配置里key不支持 '_' 推荐使用'-' type: com.alibaba.druid.pool.DruidDataSource driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://localhost:3306/t_user_0?characterEncoding=UTF-8&serverTimezone=GMT username: root password: hou1147646079 user-1: type: com.alibaba.druid.pool.DruidDataSource driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://localhost:3306/t_user_1?characterEncoding=UTF-8&serverTimezone=GMT username: root password: hou1147646079 config: sharding: #分片 default-database-strategy: inline: sharding-column: user_id algorithm-expression: user ->${user_id % 2} props: sql.show: true tables: user: key-generator-column-name: user_id #user 表的主键 database-strategy: inline: shardingColumn: user_id #数据库分片测略 algorithm-expression: user-${user_id % 2} # # actual-data-nodes: user-${0..1}.user_${0..1} #设置的datasource 的名字 如user-0,user-1,user_${0..1} 数据库中的 table-strategy: standard: # 单列sharidng算法,须要配合对应的preciseShardingAlgorithm,rangeShardingAlgorithm接口的实现使用,目前无生产可用实现 shardingColumn: user_id # 列名,容许单列 precise-algorithm-class-name: com.simianBook.config.TimeShardingTableAlgorithm # preciseShardingAlgorithm接口的实现类 # rangeShardingAlgorithm: # rangeShardingAlgorithm接口的实现类 # inline: # sharding-column: user_id # algorithm-expression: user_${user_id % 2} defaultTableStrategy: none: defaultKeyGenerator: type: SNOWFLAKE
下面须要来编写按月分表的方法node
package com.simianBook.config; import io.shardingsphere.api.algorithm.sharding.PreciseShardingValue; import io.shardingsphere.api.algorithm.sharding.standard.PreciseShardingAlgorithm; import io.shardingsphere.core.keygen.DefaultKeyGenerator; import java.text.SimpleDateFormat; import java.util.Collection; public class TimeShardingTableAlgorithm implements PreciseShardingAlgorithm<Long> { private SimpleDateFormat dateformat = new SimpleDateFormat("yyyyMM"); @Override public String doSharding(Collection<String> availableTargetNames, PreciseShardingValue<Long> shardingValue) { StringBuffer tableName = new StringBuffer(); tableName.append(shardingValue.getLogicTableName()) .append("_").append(getTime(shardingValue.getValue())); return tableName.toString(); } public String getTime(long shardingKey){ return dateformat.format(DefaultKeyGenerator.EPOCH+(Long.valueOf(shardingKey+"")>>22)); } }
我是使用sharding-jdbc 自带的雪花算法 来生成主键的,雪花算法的实现逻辑
所以我在获得分片键时对分片键进行逆推能够推出分片键的时间戳。再根据时间戳获得建立此条数据建立的年月进而定位到那个表位置(或者说表名)
DefaultKeyGenerator.EPOCH+(Long.valueOf(shardingKey+"")>>22)
DefaultKeyGenerator.EPOCH 表示起始时间。在雪花算法当中生成的时间戳须要减去起始时间在进行左移22位在进行或运算
sharding-jdbc 的雪花实现方法以下 版本3.0 该版本有bug 并发量低的时候生成的分片键始终为偶数mysql
public synchronized Number generateKey() { long currentMillis = timeService.getCurrentMillis(); Preconditions.checkState(this.lastTime <= currentMillis, "Clock is moving backwards, last time is %d milliseconds, current time is %d milliseconds", new Object[]{this.lastTime, currentMillis}); if (this.lastTime == currentMillis) { //最新时间与当前时间相同(发生并发) // //sequence+1 若是suquence&4095L==0时条件成立时 if (0L == (this.sequence = this.sequence + 1L & 4095L)) { currentMillis = this.waitUntilNextTime(currentMillis);//获取最新时间 } } else { this.sequence = 0L; //此处有bug 并发量低的时候this.sequence始终为0L的 } this.lastTime = currentMillis; return currentMillis - EPOCH << 22 | workerId << 12 | this.sequence; }
3.1.0 版本解决了 本身能够看一下有什么不一样web
public synchronized Number generateKey() { long currentMilliseconds = timeService.getCurrentMillis(); if (this.waitTolerateTimeDifferenceIfNeed(currentMilliseconds)) { currentMilliseconds = timeService.getCurrentMillis(); } if (this.lastMilliseconds == currentMilliseconds) { if (0L == (this.sequence = this.sequence + 1L & 4095L)) { currentMilliseconds = this.waitUntilNextTime(currentMilliseconds); } } else { this.vibrateSequenceOffset(); this.sequence = (long)this.sequenceOffset; } this.lastMilliseconds = currentMilliseconds; return currentMilliseconds - EPOCH << 22 | workerId << 12 | this.sequence; }
更新RangeShardingAlgorithm 的实现算法
package com.simianBook.config; import com.google.common.collect.Range; import com.simianBook.tool.ParaseShardingKeyTool; import io.shardingsphere.api.algorithm.sharding.RangeShardingValue; import io.shardingsphere.api.algorithm.sharding.standard.RangeShardingAlgorithm; import io.shardingsphere.core.keygen.DefaultKeyGenerator; import java.text.SimpleDateFormat; import java.util.Calendar; import java.util.Collection; import java.util.Date; import java.util.LinkedHashSet; import java.util.stream.Stream; /** * 搜查多表 * 范围搜索时(跨表)应传递时间戳并左移22位 */ public class TimeRangeShardingAlgorithm implements RangeShardingAlgorithm<Long> { private SimpleDateFormat dateformat = new SimpleDateFormat("yyyyMM"); @Override public Collection<String> doSharding(Collection<String> availableTargetNames, RangeShardingValue<Long> shardingValue) { Collection<String> result = new LinkedHashSet<String>(); Range<Long> shardingKey = shardingValue.getValueRange(); long startShardingKey = shardingKey.lowerEndpoint(); long endShardingKey = shardingKey.upperEndpoint(); String startTimeString = ParaseShardingKeyTool.getYearAndMonth(startShardingKey);//获取到开始时间戳 String endTimeString = ParaseShardingKeyTool.getYearAndMonth(endShardingKey);//获取结束时间戳 Calendar cal = Calendar.getInstance(); Date startTime = new Date(dateformat.format(startTimeString));//获取开始的年月 Date endTime = new Date(dateformat.format(endTimeString));//获取结束的年月 while(startTime.getTime() < endTime.getTime()){ //进行判断 获取跨月份的表 如201901,201902,201903 三个月的表 StringBuffer tableName = new StringBuffer(); tableName.append(shardingValue.getLogicTableName()) .append("_").append(dateformat.format(startTime)); result.add(tableName.toString()); } return result; } }
ParaseShardingKeyToolspring
package com.simianBook.tool; import io.shardingsphere.core.keygen.DefaultKeyGenerator; import java.text.SimpleDateFormat; public class ParaseShardingKeyTool { private static SimpleDateFormat yearAndMonth = new SimpleDateFormat("yyyyMM"); private static SimpleDateFormat year = new SimpleDateFormat("yyyy"); public static String getYearAndMonth(long shardingKey){ return yearAndMonth.format(DefaultKeyGenerator.EPOCH+(Long.valueOf(shardingKey+"")>>22)); } public static String getYear(long shardingKey){ return year.format(DefaultKeyGenerator.EPOCH+(Long.valueOf(shardingKey+"")>>22)); } }