2024-01-18
原文作者:hashcon 原文地址: https://zhanghaoxin.blog.csdn.net/article/details/51463927

5. 路由模块

5.4 DDL语句路由

可以分为两步,整体源代码:

    public static RouteResultset routeToDDLNode(RouteResultset rrs, int sqlType, String stmt,SchemaConfig schema) throws SQLSyntaxErrorException {
        stmt = getFixedSql(stmt);
        String tablename = "";      
        final String upStmt = stmt.toUpperCase();
        if(upStmt.startsWith("CREATE")){
            if (upStmt.contains("CREATE INDEX ")){
                tablename = RouterUtil.getTableName(stmt, RouterUtil.getCreateIndexPos(upStmt, 0));
            }else tablename = RouterUtil.getTableName(stmt, RouterUtil.getCreateTablePos(upStmt, 0));
        }else if(upStmt.startsWith("DROP")){
            if (upStmt.contains("DROP INDEX ")){
                tablename = RouterUtil.getTableName(stmt, RouterUtil.getDropIndexPos(upStmt, 0));
            }else tablename = RouterUtil.getTableName(stmt, RouterUtil.getDropTablePos(upStmt, 0));
        }else if(upStmt.startsWith("ALTER")){
            tablename = RouterUtil.getTableName(stmt, RouterUtil.getAlterTablePos(upStmt, 0));
        }else if (upStmt.startsWith("TRUNCATE")){
            tablename = RouterUtil.getTableName(stmt, RouterUtil.getTruncateTablePos(upStmt, 0));
        }
        tablename = tablename.toUpperCase();
    
        if (schema.getTables().containsKey(tablename)){
            if(ServerParse.DDL==sqlType){
                List<String> dataNodes = new ArrayList<>();
                Map<String, TableConfig> tables = schema.getTables();
                TableConfig tc;
                if (tables != null && (tc = tables.get(tablename)) != null) {
                    dataNodes = tc.getDataNodes();
                }
                Iterator<String> iterator1 = dataNodes.iterator();
                int nodeSize = dataNodes.size();
                RouteResultsetNode[] nodes = new RouteResultsetNode[nodeSize];
    
                for(int i=0;i<nodeSize;i++){
                    String name = iterator1.next();
                    nodes[i] = new RouteResultsetNode(name, sqlType, stmt);
                }
                rrs.setNodes(nodes);
            }
            return rrs;
        }else if(schema.getDataNode()!=null){       //默认节点ddl
            RouteResultsetNode[] nodes = new RouteResultsetNode[1];
            nodes[0] = new RouteResultsetNode(schema.getDataNode(), sqlType, stmt);
            rrs.setNodes(nodes);
            return rrs;
        }
        //both tablename and defaultnode null
        LOGGER.error("table not in schema----"+tablename);
        throw new SQLSyntaxErrorException("op table not in schema----"+tablename);
    }

首先,获取表名,步骤如下:

202401182020445841.png
拿一个获取表名的函数举例:

    /**
     * 获取语句中前关键字位置和占位个数表名位置
     *
     * @param upStmt
     *            执行语句
     * @param start
     *            开始位置
     * @return int[]关键字位置和占位个数
     * @author aStoneGod
     */
    public static int[] getCreateIndexPos(String upStmt, int start) {
        String token1 = "CREATE ";
        String token2 = " INDEX ";
        String token3 = " ON ";
        int createInd = upStmt.indexOf(token1, start);
        int idxInd = upStmt.indexOf(token2, start);
        int onInd = upStmt.indexOf(token3, start);
        // 既包含CREATE又包含INDEX,且CREATE关键字在INDEX关键字之前, 且包含ON...
        if (createInd >= 0 && idxInd > 0 && idxInd > createInd && onInd > 0 && onInd > idxInd) {
            return new int[] {onInd , token3.length() };
        } else {
            return new int[] { -1, token2.length() };// 不满足条件时,只关注第一个返回值为-1,第二个任意
        }
    }

然后,根据表名获取配置进行路由:

202401182020449422.png

* 默认语句路由*

对于有默认节点的schema,且不是show, describe, select @@之类的语句,则路由到默认的节点上。
对于show, describe, select @@之类的语句,利用查询信息路由方法算出路由。

202401182020453013.png
接下来,取一个举例,对于Show语句:
analyseShowSQL(schema, rrs, stmt)方法

202401182020456874.png

5.5 AST语义解析路由

首先我们看一下MySQL的SQL解析步骤(硬解析和软解析):

202401182020462885.png
MyCat的机制,仿照MySQL的,可以总结为:

202401182020470036.png
这里我们可以总结一个优化思路,就是通过仿照MySQL物理优化原理(定时更新表配置,报表信息),来做进一步MyCat查询的优化。
语义解析基本过程:

1.词法分析(一般抽象都叫Lexer):不同的关键词有不同的含义

    select concat(id,'_',name),value from student where value>60 order by value

202401182020477357.png
词法分析的输出,就是一句带上词义的语句:

    (select: Keyword)  (concat: Keyword)((: LB)…… (from: keyword) (student: identifier)

2.语法分析:

  • 分析关键词之间的联系,生成表达式(expression)

  • 基本语法正确性判断(比如from这个keyword之后必须紧跟一个表名(就是一个identifier))

    202401182020480938.png

3.生成AST语意树(完整解析的statement)

202401182020485199.png
根据MyCat权威指南,DruidParser比其他Parser快很多很多。
快在哪里呢?主要是抽象静态化的粒度,拿jsqlparser和druidparser对比。
这两个parser都遵从了上面的步骤,对于词(lexer),表达式(expression)和语句AST(statement)都有抽象。
但是对于语句AST(statement)的抽象, DruidParser做的粒度更细。如下图对于Alter语句的对比:

2024011820204892210.png
所以,不难推测为啥DruidParser快了

阅读全文