当前位置: 首页 > article >正文

【源码】Sharding-JDBC源码分析之SQL中影子库ShadowSQLRouter路由的原理

 Sharding-JDBC系列

1、Sharding-JDBC分库分表的基本使用

2、Sharding-JDBC分库分表之SpringBoot分片策略

3、Sharding-JDBC分库分表之SpringBoot主从配置

4、SpringBoot集成Sharding-JDBC-5.3.0分库分表

5、SpringBoot集成Sharding-JDBC-5.3.0实现按月动态建表分表

6、【源码】Sharding-JDBC源码分析之JDBC

7、【源码】Sharding-JDBC源码分析之SPI机制

8、【源码】Sharding-JDBC源码分析之Yaml分片配置文件解析原理

9、【源码】Sharding-JDBC源码分析之Yaml分片配置原理(一)

10、【源码】Sharding-JDBC源码分析之Yaml分片配置原理(二)

11、【源码】Sharding-JDBC源码分析之Yaml分片配置转换原理

12、【源码】Sharding-JDBC源码分析之ShardingSphereDataSource的创建原理

13、【源码】Sharding-JDBC源码分析之ContextManager创建中mode分片配置信息的持久化存储的原理

14、【源码】Sharding-JDBC源码分析之ContextManager创建中ShardingSphereDatabase的创建原理

15、【源码】Sharding-JDBC源码分析之分片规则生成器DatabaseRuleBuilder实现规则配置到规则对象的生成原理

16、【源码】Sharding-JDBC源码分析之配置数据库定义的表的元数据解析原理

17、【源码】Sharding-JDBC源码分析之ShardingSphereConnection的创建原理

18、【源码】Sharding-JDBC源码分析之ShardingSpherePreparedStatement的创建原理

19、【源码】Sharding-JDBC源码分析之Sql解析的原理

20、【源码】Sharding-JDBC源码分析之SQL路由及SingleSQLRouter单表路由

21、【源码】Sharding-JDBC源码分析之SQL中分片键路由ShardingSQLRouter的原理

22、【源码】Sharding-JDBC源码分析之SQL中读写分离路由ReadwriteSplittingSQLRouter的原理

23、 【源码】Sharding-JDBC源码分析之SQL中读写分离动态策略、数据库发现规则及DatabaseDiscoverySQLRouter路由的原理

24、【源码】Sharding-JDBC源码分析之SQL中影子库ShadowSQLRouter路由的原理

前言

ShardingSphere 影子库是一个在数据库层面解决全链路在线压测问题的有效工具。影子库是实际中使用的数据库的完整数据拷贝,用于接收测试数据,以防止测试数据污染生产数据库。影子库应与正式的生产库保持相同的配置,以确保测试结果的准确性。

在正式环境中进行全链路压测时,使用影子库可以隔离测试数据,可以模拟生产环境,进行各种测试,而不会对生产数据库造成任何影响。

本篇从源码的角度,分析 ShardingSphere 中影子库路由的实现原理,其对应的路由器对象为ShadowSQLRouter。

ShardingSpherePreparedStatement回顾

在【源码】Sharding-JDBC源码分析之SQL路由及SingleSQLRouter单表路由-CSDN博客中分析在执行SQL语句前,会进行SQL路由,创建RouteContext对象,在RouteContext路由上下文对象中,包含了SQL真正执行的数据源、逻辑表及真实表的映射。

SQL路由时,循环执行配置的路由器,进行路由上下文RouteContext对象的创建或装饰。如果配置了影子库,将会在路由器集合中最后执行影子库路由器 ShadowSQLRouter。

ShadowSQLRouter

ShadowSQLRouter的源码如下:

package org.apache.shardingsphere.shadow.route;

/**
 * SQL影子库路由器
 */
public final class ShadowSQLRouter implements SQLRouter<ShadowRule> {
    
    @Override
    public RouteContext createRouteContext(final QueryContext queryContext, final ShardingSphereDatabase database,
                                           final ShadowRule rule, final ConfigurationProperties props, final ConnectionContext connectionContext) {
        // TODO
        return new RouteContext();
    }

    /**
     * 装饰路由上下文。根据 SQL 语句类型,创建一个ShadowRouteEngine,执行ShadowRouteEngine.route()方法
     * @param routeContext 路由上下文
     * @param queryContext 查询上下文
     * @param database 数据库信息
     * @param rule 影子库规则对象
     * @param props 配置的属性
     * @param connectionContext 连接上下文
     */
    @Override
    public void decorateRouteContext(final RouteContext routeContext, final QueryContext queryContext, final ShardingSphereDatabase database,
                                     final ShadowRule rule, final ConfigurationProperties props, final ConnectionContext connectionContext) {
        ShadowRouteEngineFactory.newInstance(queryContext).route(routeContext, rule);
    }
    
    @Override
    public int getOrder() {
        return ShadowOrder.ORDER;
    }
    
    @Override
    public Class<ShadowRule> getTypeClass() {
        return ShadowRule.class;
    }
}

ShadowSQLRouter的源码很简单,通过ShadowRouteEngineFactory的newInstance()获取一个ShadowRouteEngine对象,执行ShadowRouteEngine的route()方法。

ShadowRouteEngineFactory

ShadowRouteEngineFactory的源码如下:

package org.apache.shardingsphere.shadow.route.engine;

/**
 * 影子路由引擎工厂
 */
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public final class ShadowRouteEngineFactory {

    /**
     * 创建影子路由引擎。不同的SQL语句,使用不同的处理方式
     * @param queryContext
     * @return
     */
    public static ShadowRouteEngine newInstance(final QueryContext queryContext) {
        SQLStatement sqlStatement = queryContext.getSqlStatementContext().getSqlStatement();
        // 插入语句
        if (sqlStatement instanceof InsertStatement) {
            return createShadowInsertStatementRoutingEngine(queryContext);
        }
        // 删除语句
        if (sqlStatement instanceof DeleteStatement) {
            return createShadowDeleteStatementRoutingEngine(queryContext);
        }
        // 修改语句
        if (sqlStatement instanceof UpdateStatement) {
            return createShadowUpdateStatementRoutingEngine(queryContext);
        }
        // 选择语句
        if (sqlStatement instanceof SelectStatement) {
            return createShadowSelectStatementRoutingEngine(queryContext);
        }
        // 其他语句
        return createShadowNonMDLStatementRoutingEngine(queryContext);
    }
    
    private static ShadowRouteEngine createShadowNonMDLStatementRoutingEngine(final QueryContext queryContext) {
        return new ShadowNonDMLStatementRoutingEngine(queryContext.getSqlStatementContext());
    }
    
    private static ShadowRouteEngine createShadowSelectStatementRoutingEngine(final QueryContext queryContext) {
        return new ShadowSelectStatementRoutingEngine((SelectStatementContext) queryContext.getSqlStatementContext(), queryContext.getParameters());
    }
    
    private static ShadowRouteEngine createShadowUpdateStatementRoutingEngine(final QueryContext queryContext) {
        return new ShadowUpdateStatementRoutingEngine((UpdateStatementContext) queryContext.getSqlStatementContext(), queryContext.getParameters());
    }
    
    private static ShadowRouteEngine createShadowDeleteStatementRoutingEngine(final QueryContext queryContext) {
        return new ShadowDeleteStatementRoutingEngine((DeleteStatementContext) queryContext.getSqlStatementContext(), queryContext.getParameters());
    }

    /**
     * 传入插入语句的路由引擎
     * @param queryContext
     * @return
     */
    private static ShadowRouteEngine createShadowInsertStatementRoutingEngine(final QueryContext queryContext) {
        return new ShadowInsertStatementRoutingEngine((InsertStatementContext) queryContext.getSqlStatementContext());
    }
}

在newInstance()方法中,根据当前SQL语句的操作类型,创建不同的ShadowRouteEngine影子路由引擎对象。如针对插入语句,创建ShadowInsertStatementRoutingEngine路由引擎。

ShadowInsertStatementRoutingEngine

ShadowInsertStatementRoutingEngine的源码如下:

package org.apache.shardingsphere.shadow.route.engine.dml;

/**
 * 插入语句影子路由引擎
 */
@RequiredArgsConstructor
public final class ShadowInsertStatementRoutingEngine extends AbstractShadowDMLStatementRouteEngine {

    // 插入语句上下文
    private final InsertStatementContext insertStatementContext;

    /**
     * 插入语句中涉及的表
     * @return
     */
    @Override
    protected Collection<SimpleTableSegment> getAllTables() {
        return insertStatementContext.getAllTables();
    }
    
    @Override
    protected ShadowOperationType getShadowOperationType() {
        return ShadowOperationType.INSERT;
    }

    /**
     * 获取插入语句中的注释段信息
     * @return
     */
    @Override
    protected Optional<Collection<String>> parseSQLComments() {
        Collection<String> result = new LinkedList<>();
        insertStatementContext.getSqlStatement().getCommentSegments().forEach(each -> result.add(each.getText()));
        return result.isEmpty() ? Optional.empty() : Optional.of(result);
    }
    
    @Override
    protected Iterator<Optional<ShadowColumnCondition>> getShadowColumnConditionIterator(final String shadowColumn) {
        return new ShadowColumnConditionIterator(shadowColumn, parseColumnNames().iterator(), insertStatementContext.getInsertValueContexts());
    }

    /**
     * 解析插入语句中的参数名称
     * @return
     */
    private Collection<String> parseColumnNames() {
        return insertStatementContext.getInsertColumnNames();
    }

    /**
     * 影子列条件迭代器
     */
    private class ShadowColumnConditionIterator implements Iterator<Optional<ShadowColumnCondition>> {
        
        private int index;

        // 影子列
        private final String shadowColumn;

        // 插入语句中的列名迭代器
        private final Iterator<String> iterator;

        // 插入值上下文集合。每个插入值上下文中包含单条记录的参数信息
        private final List<InsertValueContext> insertValueContexts;
        
        ShadowColumnConditionIterator(final String shadowColumn, final Iterator<String> iterator, final List<InsertValueContext> insertValueContexts) {
            index = 0;
            this.shadowColumn = shadowColumn;
            this.iterator = iterator;
            this.insertValueContexts = insertValueContexts;
        }
        
        @Override
        public boolean hasNext() {
            return iterator.hasNext();
        }

        /**
         * 遍历下一个参数,如果不是影子列,返回空
         * @return
         */
        @Override
        public Optional<ShadowColumnCondition> next() {
            String columnName = iterator.next();
            // 不是影子列,返回空
            if (!shadowColumn.equals(columnName)) {
                index++;
                return Optional.empty();
            }
            // 如果是影子列
            Optional<Collection<Comparable<?>>> columnValues = getColumnValues(insertValueContexts, index);
            index++;
            return columnValues.map(each -> new ShadowColumnCondition(getSingleTableName(), columnName, each));
        }

        /**
         * 获取列的值
         * @param insertValueContexts
         * @param columnIndex 当前投影列在列名集合中的下标
         * @return
         */
        private Optional<Collection<Comparable<?>>> getColumnValues(final List<InsertValueContext> insertValueContexts, final int columnIndex) {
            Collection<Comparable<?>> result = new LinkedList<>();
            for (InsertValueContext each : insertValueContexts) {
                // 获取参数值或参数在sql语句中的下标
                Object valueObject = each.getLiteralValue(columnIndex).orElseThrow(() -> new UnsupportedShadowInsertValueException(columnIndex));
                if (valueObject instanceof Comparable<?>) {
                    result.add((Comparable<?>) valueObject);
                } else {
                    return Optional.empty();
                }
            }
            return result.isEmpty() ? Optional.empty() : Optional.of(result);
        }
    }
}

ShadowInsertStatementRoutingEngine继承抽象类AbstractShadowDMLStatementRouteEngine,其实现的核心逻辑都在父类AbstractShadowDMLStatementRouteEngine中,具体SQL语句的实现类核心功能在于获取对应影子列的值的获取。

在ShadowInsertStatementRoutingEngine中,主要功能如下:

1)获取插入SQL语句中的所有表部分;

2)获取插入SQL语句中的注解;

3)获取投影列的条件迭代器;

迭代器提供了获取插入语句中配置了投影的列及对应列准备插入的值。在抽象父类中,通过列及对应的值,结合配置的算法,确认是否要将数据源替换为对应的投影数据源;

AbstractShadowDMLStatementRouteEngine

AbstractShadowDMLStatementRouteEngine的源码如下:

package org.apache.shardingsphere.shadow.route.engine.dml;

/**
 * DML语句的影子路由引擎
 */
@Getter
public abstract class AbstractShadowDMLStatementRouteEngine implements ShadowRouteEngine {

    // 表昵称和表名的映射
    private final Map<String, String> tableAliasNameMappings = new LinkedHashMap<>();

    /**
     * 影子库路由
     * @param routeContext route context
     * @param shadowRule shadow rule
     */
    @Override
    public void route(final RouteContext routeContext, final ShadowRule shadowRule) {
        decorateRouteContext(routeContext, shadowRule, findShadowDataSourceMappings(shadowRule));
    }

    /**
     * 查找影子库数据源映射。key为生产数据源、value为影子数据源
     * @param shadowRule
     * @return
     */
    private Map<String, String> findShadowDataSourceMappings(final ShadowRule shadowRule) {
        // 从SQL语句中的所有表,查找配置影子规则的表
        Collection<String> relatedShadowTables = getRelatedShadowTables(getAllTables(), shadowRule);
        if (relatedShadowTables.isEmpty() && isMatchDefaultShadowAlgorithm(shadowRule)) {
            return shadowRule.getAllShadowDataSourceMappings();
        }
        ShadowOperationType shadowOperationType = getShadowOperationType();
        // 判断 sql 注释中是否有shadow的提示。如 影子算法为 SIMPLE_HINT,且sql语句中添加了 /* SHARDINGSPHERE_HINT: SHADOW=true */
        Map<String, String> result = findBySQLComments(relatedShadowTables, shadowRule, shadowOperationType);
        if (!result.isEmpty()) {
            return result;
        }
        // 查找表中的是否存在满足条件的影子列,如果存在,则返回对应表的数据源映射
        return findByShadowColumn(relatedShadowTables, shadowRule, shadowOperationType);
    }

    /**
     * 结合配置的规则,查找配置了影子规则的表
     * @param simpleTableSegments
     * @param shadowRule
     * @return
     */
    private Collection<String> getRelatedShadowTables(final Collection<SimpleTableSegment> simpleTableSegments, final ShadowRule shadowRule) {
        Collection<String> tableNames = new LinkedHashSet<>();
        // 遍历表部分,解析出表名及表昵称映射
        for (SimpleTableSegment each : simpleTableSegments) {
            String tableName = each.getTableName().getIdentifier().getValue();
            String alias = each.getAlias().isPresent() ? each.getAlias().get() : tableName;
            tableNames.add(tableName);
            tableAliasNameMappings.put(alias, tableName);
        }
        // 从影子规则中获取相关的表
        return shadowRule.getRelatedShadowTables(tableNames);
    }

    /**
     * 是否匹配默认阴影算法
     * @param shadowRule
     * @return
     */
    @SuppressWarnings("unchecked")
    private boolean isMatchDefaultShadowAlgorithm(final ShadowRule shadowRule) {
        Optional<Collection<String>> sqlComments = parseSQLComments();
        // 如果没有设置注释段,返回false
        if (!sqlComments.isPresent()) {
            return false;
        }
        // 获取默认影子算法
        Optional<ShadowAlgorithm> defaultShadowAlgorithm = shadowRule.getDefaultShadowAlgorithm();
        if (defaultShadowAlgorithm.isPresent()) {
            ShadowAlgorithm shadowAlgorithm = defaultShadowAlgorithm.get();
            // 是Hint的影子算法
            if (shadowAlgorithm instanceof HintShadowAlgorithm<?>) {
                // 创建影子确定条件,影子操作类型为HINT_MATCH
                ShadowDetermineCondition shadowDetermineCondition = new ShadowDetermineCondition("", ShadowOperationType.HINT_MATCH);
                // 是否在hint影子算法中。如sql注释中是否添加了 shadow 的hint提示
                return HintShadowAlgorithmDeterminer.isShadow((HintShadowAlgorithm<Comparable<?>>) shadowAlgorithm, shadowDetermineCondition.initSQLComments(sqlComments.get()), shadowRule);
            }
        }
        return false;
    }

    /**
     * 查找SQL的注释部分,获取数据源信息。key为生产数据源、value为影子数据源
     * @param relatedShadowTables sql语句中配置了影子规则的表
     * @param shadowRule 影子规则对象
     * @param shadowOperationType 当前sql操作的类型
     * @return
     */
    private Map<String, String> findBySQLComments(final Collection<String> relatedShadowTables, final ShadowRule shadowRule, final ShadowOperationType shadowOperationType) {
        Map<String, String> result = new LinkedHashMap<>();
        // 遍历影子表
        for (String each : relatedShadowTables) {
            // 判断sql的注释中是否包含了shadow信息,如 /* SHARDINGSPHERE_HINT: SHADOW=true */
            if (isContainsShadowInSQLComments(each, shadowRule, new ShadowDetermineCondition(each, shadowOperationType))) {
                // 获取影子数据源映射。key为生产数据源、value为影子数据源
                result.putAll(shadowRule.getRelatedShadowDataSourceMappings(each));
                return result;
            }
        }
        return result;
    }

    /**
     * 判断sql语句的注释段是否包含了 shadow 的注释
     * @param tableName
     * @param shadowRule
     * @param shadowCondition
     * @return
     */
    private boolean isContainsShadowInSQLComments(final String tableName, final ShadowRule shadowRule, final ShadowDetermineCondition shadowCondition) {
        // 获取解析后的 SQL 注释,判断注释中是否包含了 shadow 的信息
        return parseSQLComments().filter(each -> isMatchAnyHintShadowAlgorithms(shadowRule.getRelatedHintShadowAlgorithms(tableName), shadowCondition.initSQLComments(each), shadowRule)).isPresent();
    }

    /**
     * 是否匹配任意的Hint影子算法
     * @param shadowAlgorithms
     * @param shadowCondition
     * @param shadowRule
     * @return
     */
    private boolean isMatchAnyHintShadowAlgorithms(final Collection<HintShadowAlgorithm<Comparable<?>>> shadowAlgorithms, final ShadowDetermineCondition shadowCondition, final ShadowRule shadowRule) {
        // 遍历hint影子算法
        for (HintShadowAlgorithm<Comparable<?>> each : shadowAlgorithms) {
            // 判断是否满足hint影子算法中的影子规则
            // 默认的hint影子算法为SimpleHintShadowAlgorithm,配置的type为SIMPLE_HINT
            // 该算法解析传入的shadowCondition中的sqlComment是否存在 /* SHARDINGSPHERE_HINT: SHADOW=true */ 的信息
            // 即判断对应的sql语句是否有 /* SHARDINGSPHERE_HINT: SHADOW=true */ 相关注解信息
            if (HintShadowAlgorithmDeterminer.isShadow(each, shadowCondition, shadowRule)) {
                return true;
            }
        }
        return false;
    }

    /**
     * 通过影子列查找数据源映射,key为生产数据源、value为影子数据源
     * @param relatedShadowTables 相关影子表
     * @param shadowRule 影子规则
     * @param shadowOperationType 影子操作类型
     * @return
     */
    private Map<String, String> findByShadowColumn(final Collection<String> relatedShadowTables, final ShadowRule shadowRule, final ShadowOperationType shadowOperationType) {
        Map<String, String> result = new LinkedHashMap<>();
        // 遍历
        for (String each : relatedShadowTables) {
            // 获取相关影子表的影子列名称
            Collection<String> relatedShadowColumnNames = shadowRule.getRelatedShadowColumnNames(shadowOperationType, each);
            // 存在影子列 && 匹配任意列影子算法
            if (!relatedShadowColumnNames.isEmpty() && isMatchAnyColumnShadowAlgorithms(each, relatedShadowColumnNames, shadowRule, shadowOperationType)) {
                // 返回对应表的影子数据源映射
                return shadowRule.getRelatedShadowDataSourceMappings(each);
            }
        }
        return result;
    }

    /**
     * 匹配任意列投影算法
     * @param shadowTable 影子表
     * @param shadowColumnNames 设置影子的列
     * @param shadowRule
     * @param shadowOperation SQL的操作类型
     * @return
     */
    private boolean isMatchAnyColumnShadowAlgorithms(final String shadowTable, final Collection<String> shadowColumnNames, final ShadowRule shadowRule, final ShadowOperationType shadowOperation) {
        // 遍历投影的列名
        for (String each : shadowColumnNames) {
            // 匹配任意列影子算法
            if (isMatchAnyColumnShadowAlgorithms(shadowTable, each, shadowOperation, shadowRule)) {
                return true;
            }
        }
        return false;
    }

    /**
     * 匹配任意列影子算法
     * @param shadowTable 影子表
     * @param shadowColumn 影子列(表中设置了影子的列)
     * @param shadowOperationType
     * @param shadowRule
     * @return
     */
    private boolean isMatchAnyColumnShadowAlgorithms(final String shadowTable, final String shadowColumn, final ShadowOperationType shadowOperationType, final ShadowRule shadowRule) {
        // 从影子规则中获取 shadowColumn 列的影子算法
        Collection<ColumnShadowAlgorithm<Comparable<?>>> columnShadowAlgorithms = shadowRule.getRelatedColumnShadowAlgorithms(shadowOperationType, shadowTable, shadowColumn);
        if (columnShadowAlgorithms.isEmpty()) {
            return false;
        }
        // 获取影子列的条件迭代器
        Iterator<Optional<ShadowColumnCondition>> iterator = getShadowColumnConditionIterator(shadowColumn);
        ShadowDetermineCondition shadowDetermineCondition;
        // 遍历迭代器
        while (iterator.hasNext()) {
            // 获取SQL中下一个列的条件信息
            Optional<ShadowColumnCondition> next = iterator.next();
            // 如果找到影子列
            if (next.isPresent()) {
                for (ColumnShadowAlgorithm<Comparable<?>> each : columnShadowAlgorithms) {
                    // 创建一个影子确定条件对象
                    shadowDetermineCondition = new ShadowDetermineCondition(shadowTable, shadowOperationType);
                    // 通过决策器进行影子判断
                    if (ColumnShadowAlgorithmDeterminer.isShadow(each, shadowDetermineCondition.initShadowColumnCondition(next.get()))) {
                        return true;
                    }
                }
            }
        }
        return false;
    }

    /**
     * 获取sql语句中所有的表
     * @return
     */
    protected abstract Collection<SimpleTableSegment> getAllTables();

    /**
     * 获取影子库映射类型,即当前sql语句的类型。如INSERT、UPDATE等
     * @return
     */
    protected abstract ShadowOperationType getShadowOperationType();

    /**
     * 解析SQL语句的注释信息
     * @return
     */
    protected abstract Optional<Collection<String>> parseSQLComments();
    
    /**
     * 获取投影列的条件迭代器
     * @param shadowColumn
     * @return
     */
    protected abstract Iterator<Optional<ShadowColumnCondition>> getShadowColumnConditionIterator(String shadowColumn);

    /**
     * 获取第一个表名
     * @return
     */
    protected String getSingleTableName() {
        return tableAliasNameMappings.entrySet().iterator().next().getValue();
    }
}

在ShadowSQLRouter的decorateRouteContext()方法中,通过ShadowRouteEngineFactory的newInstance()获取一个ShadowRouteEngine对象,执行ShadowRouteEngine的route()方法。即执行AbstractShadowDMLStatementRouteEngine的route()方法。

route()方法执行如下:

1)执行findShadowDataSourceMappings()方法,查找影子库数据源映射。key为生产数据源、value为影子数据源;

1.1)从SQL语句中的所有表,查找配置影子规则的表;

1.2)如果没有满足的表 && 投影规则配置了默认投影算法,则返回配置的所有影子数据源;

1.3)否则:

1.3.1)查找SQL的注释部分,获取数据源信息。key为生产数据源、value为影子数据源,如果找到,返回结果;否则往下继续执行;

1.3.2)查找表中的是否存在满足条件的影子列,如果存在,则返回对应表的数据源映射;

2)执行父类ShadowRouteEngine的decorateRouteContext(),装饰路由上下文;

遍历当前路由上下文中的路由单元,根据路由单元中的实际数据源,执行如下:

2.1)如果实际数据源配置了影子规则,则继续执行;

2.2)从1)中查找对应的实际数据源在当前SQL语句中是否满足了影子的条件(如果存在就满足,不存在就不满足),存在则替换为影子数据源;

影子库配置示例

rules:
 - !SHADOW
   dataSources:
      ds1:  # 投影组的逻辑数据源的名称
        productionDataSourceName: ds
        shadowDataSourceName: shadow_ds
    tables:
      t_order:
        dataSourceNames:
          - ds1            # 以上 dataSources下的ds
        shadowAlgorithmNames:
          - user_id_insert_match_algorithm
          - sql_hint_algorithm
    shadowAlgorithms:
      user_id_insert_match_algorithm:
        type: REGEX_MATCH
        props:
          operation: insert
          column: user_id
          regex: "[1]"
      sql_hint_algorithm:
        type: SQL_HINT

小结

以上为本篇分析的全部内容,以下做一个小结:

1)ShardingSphere 影子库是一个在数据库层面解决全链路在线压测问题的有效工具,影子库用于接收测试数据,以防止测试数据污染生产数据库;

2)影子库规则对应的路由器对象为ShadowSQLRouter,在ShardingSpherePreparedStatement执行SQL语句进行SQL路由创建RouteContext路由上下文时最后执行的路由器;

3)影子库路由器中,通过ShadowRouteEngineFactory的newInstance()创建影子库路由引擎;

不同的SQL操作语句,创建不同的路由引擎,如插入语句,创建ShadowInsertStatementRoutingEngine;

4)ShadowInsertStatementRoutingEngine插入语句的路由引擎,继承于抽象类AbstractShadowDMLStatementRouteEngine。父类负责整体逻辑允许,针对不同SQL操作语句的实现类路由引擎,提供对应影子库判断信息;

4.1)在父类AbstractShadowDMLStatementRouteEngine的route()路由方法中,查找SQL语句中满足影子库规则的数据源映射;

4.1.1)从SQL语句中的所有表,查找配置影子规则的表。如果没有满足的表 && 投影规则配置了默认投影算法,则返回配置的所有影子数据源;

4.1.2)查找SQL的注释部分,获取数据源信息。key为生产数据源、value为影子数据源,如果找到,返回结果;

4.1.3)查找表中的是否存在满足条件的影子列,如果存在,则返回对应表的数据源映射;

4.1.3.1)在ShadowInsertStatementRoutingEngine中,解析SQL的插入表及列,判断对应表的列是否配置了影子库路由规则,如果有配置,返回列及对应列的插入值;

4.1.3.2)在父类AbstractShadowDMLStatementRouteEngine中,获取子类提供的设置了影子列及值,执行影子库算法,判断值是否满足配置的规则,如果满足,保存对应表的数据源映射,最终返回满足条件的数据源映射;

4.2)遍历当前路由上下文的路由单元,替换数据源映射中的真实数据源为影子数据源名称;

关于本篇内容你有什么自己的想法或独到见解,欢迎在评论区一起交流探讨下吧。


http://www.kler.cn/a/453962.html

相关文章:

  • Unity2021.3.16f1可以正常打开,但是Unity2017.3.0f3却常常打开闪退或者Unity2017编辑器运行起来就闪退掉
  • .NET平台用C#通过字节流动态操作Excel文件
  • html + css 淘宝网实战
  • 使用 pyreqs 快速创建 requirements.txt PyCharm 中 UnicodeDecodeError 问题
  • 生成10级子目录,每个子目录下有100个不同大小的文件
  • springboot使用自定义的线程池 完成 多线程执行网络请求,返回数据后,统一返回给前段
  • SOME/IP 入门1
  • C# 2024/12/26 周四
  • 【大语言模型】ACL2024论文-36 利用NLI和ChatGPT及编码簿知识进行零样本政治关系分类
  • LabVIEW生物医学信号虚拟实验平台
  • 我用Cursor+DeepSeek做了个飞书文档一键同步插件,免费使用!
  • Java八股汇总【MySQL】
  • 代码随想录算法训练营第五十二天 | 101. 孤岛的总面积 102.沉没孤岛 103.水流问题 104.建造最大岛屿
  • 流批一体向量化计算引擎 Flex 在蚂蚁的探索和实践
  • Java爬虫实战:深度解析VIP商品详情获取技术
  • AWS IAM Roles Anywhere 使用 OpenSSL 自签 CA 过程
  • 黑马Java面试教程_P9_JVM虚拟机
  • protobuf学习使用
  • 面试题整理12----K8s中Pod创建常见错误
  • android webview 从配置文件加载网页——未来之窗跨平台架构
  • Linux文件目录 --- mkdir命令,创建目录,多级目录,设置目录权限
  • 《探索 Apache Spark MLlib 与 Java 结合的卓越之道》
  • 图【东北大学oj数据结构11-1】C++
  • kafka的备份策略:从备份到恢复
  • HDR视频技术之十一:HEVCH.265 的 HDR 编码方案
  • IP-trunk,HDLC链路的捆绑