sharding-jdbc 实现动态分表(按年按月)

1、 项目中我们希望 能够按照时间、类别来添加表。但是sharding-jdbc 是固定配置 的 actual-data-nodes 参数。

也就是说我们需要提前创建好分表或者分库。那么我们需要如何来实现动态创建表,并且动态刷新 actual-data-nodes 呢。

2、思路 就是 写个定时器来动态创建表 ,在创建表的时候 动态刷新 actual-data-nodes 实现动态创建表被shard加载。注意的是,我的任务 采用xxl-job (主要是我们的项目是分布式项目)你们如果是单体也可以直接使用架构自带的定时任务器。

3、不多废话直接上代码。(备注: 这里实现的方式 动态刷新 只能刷新单机,如果是集群部署,需要自己优化,具体的思路是 就是消息中间件的 topic 模式 订阅消费 调用 actualTablesRefresh方法刷新所有部署实例 )

maven 依赖

sharding-jdbc 依赖

<dependency>
    <groupId>org.apache.shardingsphere</groupId>
    <artifactId>sharding-jdbc-spring-boot-starter</artifactId>
    <version>4.0.0-RC1</version>
</dependency>
<dependency>
    <groupId>org.apache.shardingsphere</groupId>
    <artifactId>sharding-jdbc-spring-namespace</artifactId>
    <version>4.0.0-RC1</version>
</dependency>
## 任务管理器  XXl-job
<dependency>
    <groupId>com.xuxueli</groupId>
    <artifactId>xxl-job-core</artifactId>
    <version>2.3.0-SNAPSHOT</version>
</dependency>

yml 配置:

spring: 
### 处理连接池冲突 #####
  main:
    allow-bean-definition-overriding: true
###   这里采用的按年分表策略  采用一个库 扩展表的方式 多库同理 #####
shardingsphere:
  props:
    sql:
      show: true
  datasource:
    names: ds0
    ###  主数据库 ###
    ds0:
      type: com.alibaba.druid.pool.DruidDataSource
      driver-class-name: com.mysql.cj.jdbc.Driver
      url: jdbc:${DATASOURCE_DBTYPE:mysql}://${DATASOURCE_HOST:127.0.0.1}:${DATASOURCE_PORT:3306}/sys_logs?characterEncoding=UTF-8&useUnicode=true&useSSL=false
      username: ${DATASOURCE_USERNAME:root}
      password: ${DATASOURCE_PASSWORD:123456}
  sharding:
    tables:
      wc_passenger:
        ### 数据表 按年分表 ####   流量
        actual-data-nodes: ds0.sys_log
        table-strategy:
          standard:
            sharding-column: created_time
            precise-algorithm-class-name: com.xxx.DeviceShardingAlgorithmConfig
            range-algorithm-class-name: com.xxx.DeviceShardingAlgorithmConfig
###  动态创建表的表名称 创建时间开始年 #########
db:
  table:
    names: sys_log
    startYear: 2021

4、 按年建表(按月同理) 入库分表策略

定时器任务是每年最后一天创建下一年的表。(可根据自己的需求自定义)

入库分表策略

public class LogShardingConfig implements PreciseShardingAlgorithm, RangeShardingAlgorithm<Integer> {
 
    @Override
    public String doSharding(Collection availableTargetNames, PreciseShardingValue shardingValue) {
        String target = shardingValue.getValue().toString();
        return shardingValue.getLogicTableName() + "_" + target.substring(0,4);
    }
 
    @Override
    public Collection<String> doSharding(Collection<String> availableTargetNames, RangeShardingValue<Integer> shardingValue) {
        Collection<String> availables = new ArrayList<>();
        Range valueRange = shardingValue.getValueRange();
        for (String target : availableTargetNames) {
            Integer shardValue = Integer.parseInt(target.substring(0,4));
            if (valueRange.hasLowerBound()) {
                String lowerStr = valueRange.lowerEndpoint().toString();
                Integer start = Integer.parseInt(lowerStr.substring(0, 4));
                if (start - shardValue > 0) {
                    continue;
                }
            }
            if (valueRange.hasUpperBound()) {
                String upperStr = valueRange.upperEndpoint().toString();
                Integer end = Integer.parseInt(upperStr.substring(0, 4));
                if (end - shardValue < 0) {
                    continue;
                }
            }
            availables.add(target);
        }
        return availables;
    }
}

5、 初始化 其实时间配置

@Component
@ConfigurationProperties(prefix = "db.table")
@Data
public class TableNamesConfig {
    String[] names;
    // 起始年 默认 2021
    int startYear;
}

6、动态创建加载表

/**
 * @author Administrator
 * @title: lj
 * @projectName mybatis-plus-sample-generator
 * @description: TODO
 * @date 2021/1/13 001316:58
 */
@Log4j2
@Component
@EnableConfigurationProperties(TableNamesConfig.class)
public class LogHandler {
 
    @Autowired
    private TableNamesConfig tableNamesConfig;
 
    @Autowired
    private DataSource dataSource;
    
    @Autowired
    private SysLogMapper sysLogMapper;
 
    /**
     * 开启拉取任务(Bean模式) 获取狗信息   以 年为单位创建 硬件上报数据表
     */
    @XxlJob("createWcTableJobHandler")
    public void CreateWcTableJobHandler() throws Exception {
        XxlJobHelper.log("XXL-JOB, CreateWcTableJobHandler.");
        Date date =new Date();
        SimpleDateFormat format =  new SimpleDateFormat("yyyy");
        String s =  format.format(date);
        Integer data = Integer.parseInt(s) +1;
        String name;
 
            name = "sys_log_" + data;
            boolean flag = true;
            // 判断表是否存在 不存在则添加表  
            try{
                sysLogMapper.selectTable(name);
                flag =false;
            }catch (Exception e){
                flag = true;
            }
            try {
                if (flag) {
                     // 这里创建表 建立采用   CREATE TABLE xx like sys_log 减少维护量
                    SysLogMapper.createTableCard(name);
                }
            }catch (Exception e){
                e.printStackTrace();
            }
        // 这里特别重要  创建成功之后 刷新 actual-data-nodes
        actualTablesRefresh(data);
    }
 
    /***
     * 启动的时候初始化一下 动态表单的配置    初化当前年
     */
    @PostConstruct
    private  void intData(){
        Date date =new Date();
        SimpleDateFormat format =  new SimpleDateFormat("yyyy");
        String s =  format.format(date);
        actualTablesRefresh(Integer.parseInt(s));
    }
 
    /**
     *  动态更新 处理化的表配置
     * @param data   表名称
     */
    public void actualTablesRefresh(Integer data)  {
        try {
            System.out.println("---------------------------------");
            ShardingDataSource dataSource = (ShardingDataSource) this.dataSource;
            if (tableNamesConfig.getNames() == null || tableNamesConfig.getNames().length == 0) {
                log.info("dynamic.table.names为空");
                return;
            }
            for (int i = 0; i < tableNamesConfig.getNames().length; i++) {
                TableRule tableRule = null;
                try {
                    tableRule = dataSource.getShardingContext().getShardingRule().getTableRule(tableNamesConfig.getNames()[i]);
                } catch (ShardingConfigurationException e) {
                    log.error("报错啦" +   e.getMessage());
                   
                }
                List<DataNode> dataNodes = tableRule.getActualDataNodes();
                Field actualDataNodesField = TableRule.class.getDeclaredField("actualDataNodes");
                Field modifiersField = Field.class.getDeclaredField("modifiers");
                modifiersField.setAccessible(true);
                modifiersField.setInt(actualDataNodesField, actualDataNodesField.getModifiers() & ~Modifier.FINAL);
                actualDataNodesField.setAccessible(true);
                List<DataNode> newDataNodes = new ArrayList<>();
                int time = tableNamesConfig.getStartYear();
                String dataSourceName = dataNodes.get(0).getDataSourceName();
                while (true) {
                    DataNode dataNode = new DataNode(dataSourceName + "." + tableNamesConfig.getNames()[i]+ "_" + time);
                    newDataNodes.add(dataNode);
                    time = time + 1;
                    if (time > data.intValue()) {
                        break;
                    }
                }
                actualDataNodesField.set(tableRule, newDataNodes);
            }
        }catch (Exception e){
            e.printStackTrace();
            log.info("初始化 动态表单失败" + e.getMessage());
        }
    }
}
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 217,185评论 6 503
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,652评论 3 393
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 163,524评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,339评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,387评论 6 391
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,287评论 1 301
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,130评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,985评论 0 275
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,420评论 1 313
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,617评论 3 334
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,779评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,477评论 5 345
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,088评论 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,716评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,857评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,876评论 2 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,700评论 2 354

推荐阅读更多精彩内容