sharding-jdbc之解析引擎

sharding-jdbc对于sql的解析需要依赖解析引擎,解析过程会经历两大过程:
1 通过G4规则的解析器生成抽象语法树;
2 通过SQL解析引擎输出解析结果,这一步包含SQL提取、SQL填充、SQL优化。

抽象语法树

解析过程分为词法解析和语法解析。 词法解析器用于将SQL拆解为不可再分的原子符号,称为Token。并根据不同数据库方言所提供的字典,将其归类为关键字,表达式,字面量和操作符。 再使用语法解析器将SQL转换为抽象语法树。最后,通过对抽象语法树的遍历去提炼分片所需的上下文,并标记有可能需要改写的位置。 供分片使用的解析上下文包含查询选择项(Select Items)、表信息(Table)、分片条件(Sharding Condition)、自增主键信息(Auto increment Primary Key)、排序信息(Order By)、分组信息(Group By)以及分页信息(Limit、Rownum、Top)。 SQL的一次解析过程是不可逆的,一个个Token的按SQL原本的顺序依次进行解析,性能很高。 考虑到各种数据库SQL方言的异同,在解析模块提供了各类数据库的SQL方言字典。

SQL解析引擎

第三代SQL解析器则从3.0.x版本开始,ShardingSphere尝试使用ANTLR作为SQL解析的引擎,并计划根据DDL -> TCL -> DAL –> DCL -> DML –>DQL这个顺序,依次替换原有的解析引擎,目前仍处于替换迭代中。 使用ANTLR的原因是希望ShardingSphere的解析引擎能够更好的对SQL进行兼容。对于复杂的表达式、递归、子查询等语句,虽然ShardingSphere的分片核心并不关注,但是会影响对于SQL理解的友好度。 经过实例测试,ANTLR解析SQL的性能比自研的SQL解析引擎慢3-10倍左右。为了弥补这一差距,ShardingSphere将使用PreparedStatement的SQL解析的语法树放入缓存。 因此建议采用PreparedStatement这种SQL预编译的方式提升性能。
sharding-jdbc之解析引擎解析相应的源码如下:

//Antlr解析引擎
public final class AntlrParsingEngine implements SQLParser {
    //SQL解析器
    private final SQLParserEngine parserEngine;
    //SQL提取器
    private final SQLSegmentsExtractorEngine extractorEngine;
    //SQL填充器
    private final SQLStatementFillerEngine fillerEngine; 
    //SQL优化器
    private final SQLStatementOptimizerEngine optimizerEngine;
    
    public AntlrParsingEngine(final DatabaseType databaseType, final String sql, final ShardingRule shardingRule, final ShardingTableMetaData shardingTableMetaData) {
        parserEngine = new SQLParserEngine(databaseType, sql);
        extractorEngine = new SQLSegmentsExtractorEngine();
        fillerEngine = new SQLStatementFillerEngine(sql, shardingRule, shardingTableMetaData);
        optimizerEngine = new SQLStatementOptimizerEngine(shardingTableMetaData);
    }
    
    @Override
    public SQLStatement parse() {
         //1 通过SQL解析器解析,得到抽象语法树
        SQLAST ast = parserEngine.parse();
        //2 通过SQL提取器解析,得到SQL片段集合
        Collection<SQLSegment> sqlSegments = extractorEngine.extract(ast);
        //3 通过SQL填充器,得到SQL解析结果
        SQLStatement result = fillerEngine.fill(sqlSegments, ast.getRule());
        //4 通过优化器进一步优化SQL解析结果
        optimizerEngine.optimize(ast.getRule(), result);
        return result;
    }
}

SQL解析器

@RequiredArgsConstructor
public final class SQLParserEngine {
    //解析规则注册表
    private final ParsingRuleRegistry parsingRuleRegistry = ParsingRuleRegistry.getInstance();
    //数据库类型
    private final DatabaseType databaseType;
    //需要解析的SQL
    private final String sql;
    
    /**
     * Parse SQL to AST.
     * 
     * @return Abstract syntax tree of SQL
     */
    public SQLAST parse() {
    	// 通过数据库类型获取指定数据库SQL解析器的实例,执行sql的解析,获取解析结果
        ParseTree parseTree = SQLParserFactory.newInstance(databaseType, sql).execute().getChild(0);
        if (parseTree instanceof ErrorNode) {
            throw new SQLParsingUnsupportedException(String.format("Unsupported SQL of `%s`", sql));
        }
        //从SQL解析规则注册表中获取解析规则
        Optional<SQLStatementRule> sqlStatementRule = parsingRuleRegistry.findSQLStatementRule(databaseType, parseTree.getClass().getSimpleName());
        if (!sqlStatementRule.isPresent()) {
            throw new SQLParsingUnsupportedException(String.format("Unsupported SQL of `%s`", sql));
        }
        //返回抽象语法树
        return new SQLAST((ParserRuleContext) parseTree, sqlStatementRule.get());
    }
}

SQL提取器

public final class SQLSegmentsExtractorEngine {
    
    /** 
     * Extract SQL segments.
     * 
     * @param ast SQL AST
     * @return SQL segments
     */
    public Collection<SQLSegment> extract(final SQLAST ast) {
        Collection<SQLSegment> result = new LinkedList<>();
        //遍历抽象语法树中SQL语法规则中的(SQLSegmentExtractor)SQL片段提取器
        for (SQLSegmentExtractor each : ast.getRule().getExtractors()) {
        //
            if (each instanceof OptionalSQLSegmentExtractor) {
            	//OptionalSQLSegmentExtractor提取器提取的SQL片段为单个
                Optional<? extends SQLSegment> sqlSegment = ((OptionalSQLSegmentExtractor) each).extract(ast.getParserRuleContext());
                if (sqlSegment.isPresent()) {
                    result.add(sqlSegment.get());
                }
            }
            //CollectionSQLSegmentExtractor提取器提取的SQL片段为SQL片段集合
            if (each instanceof CollectionSQLSegmentExtractor) {
                result.addAll(((CollectionSQLSegmentExtractor) each).extract(ast.getParserRuleContext()));
            }
        }
        return result;
    }
}

SQL填充器

@RequiredArgsConstructor
public final class SQLStatementFillerEngine {
    //解析规则注册表
    private final ParsingRuleRegistry parsingRuleRegistry = ParsingRuleRegistry.getInstance();
    
    private final String sql;
   // 数据库和表分片规则配置
    private final ShardingRule shardingRule;
    //分片表元数据
    private final ShardingTableMetaData shardingTableMetaData;
    
    /**
     * Fill SQL statement.
     *
     * @param sqlSegments SQL segments
     * @param rule SQL statement rule
     * @return SQL statement
     */
    @SuppressWarnings("unchecked")
    //避免javac捕获或抛出方法主体中的语句声明它们生成的任何已检查异常
    @SneakyThrows
    public SQLStatement fill(final Collection<SQLSegment> sqlSegments, final SQLStatementRule rule) {
        SQLStatement result = rule.getSqlStatementClass().newInstance();
        //遍历SQL片段,从SQL解析规则注册表中找到相应SQL填充器
        for (SQLSegment each : sqlSegments) {
            Optional<SQLStatementFiller> filler = parsingRuleRegistry.findSQLStatementFiller(each.getClass());
            if (filler.isPresent()) {
            //填充解析的结果中
                filler.get().fill(each, result, sql, shardingRule, shardingTableMetaData);
            }
        }
        return result;
    }
}

SQL优化器

@RequiredArgsConstructor
public final class SQLStatementOptimizerEngine {
    
    private final ShardingTableMetaData shardingTableMetaData;
    
    /**
     * Optimize SQL statement.
     *
     * @param rule SQL statement rule
     * @param sqlStatement SQL statement
     */
    public void optimize(final SQLStatementRule rule, final SQLStatement sqlStatement) {
        Optional<SQLStatementOptimizer> optimizer = rule.getOptimizer();
        if (optimizer.isPresent()) {
            optimizer.get().optimize(sqlStatement, shardingTableMetaData);
        }
    }
}

上一篇:Spring-boot2X基于sharding-jdbc3X分表分库


下一篇:sharding-proxy总揽