Sharding-Sphere初始化(三)

本文深入解析了Sharding-Sphere的路由过程,包括通过StandardRoutingEngine进行数据节点选择,依据分库分表策略进行路由,创建SQLRewriteEngine重写SQL语句,并详细介绍了SQLExecutionUnit的生成及PreparedStatement的执行流程。

9. 执行StandardRoutingEngine#route获取路由结果,获取分表规则以及分库分表对应的字段列,获取分库分表对应的字段对应的具体值,

 public RoutingResult route() {
        TableRule tableRule = shardingRule.getTableRule(logicTableName);
        Collection<String> databaseShardingColumns = shardingRule.getDatabaseShardingStrategy(tableRule).getShardingColumns();
        Collection<String> tableShardingColumns = shardingRule.getTableShardingStrategy(tableRule).getShardingColumns();
        Collection<DataNode> routedDataNodes = new LinkedHashSet<>();
        if (HintManagerHolder.isUseShardingHint()) {
            List<ShardingValue> databaseShardingValues = getDatabaseShardingValuesFromHint(databaseShardingColumns);
            List<ShardingValue> tableShardingValues = getTableShardingValuesFromHint(tableShardingColumns);
            Collection<DataNode> dataNodes = route(tableRule, databaseShardingValues, tableShardingValues);
            for (ShardingCondition each : shardingConditions.getShardingConditions()) {
                if (each instanceof InsertShardingCondition) {
                    ((InsertShardingCondition) each).getDataNodes().addAll(dataNodes);
                }
            }
            routedDataNodes.addAll(dataNodes);
        } else {
            if (shardingConditions.getShardingConditions().isEmpty()) {
                routedDataNodes.addAll(route(tableRule, Collections.<ShardingValue>emptyList(), Collections.<ShardingValue>emptyList()));
            } else {
                for (ShardingCondition each : shardingConditions.getShardingConditions()) {
                    List<ShardingValue> databaseShardingValues = getShardingValues(databaseShardingColumns, each);
                    List<ShardingValue> tableShardingValues = getShardingValues(tableShardingColumns, each);
                    Collection<DataNode> dataNodes = route(tableRule, databaseShardingValues, tableShardingValues);
                    routedDataNodes.addAll(dataNodes);
                    if (each instanceof InsertShardingCondition) {
                        ((InsertShardingCondition) each).getDataNodes().addAll(dataNodes);
                    }
                }
            }
        }
        return generateRoutingResult(routedDataNodes);
    }

路由到对应的数据节点,获取到实际存在的数据库源和表名,再根据xml文件中配置的策略进行选择StandardShardingStrategy#doSharding,PreciseShardingAlgorithm#doSharding选择符合条件的数据节点。

 private Collection<DataNode> route(final TableRule tableRule, final List<ShardingValue> databaseShardingValues, final List<ShardingValue> tableShardingValues) {
        Collection<String> routedDataSources = routeDataSources(tableRule, databaseShardingValues);
        Collection<DataNode> result = new LinkedList<>();
        for (String each : routedDataSources) {
            result.addAll(routeTables(tableRule, each, tableShardingValues));
        }
        return result;
    }

生成最后的路由结果,保存对应的数据库原,保存逻辑表名和实际表名的对应关系。

10. 创建sql语句重写引擎SQLRewriteEngine,重写表名appendTablePlaceholder(result, (TableToken) each, count, sqlTokens);重写()字段列值appendInsertValuesToken(result, (InsertValuesToken) each, count, sqlTokens);

public SQLBuilder rewrite(final boolean isRewriteLimit) {
        SQLBuilder result = new SQLBuilder(parameters);
        if (sqlTokens.isEmpty()) {
            result.appendLiterals(originalSQL);
            return result;
        }
        int count = 0;
        sortByBeginPosition();
        for (SQLToken each : sqlTokens) {
            if (0 == count) {
                result.appendLiterals(originalSQL.substring(0, each.getBeginPosition()));
            }
            if (each instanceof TableToken) {
                appendTablePlaceholder(result, (TableToken) each, count, sqlTokens);
            } else if (each instanceof SchemaToken) {
                appendSchemaPlaceholder(result, (SchemaToken) each, count, sqlTokens);
            } else if (each instanceof IndexToken) {
                appendIndexPlaceholder(result, (IndexToken) each, count, sqlTokens);
            } else if (each instanceof ItemsToken) {
                appendItemsToken(result, (ItemsToken) each, count, sqlTokens);
            } else if (each instanceof InsertValuesToken) {
                appendInsertValuesToken(result, (InsertValuesToken) each, count, sqlTokens);
            } else if (each instanceof RowCountToken) {
                appendLimitRowCount(result, (RowCountToken) each, count, sqlTokens, isRewriteLimit);
            } else if (each instanceof OffsetToken) {
                appendLimitOffsetToken(result, (OffsetToken) each, count, sqlTokens, isRewriteLimit);
            } else if (each instanceof OrderByToken) {
                appendOrderByToken(result, count, sqlTokens);
            } else if (each instanceof InsertColumnToken) {
                appendSymbolToken(result, (InsertColumnToken) each, count, sqlTokens);
            }
            count++;
        }
        return result;
    }

用SQLRewriteEngine#generateSQL组合成真正的sql语句,获取逻辑实际表的对应关系,以及绑定表的关系。用SQLBuilder拼接sql,不属于占位符的直接拼接,根据逻辑表名获取实际表名,拼接表名以及字段名,解析()参数值以及具体参数值,组合成SQLUnit返回,最后把数据源以及sql语句和具体的参数值包装成SQLExecutionUnit放进SQLRouteResult返回。

public SQLUnit toSQL(final TableUnit tableUnit, final Map<String, String> logicAndActualTableMap, final ShardingRule shardingRule) {
        List<Object> insertParameters = new LinkedList<>();
        StringBuilder result = new StringBuilder();
        for (Object each : segments) {
            if (!(each instanceof ShardingPlaceholder)) {
                result.append(each);
                continue;
            }
            String logicTableName = ((ShardingPlaceholder) each).getLogicTableName();
            String actualTableName = logicAndActualTableMap.get(logicTableName);
            if (each instanceof TablePlaceholder) {
                result.append(null == actualTableName ? logicTableName : actualTableName);
            } else if (each instanceof SchemaPlaceholder) {
                SchemaPlaceholder schemaPlaceholder = (SchemaPlaceholder) each;
                Optional<TableRule> tableRule = shardingRule.tryFindTableRuleByActualTable(actualTableName);
                if (!tableRule.isPresent() && Strings.isNullOrEmpty(shardingRule.getShardingDataSourceNames().getDefaultDataSourceName())) {
                    throw new ShardingException("Cannot found schema name '%s' in sharding rule.", schemaPlaceholder.getLogicSchemaName());
                }
                // TODO 目前只能找到真实数据源名称. 未来需要在初始化sharding rule时创建connection,并验证连接是否正确,并获取出真实的schema的名字, 然后在这里替换actualDataSourceName为actualSchemaName
                // TODO 目前actualDataSourceName必须actualSchemaName一样,才能保证替换schema的场景不出错, 如: show columns xxx
                Preconditions.checkState(tableRule.isPresent());
                result.append(tableRule.get().getActualDatasourceNames().iterator().next());
            } else if (each instanceof IndexPlaceholder) {
                IndexPlaceholder indexPlaceholder = (IndexPlaceholder) each;
                result.append(indexPlaceholder.getLogicIndexName());
                if (!Strings.isNullOrEmpty(actualTableName)) {
                    result.append("_");
                    result.append(actualTableName);
                }
            } else if (each instanceof InsertValuesPlaceholder) {
                InsertValuesPlaceholder insertValuesPlaceholder = (InsertValuesPlaceholder) each;
                List<String> expressions = new LinkedList<>();
                for (ShardingCondition shardingCondition : insertValuesPlaceholder.getShardingConditions().getShardingConditions()) {
                    processInsertShardingCondition(tableUnit, (InsertShardingCondition) shardingCondition, expressions, insertParameters);
                }
                int count = 0;
                for (String s : expressions) {
                    if (0 != count) {
                        result.append(", ");
                    }
                    result.append(s);
                    count++;
                }
            } else {
                result.append(each);
            }
        }
        if (insertParameters.isEmpty()) {
            return new SQLUnit(result.toString(), new ArrayList<>(Collections.singleton(parameters)));
        } else {
            return new SQLUnit(result.toString(), new ArrayList<>(Collections.singleton(insertParameters)));
        }
    }

11. 继续回到ShardingPreparedStatement#route,根据数据源获取数据库连接AbstractConnectionAdapter#getConnection,放进连接缓存中cachedConnections.put(dataSourceName, result);,执行钩子方法等WrapperAdapter#replayMethodsInvocation,获取PreparedStatement并保存

 for (SQLExecutionUnit each : routeResult.getExecutionUnits()) {
            PreparedStatement preparedStatement = generatePreparedStatement(each);
            routedStatements.add(preparedStatement);
            replaySetParameter(preparedStatement, each.getSqlUnit().getParameterSets().get(0));
            result.add(new PreparedStatementUnit(each, preparedStatement));
        }

private PreparedStatement generatePreparedStatement(final SQLExecutionUnit sqlExecutionUnit) throws SQLException {
        Connection connection = getConnection().getConnection(sqlExecutionUnit.getDataSource());
        return returnGeneratedKeys ? connection.prepareStatement(sqlExecutionUnit.getSqlUnit().getSql(), Statement.RETURN_GENERATED_KEYS)
                : connection.prepareStatement(sqlExecutionUnit.getSqlUnit().getSql(), resultSetType, resultSetConcurrency, resultSetHoldability);
    }

使用反射方式构造设置参数的方法SetParameterMethodInvocation,执行对应的设置参数的方法invoke,把具体的参数值设置到preparedStatement中,最后返回PreparedStatementUnit。

private void recordSetParameter(final String methodName, final Class[] argumentTypes, final Object... arguments) {
        try {
            setParameterMethodInvocations.add(new SetParameterMethodInvocation(PreparedStatement.class.getMethod(methodName, argumentTypes), arguments, arguments[1]));
        } catch (final NoSuchMethodException ex) {
            throw new ShardingException(ex);
        }
    }
    
    protected void replaySetParameter(final PreparedStatement preparedStatement, final List<Object> parameters) {
        setParameterMethodInvocations.clear();
        addParameters(parameters);
        for (SetParameterMethodInvocation each : setParameterMethodInvocations) {
            each.invoke(preparedStatement);
        }
    }
    
    private void addParameters(final List<Object> parameters) {
        for (int i = 0; i < parameters.size(); i++) {
            recordSetParameter("setObject", new Class[]{int.class, Object.class}, i + 1, parameters.get(i));
        }
    }

12. 把preparedStatement放入线程池中开始执行,执行结果后清理一些本次缓存的数据,批量信息,参数信息等等以及另外一些收尾工作。

public boolean execute() throws SQLException {
        try {
            Collection<PreparedStatementUnit> preparedStatementUnits = route();
            return new PreparedStatementExecutor(
                    getConnection().getShardingContext().getExecutorEngine(), routeResult.getSqlStatement().getType(), preparedStatementUnits).execute();
        } finally {
            JDBCShardingRefreshHandler.build(routeResult, connection).execute();
            clearBatch();
        }
    }

 public boolean execute() throws SQLException {
        List<Boolean> result = executorEngine.execute(sqlType, preparedStatementUnits, new ExecuteCallback<Boolean>() {
            
            @Override
            public Boolean execute(final BaseStatementUnit baseStatementUnit) throws Exception {
                return ((PreparedStatement) baseStatementUnit.getStatement()).execute();
            }
        });
        if (null == result || result.isEmpty() || null == result.get(0)) {
            return false;
        }
        return result.get(0);
    }

ExecutorEngine#execute,取出第一个执行单元同步执行,然后异步执行其他的执行单元,最后把所有的结果包装成ListenableFuture返回,至此,insert语句就解析完毕了。

public <T> List<T> execute(
            final SQLType sqlType, final Collection<? extends BaseStatementUnit> baseStatementUnits, final ExecuteCallback<T> executeCallback) throws SQLException {
        if (baseStatementUnits.isEmpty()) {
            return Collections.emptyList();
        }
        OverallExecutionEvent event = new OverallExecutionEvent(sqlType, baseStatementUnits.size());
        EventBusInstance.getInstance().post(event);
        Iterator<? extends BaseStatementUnit> iterator = baseStatementUnits.iterator();
        BaseStatementUnit firstInput = iterator.next();
        ListenableFuture<List<T>> restFutures = asyncExecute(sqlType, Lists.newArrayList(iterator), executeCallback);
        T firstOutput;
        List<T> restOutputs;
        try {
            firstOutput = syncExecute(sqlType, firstInput, executeCallback);
            restOutputs = restFutures.get();
            // CHECKSTYLE:OFF
        } catch (final Exception ex) {
            // CHECKSTYLE:ON
            event.setException(ex);
            event.setEventExecutionType(EventExecutionType.EXECUTE_FAILURE);
            EventBusInstance.getInstance().post(event);
            ExecutorExceptionHandler.handleException(ex);
            return null;
        }
        event.setEventExecutionType(EventExecutionType.EXECUTE_SUCCESS);
        EventBusInstance.getInstance().post(event);
        List<T> result = Lists.newLinkedList(restOutputs);
        result.add(0, firstOutput);
        return result;
    }


 private <T> T executeInternal(final SQLType sqlType, final BaseStatementUnit baseStatementUnit, final ExecuteCallback<T> executeCallback,
                                  final boolean isExceptionThrown, final Map<String, Object> dataMap) throws Exception {
        synchronized (baseStatementUnit.getStatement().getConnection()) {
            T result;
            ExecutorExceptionHandler.setExceptionThrown(isExceptionThrown);
            ExecutorDataMap.setDataMap(dataMap);
            List<AbstractExecutionEvent> events = new LinkedList<>();
            for (List<Object> each : baseStatementUnit.getSqlExecutionUnit().getSqlUnit().getParameterSets()) {
                events.add(getExecutionEvent(sqlType, baseStatementUnit, each));
            }
            for (AbstractExecutionEvent event : events) {
                EventBusInstance.getInstance().post(event);
            }
            try {
                result = executeCallback.execute(baseStatementUnit);
            } catch (final SQLException ex) {
                for (AbstractExecutionEvent each : events) {
                    each.setEventExecutionType(EventExecutionType.EXECUTE_FAILURE);
                    each.setException(ex);
                    EventBusInstance.getInstance().post(each);
                    ExecutorExceptionHandler.handleException(ex);
                }
                return null;
            }
            for (AbstractExecutionEvent each : events) {
                each.setEventExecutionType(EventExecutionType.EXECUTE_SUCCESS);
                EventBusInstance.getInstance().post(each);
            }
            return result;
        }
    }
    
    private AbstractExecutionEvent getExecutionEvent(final SQLType sqlType, final BaseStatementUnit baseStatementUnit, final List<Object> parameters) {
        AbstractExecutionEvent result;
        if (SQLType.DQL == sqlType) {
            result = new DQLExecutionEvent(baseStatementUnit.getSqlExecutionUnit().getDataSource(), baseStatementUnit.getSqlExecutionUnit().getSqlUnit(), parameters);
        } else {
            result = new DMLExecutionEvent(baseStatementUnit.getSqlExecutionUnit().getDataSource(), baseStatementUnit.getSqlExecutionUnit().getSqlUnit(), parameters);
        }
        return result;
    }

 

server: port: 51809 spring: application: name: coupon-send-service sharding-sphere: datasource: names: shard0,shard1 shard0: type: com.alibaba.druid.pool.DruidDataSource driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://localhost:3306/sk_day09?useSSL=false&serverTimezone=UTC&rewriteBatchedStatements=true username: root password: 123456 # Druid连接池配置 initial-size: 5 min-idle: 5 max-active: 20 shard1: type: com.alibaba.druid.pool.DruidDataSource driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://localhost:3306/sk_day10?useSSL=false&serverTimezone=UTC&rewriteBatchedStatements=true username: root password: 123456 # Druid连接池配置 initial-size: 5 min-idle: 5 max-active: 20 sharding: tables: task_user: actual-data-nodes: shard$->{0..1}.task_user database-strategy: inline: sharding-column: user_id algorithm-expression: shard$->{user_id % 2} props: sql: show: true batch: initialize-schema: always job: enabled: false redis: host: localhost port: 6379 password: 123456 database: 0 cloud: alibaba: seata: tx-service-group: coupon-send-service-group mybatis-plus: configuration: map-underscore-to-camel-case: true xxl: job: admin: addresses: http://127.0.0.1:8080/xxl-job-admin # 地址填我们的调度中心的地址 executor: appname: xxl-job-executor-batch-send-coupon # 这个名称可以自定义,称为执行器的名称 address: ip: port: 9999 # 端口要填,为了调度中心调度使用 logpath: ./logs/xxl-job-executor-sample logretentiondays: 30 seata: application-id: ${spring.application.name} tx-service-group: coupon-send-service-group service: vgroup-mapping: coupon-send-service-group: default grouplist: default: 127.0.0.1:8091 config: type: file registry: type: file job: data: path: E:/spring-batch-example/
09-04
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值