zoukankan      html  css  js  c++  java
  • sharding sphere 4.0.0-RC1版本 按年分表实战

    1. sharding sphere 4.0.0-RC1版本 按年分表实战

    1.1. 需求

    需要对日志表进行按时间划分表,由于用于后台系统,日志量预估不会太大,因此按年划分表

    经过我不断的查阅sharding sphere资料和实践,我最后还是决定先建表,再把actual-data-nodes表结点给定下来,为什么这么说?

    我纠结的是到底要不要动态创建表,若想要不自己手动每隔几年维护表,我们当然希望能自动创建。但经过我的实践,sharding sphere本身没有提供该功能,但可以通过分片算法实现类中自定义实现,但前提是我们要随时知道要分片表有几个分片,比如log_2019,log_2020,log_2021,只要我能初始化的时候知道分片有几个表以及表名,那么我就不会查询到不存在的表导致报错,反之则容易报错

    我们知道mysql可以通过查询information_schema.TABLES来查询存在的表,但是不知道是不是sharding sphere的bug,我用库名加表名查该库它会强制给我改写成我默认的连接库,导致表不存在,根本查不到

    所以我退而求其次,下面我列出我的方案,方案采用的版本是4.0.0-RC1

    1.2. 引入pom

    1. 先把pom列出来,只给代码不给pom都是耍流氓
             <!-- 分库分表 -->
            <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>
    

    1.3. application.yml配置

    1. 如下配置,分表最重要的是table-strategy分表策略,sharding-column表示分表字段,当插入查询需要指定哪个分表时,必须带上这个条件,否则可能出错,actual-data-nodes表示你分了哪些表,它有一定语法,如下$->{0..1}表示system_log_2020,system_log_2021两张表,我需要在mysql建好这两张表
    spring:
      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:mysql://xxxxx:3306/test?useUnicode=true&characterEncoding=utf-8&serverTimezone=GMT%2B8&allowMultiQueries=true
            username: xxx
            password: xxx
        sharding:
          tables:
            system_log:
              actual-data-nodes: ds0.system_log_202$->{0..1}
              table-strategy:
                standard:
                  sharding-column: created
                  precise-algorithm-class-name: com.xxx.platform.system.log.LogShardingAlgorithm
                  range-algorithm-class-name: com.xxx.platform.system.log.LogShardingAlgorithm
    

    1.4. 分表策略

    1. 最重要的就是LogShardingAlgorithm这个类
    import com.google.common.collect.Range;
    import lombok.extern.slf4j.Slf4j;
    import org.apache.shardingsphere.api.sharding.standard.PreciseShardingAlgorithm;
    import org.apache.shardingsphere.api.sharding.standard.PreciseShardingValue;
    import org.apache.shardingsphere.api.sharding.standard.RangeShardingAlgorithm;
    import org.apache.shardingsphere.api.sharding.standard.RangeShardingValue;
    
    import java.util.ArrayList;
    import java.util.Collection;
    
    /**
     * @author: laoliangliang
     * @description: 日志分片
     * @create: 2020/1/2 10:19
     **/
    @Slf4j
    public class LogShardingAlgorithm implements PreciseShardingAlgorithm, RangeShardingAlgorithm<Integer> {
    
        @Override
        public String doSharding(Collection availableTargetNames, PreciseShardingValue shardingValue) {
            String target = shardingValue.getValue().toString();
            return shardingValue.getLogicTableName() + "_" + target.substring(target.lastIndexOf("_") + 1, target.lastIndexOf("_") + 5);
        }
    
        @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(target.lastIndexOf("_") + 1, target.lastIndexOf("_") + 5));
                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;
        }
    }
    
    1. 我实现了PreciseShardingAlgorithm, RangeShardingAlgorithm这两个接口,分别表示当created条件为=between时会分别进入这两个方法,用来判断sql语句命中哪个表
    2. 这里要注意,created的><大于小于判断是不起效果的,求范围只能用between,如果我说错了请提醒哦
    3. 接下来调用sql语句我是这样写的
    SELECT created,user_name,`action`,id FROM system_log
    <where>
        <if test="id!=null and id!=''">
            and pk_id=#{id}
        </if>
        <if test="startTime != null and endTime != null">
            and created BETWEEN #{startTime} and #{endTime}
        </if>
    </where>
    order by created desc
    

    1.5. 结果

    1. mybatis插入后日志如下,可以看到mybatis打印的日志表名还是system_log,但实际对应数据库有system_log_2020,system_log_2021两张表,我插入的时间是2020年,所以只插入2020的表
    2020-01-07 16:40:28.165 DEBUG 7780 --- [pool-4-thread-1] c.o.p.p.m.S.insertSelective              : ==>  Preparing: INSERT INTO system_log ( type,pk_id,remark,user_name,created,action ) VALUES( ?,?,?,?,?,? ) 
    2020-01-07 16:40:28.165 DEBUG 7780 --- [pool-4-thread-1] c.o.p.p.m.S.insertSelective              : ==> Parameters: 1(Integer), 0(Integer), string(String), 15162191629(String), 2020-01-07 16:40:28.161(Timestamp), 内容(String)
    2020-01-07 16:40:28.198  INFO 7780 --- [pool-4-thread-1] ShardingSphere-SQL                       : Rule Type: sharding
    2020-01-07 16:40:28.198  INFO 7780 --- [pool-4-thread-1] ShardingSphere-SQL                       : Logic SQL: INSERT INTO system_log  ( type,pk_id,remark,user_name,created,action ) VALUES( ?,?,?,?,?,? )
    2020-01-07 16:40:28.198  INFO 7780 --- [pool-4-thread-1] ShardingSphere-SQL                       : SQLStatement: InsertStatement(super=DMLStatement(super=AbstractSQLStatement(type=DML, tables=Tables(tables=[Table(name=system_log, alias=Optional.absent())]), routeConditions=Conditions(orCondition=OrCondition(andConditions=[AndCondition(conditions=[Condition(column=Column(name=created, tableName=system_log), operator=EQUAL, compareOperator=null, positionValueMap={}, positionIndexMap={0=4})])])), encryptConditions=Conditions(orCondition=OrCondition(andConditions=[])), sqlTokens=[TableToken(tableName=system_log, quoteCharacter=NONE, schemaNameLength=0), SQLToken(startIndex=24)], parametersIndex=6, logicSQL=INSERT INTO system_log  ( type,pk_id,remark,user_name,created,action ) VALUES( ?,?,?,?,?,? )), deleteStatement=false, updateTableAlias={}, updateColumnValues={}, whereStartIndex=0, whereStopIndex=0, whereParameterStartIndex=0, whereParameterEndIndex=0), columnNames=[type, pk_id, remark, user_name, created, action], values=[InsertValue(columnValues=[org.apache.shardingsphere.core.parse.old.parser.expression.SQLPlaceholderExpression@21625d01, org.apache.shardingsphere.core.parse.old.parser.expression.SQLPlaceholderExpression@34dda176, org.apache.shardingsphere.core.parse.old.parser.expression.SQLPlaceholderExpression@5d631384, org.apache.shardingsphere.core.parse.old.parser.expression.SQLPlaceholderExpression@13cfbf64, org.apache.shardingsphere.core.parse.old.parser.expression.SQLPlaceholderExpression@20f67249, org.apache.shardingsphere.core.parse.old.parser.expression.SQLPlaceholderExpression@79f9b130])])
    2020-01-07 16:40:28.198  INFO 7780 --- [pool-4-thread-1] ShardingSphere-SQL                       : Actual SQL: ds0 ::: INSERT INTO system_log_2020   (type, pk_id, remark, user_name, created, action) VALUES (?, ?, ?, ?, ?, ?) ::: [1, 0, string, 15162191629, 2020-01-07 16:40:28.161, 内容]
    2020-01-07 16:40:28.210 DEBUG 7780 --- [pool-4-thread-1] c.o.p.p.m.S.insertSelective              : <==    Updates: 1
    
    1. 如上的查询语句结果也同理,只查2020年

    查询参数

    {
      "endTime": "2020-01-10 01:01:01",
      "id": 435,
      "page": 1,
      "pageSize": 10,
      "startTime": "2020-01-01 01:01:01"
    }
    

    查询结果

    2020-01-07 16:50:49.878 DEBUG 5408 --- [nio-9000-exec-2] c.o.p.p.m.S.getReportLogList             : ==>  Preparing: SELECT created,user_name,`action`,id,remark FROM system_log WHERE pk_id=? and created BETWEEN ? and ? order by created desc LIMIT ? 
    2020-01-07 16:50:49.879 DEBUG 5408 --- [nio-9000-exec-2] c.o.p.p.m.S.getReportLogList             : ==> Parameters: 435(Integer), 2020-01-01 01:01:01.0(Timestamp), 2020-01-10 01:01:01.0(Timestamp), 10(Integer)
    2020-01-07 16:50:49.891  INFO 5408 --- [nio-9000-exec-2] ShardingSphere-SQL                       : Rule Type: sharding
    2020-01-07 16:50:49.891  INFO 5408 --- [nio-9000-exec-2] ShardingSphere-SQL                       : Logic SQL: SELECT created,user_name,`action`,id,remark FROM system_log
             WHERE  pk_id=?
                
                
                    and created BETWEEN ? and ? 
            order by created desc LIMIT ? 
    2020-01-07 16:50:49.891  INFO 5408 --- [nio-9000-exec-2] ShardingSphere-SQL                       : SQLStatement: SelectStatement(super=DQLStatement(super=AbstractSQLStatement(type=DQL, tables=Tables(tables=[Table(name=system_log, alias=Optional.absent())]), routeConditions=Conditions(orCondition=OrCondition(andConditions=[AndCondition(conditions=[Condition(column=Column(name=created, tableName=system_log), operator=BETWEEN, compareOperator=null, positionValueMap={}, positionIndexMap={0=1, 1=2})])])), encryptConditions=Conditions(orCondition=OrCondition(andConditions=[])), sqlTokens=[TableToken(tableName=system_log, quoteCharacter=NONE, schemaNameLength=0)], parametersIndex=4, logicSQL=SELECT created,user_name,`action`,id,remark FROM system_log
             WHERE  pk_id=?
                
                
                    and created BETWEEN ? and ? 
            order by created desc LIMIT ? )), containStar=false, firstSelectItemStartIndex=7, selectListStopIndex=42, groupByLastIndex=0, items=[CommonSelectItem(expression=created, alias=Optional.absent()), CommonSelectItem(expression=user_name, alias=Optional.absent()), CommonSelectItem(expression=action, alias=Optional.absent()), CommonSelectItem(expression=id, alias=Optional.absent()), CommonSelectItem(expression=remark, alias=Optional.absent())], groupByItems=[], orderByItems=[OrderItem(owner=Optional.absent(), name=Optional.of(created), orderDirection=DESC, nullOrderDirection=ASC, index=-1, expression=null, alias=Optional.absent())], limit=Limit(offset=null, rowCount=LimitValue(value=-1, index=3, boundOpened=false)), subqueryStatement=null, subqueryStatements=[], subqueryConditions=[])
    2020-01-07 16:50:49.891  INFO 5408 --- [nio-9000-exec-2] ShardingSphere-SQL                       : Actual SQL: ds0 ::: SELECT created,user_name,`action`,id,remark FROM system_log_2020
             WHERE  pk_id=?
                
                
                    and created BETWEEN ? and ? 
            order by created desc LIMIT ?  ::: [435, 2020-01-01 01:01:01.0, 2020-01-10 01:01:01.0, 10]
    2020-01-07 16:50:49.898 DEBUG 5408 --- [nio-9000-exec-2] c.o.p.p.m.S.getReportLogList             : <==      Total: 2
    

    1.6. 总结

    这次主要的碰壁内容就是created的大于小于问题,大于小于触发不了表分片行为,需要特别注意。希望对你有帮助
    老梁讲Java

    欢迎关注公众号,一起学习进步

  • 相关阅读:
    html页面3秒后自动跳转的方法有哪些
    web前端教程之javascript创建对象的方法
    想学好web前端,需要看哪些书籍
    码农只能干到30岁的说法可信吗?
    不管你以后写不写JS,都应该学会这种思考方式
    Python基础知识之排序法
    input和textarea修改placeholder颜色和字号
    关于IE和Firefox兼容性问题及解决办法
    vue-cli构建vue项目
    js 小数取整的函数
  • 原文地址:https://www.cnblogs.com/sky-chen/p/12162587.html
Copyright © 2011-2022 走看看