• 数据库路由中间件MyCat


    此文已由作者张镐薪授权网易云社区发布。

    欢迎访问网易云社区,了解更多网易技术产品运营经验。


    public static void handle(String stmt, ServerConnection c, int offs) {
            int offset = offs;
            switch (ServerParseSelect.parse(stmt, offs)) {
            case ServerParseSelect.VERSION_COMMENT:
                SelectVersionComment.response(c);
                break;
            case ServerParseSelect.DATABASE:
                SelectDatabase.response(c);
                break;
            case ServerParseSelect.USER:
                SelectUser.response(c);
                break;
            case ServerParseSelect.VERSION:
                SelectVersion.response(c);
                break;
            case ServerParseSelect.SESSION_INCREMENT:
                SessionIncrement.response(c);
                break;
            case ServerParseSelect.SESSION_ISOLATION:
                SessionIsolation.response(c);
                break;
            case ServerParseSelect.LAST_INSERT_ID:
                // offset = ParseUtil.move(stmt, 0, "select".length());
                loop:for (int l=stmt.length(); offset < l; ++offset) {
                    switch (stmt.charAt(offset)) {
                    case ' ':
                        continue;
                    case '/':
                    case '#':
                        offset = ParseUtil.comment(stmt, offset);
                        continue;
                    case 'L':
                    case 'l':
                        break loop;
                    }
                }
                offset = ServerParseSelect.indexAfterLastInsertIdFunc(stmt, offset);
                offset = ServerParseSelect.skipAs(stmt, offset);
                SelectLastInsertId.response(c, stmt, offset);
                break;
            case ServerParseSelect.IDENTITY:
                // offset = ParseUtil.move(stmt, 0, "select".length());
                loop:for (int l=stmt.length(); offset < l; ++offset) {
                    switch (stmt.charAt(offset)) {
                    case ' ':
                        continue;
                    case '/':
                    case '#':
                        offset = ParseUtil.comment(stmt, offset);
                        continue;
                    case '@':
                        break loop;
                    }
                }
                int indexOfAtAt = offset;
                offset += 2;
                offset = ServerParseSelect.indexAfterIdentity(stmt, offset);
                String orgName = stmt.substring(indexOfAtAt, offset);
                offset = ServerParseSelect.skipAs(stmt, offset);
                SelectIdentity.response(c, stmt, offset, orgName);
                break;
                case ServerParseSelect.SELECT_VAR_ALL:
                    SelectVariables.execute(c,stmt);
                    break;
            default:
                c.execute(stmt, ServerParse.SELECT);
            }
        }

    下一步,ServerConnection类处理SQL语句


    ServerConnection.java

    public void execute(String sql, int type) {        //连接状态检查
            if (this.isClosed()) {
                LOGGER.warn("ignore execute ,server connection is closed " + this);            return;
            }        // 事务状态检查
            if (txInterrupted) {
                writeErrMessage(ErrorCode.ER_YES,                    "Transaction error, need to rollback." + txInterrputMsg);            return;
            }        // 检查当前使用的DB
            String db = this.schema;        if (db == null) {
                db = SchemaUtil.detectDefaultDb(sql, type);            if (db == null) {
                    writeErrMessage(ErrorCode.ERR_BAD_LOGICDB, "No MyCAT Database selected");                return;
                }
            }        // 兼容PhpAdmin's, 支持对MySQL元数据的模拟返回
            //// TODO: 2016/5/20 支持更多information_schema特性 
            if (ServerParse.SELECT == type 
                    && db.equalsIgnoreCase("information_schema") ) {
                MysqlInformationSchemaHandler.handle(sql, this);            return;
            }        if (ServerParse.SELECT == type 
                    && sql.contains("mysql") 
                    && sql.contains("proc")) {
    
                SchemaUtil.SchemaInfo schemaInfo = SchemaUtil.parseSchema(sql);            if (schemaInfo != null 
                        && "mysql".equalsIgnoreCase(schemaInfo.schema)
                        && "proc".equalsIgnoreCase(schemaInfo.table)) {                // 兼容MySQLWorkbench
                    MysqlProcHandler.handle(sql, this);                return;
                }
            }
    
            SchemaConfig schema = MycatServer.getInstance().getConfig().getSchemas().get(db);        if (schema == null) {
                writeErrMessage(ErrorCode.ERR_BAD_LOGICDB,                    "Unknown MyCAT Database '" + db + "'");            return;
            }
    
            routeEndExecuteSQL(sql, type, schema);
    
        }

    调用routeEndExecuteSQL方法,会解析出RouteResultSet。这步包含了SQL语义解析,SQL路由,SQL查询优化,SQL语句改写,全局ID生成,最后,将解析出的RouteResultSet交给这个链接对应的session进行处理。 我们先分析SQL语义解析。看调用: ServerConnection.java

    rrs = MycatServer
                        .getInstance()
                        .getRouterservice()
                        .route(MycatServer.getInstance().getConfig().getSystem(),
                                schema, type, sql, this.charset, this);

    首先,关注下这个Routerservice是啥?在MyCat初始化时,会新建一个Routerservice(如之前配置模块中所讲): MyCatServer.java

    //路由计算初始化routerService = new RouteService(cacheService);

    Routerservice结构:  其中sqlRouteCache和tableId2DataNodeCache是通过CacheService(MyCat里面是ehcache做的缓存)传入的对于sql语句缓存和tableid与后台分片对应关系的缓存。具体缓存会在缓存模块中讲。

    调用route方法解析出RouteResultSet

    public RouteResultset route(SystemConfig sysconf, SchemaConfig schema,            int sqlType, String stmt, String charset, ServerConnection sc)
                throws SQLNonTransientException {
            RouteResultset rrs = null;
            String cacheKey = null;        /**
             *  SELECT 类型的SQL, 检测
             */
            if (sqlType == ServerParse.SELECT) {
                cacheKey = schema.getName() + stmt;            
                rrs = (RouteResultset) sqlRouteCache.get(cacheKey);            if (rrs != null) {                return rrs;
                }
            }        /*!mycat: sql = select name from aa */
            /*!mycat: schema = test *///      boolean isMatchOldHint = stmt.startsWith(OLD_MYCAT_HINT);//      boolean isMatchNewHint = stmt.startsWith(NEW_MYCAT_HINT);//        if (isMatchOldHint || isMatchNewHint ) {
            int hintLength = RouteService.isHintSql(stmt);        if(hintLength != -1){            int endPos = stmt.indexOf("*/");            if (endPos > 0) {                
                    // 用!mycat:内部的语句来做路由分析//                int hintLength = isMatchOldHint ? OLD_MYCAT_HINT.length() : NEW_MYCAT_HINT.length();
                    String hint = stmt.substring(hintLength, endPos).trim();    
    
                    int firstSplitPos = hint.indexOf(HINT_SPLIT);                
                    if(firstSplitPos > 0 ){
                        Map hintMap=    parseHint(hint);
                        String hintType = (String) hintMap.get(MYCAT_HINT_TYPE);
                        String hintSql = (String) hintMap.get(hintType);                    if( hintSql.length() == 0 ) {
                            LOGGER.warn("comment int sql must meet :/*!mycat:type=value*/ or /*#mycat:type=value*/ or /*mycat:type=value*/: "+stmt);                        throw new SQLSyntaxErrorException("comment int sql must meet :/*!mycat:type=value*/ or /*#mycat:type=value*/ or /*mycat:type=value*/: "+stmt);
                        }
                        String realSQL = stmt.substring(endPos + "*/".length()).trim();
    
                        HintHandler hintHandler = HintHandlerFactory.getHintHandler(hintType);                    if( hintHandler != null ) {    
    
                            if ( hintHandler instanceof  HintSQLHandler) {                            
                                  /**
                                 * 修复 注解SQL的 sqlType 与 实际SQL的 sqlType 不一致问题, 如: hint=SELECT,real=INSERT
                                 * fixed by zhuam
                                 */
                                int hintSqlType = ServerParse.parse( hintSql ) & 0xff;     
                                rrs = hintHandler.route(sysconf, schema, sqlType, realSQL, charset, sc, tableId2DataNodeCache, hintSql,hintSqlType,hintMap);
    
                            } else {                            
                                rrs = hintHandler.route(sysconf, schema, sqlType, realSQL, charset, sc, tableId2DataNodeCache, hintSql,sqlType,hintMap);
                            }
    
                        }else{
                            LOGGER.warn("TODO , support hint sql type : " + hintType);
                        }
    
                    }else{//fixed by runfriends@126.com
                        LOGGER.warn("comment in sql must meet :/*!mycat:type=value*/ or /*#mycat:type=value*/ or /*mycat:type=value*/: "+stmt);                    throw new SQLSyntaxErrorException("comment in sql must meet :/*!mcat:type=value*/ or /*#mycat:type=value*/ or /*mycat:type=value*/: "+stmt);
                    }
                }
            } else {
                stmt = stmt.trim();
                rrs = RouteStrategyFactory.getRouteStrategy().route(sysconf, schema, sqlType, stmt,
                        charset, sc, tableId2DataNodeCache);
            }        if (rrs != null && sqlType == ServerParse.SELECT && rrs.isCacheAble()) {
                sqlRouteCache.putIfAbsent(cacheKey, rrs);
            }        return rrs;
        }

    由于注解处理和sql解析有重叠,而且注解处理一直代码不稳定,所以,这里不涉及。只说sql正常解析的步骤



    免费体验云安全(易盾)内容安全、验证码等服务

    更多网易技术、产品、运营经验分享请点击



    相关文章:
    【推荐】 AndroidTV开发(3)

  • 相关阅读:
    SQL 去重 DISTINCT 语法
    urllib2 urllib urllib3的区别联系
    urllib库中常见的类和方法
    python模块之json pickle
    python os模块
    python的常见内置模块之-----time
    列表,队列,栈,链表的内存模型
    open函数
    session和cookie
    随笔小skill
  • 原文地址:https://www.cnblogs.com/163yun/p/9895279.html
Copyright © 2020-2023  润新知