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

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

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正常解析的步骤

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

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

相关文章:
【推荐】 Redux其实很简单(原理篇)

数据库路由中间件MyCat - 源代码篇(15)的更多相关文章

  1. 数据库路由中间件MyCat - 源代码篇(1)

    此文已由作者张镐薪授权网易云社区发布. 欢迎访问网易云社区,了解更多网易技术产品运营经验. 进入了源代码篇,我们先从整体入手,之后拿一个简单流程前端连接建立与认证作为例子,理清代码思路和设计模式.然后 ...

  2. 数据库路由中间件MyCat - 源代码篇(13)

    此文已由作者张镐薪授权网易云社区发布. 欢迎访问网易云社区,了解更多网易技术产品运营经验. 4.配置模块 4.2 schema.xml 接上一篇,接下来载入每个schema的配置(也就是每个MyCat ...

  3. 数据库路由中间件MyCat - 源代码篇(7)

    此文已由作者张镐薪授权网易云社区发布. 欢迎访问网易云社区,了解更多网易技术产品运营经验. 3. 连接模块 3.4 FrontendConnection前端连接 构造方法: public Fronte ...

  4. 数据库路由中间件MyCat - 源代码篇(17)

    此文已由作者张镐薪授权网易云社区发布. 欢迎访问网易云社区,了解更多网易技术产品运营经验. 调用processInsert(sc,schema,sqlType,origSQL,tableName,pr ...

  5. 数据库路由中间件MyCat - 源代码篇(14)

    此文已由作者张镐薪授权网易云社区发布. 欢迎访问网易云社区,了解更多网易技术产品运营经验. 对于表的dataNode对应关系,有个特殊配置即类似dataNode="distributed(d ...

  6. 数据库路由中间件MyCat - 源代码篇(4)

    此文已由作者易国强授权网易云社区发布. 欢迎访问网易云社区,了解更多网易技术产品运营经验. 2. 前端连接建立与认证 Title:MySql连接建立以及认证过程client->MySql:1.T ...

  7. 数据库路由中间件MyCat - 源代码篇(2)

    此文已由作者张镐薪授权网易云社区发布. 欢迎访问网易云社区,了解更多网易技术产品运营经验. 2. 前端连接建立与认证 Title:MySql连接建立以及认证过程client->MySql:1.T ...

  8. 数据库路由中间件MyCat - 源代码篇(16)

    此文已由作者张镐薪授权网易云社区发布. 欢迎访问网易云社区,了解更多网易技术产品运营经验. 5. 路由模块 真正取得RouteResultset的步骤:AbstractRouteStrategy的ro ...

  9. 数据库路由中间件MyCat - 源代码篇(10)

    此文已由作者张镐薪授权网易云社区发布. 欢迎访问网易云社区,了解更多网易技术产品运营经验. 3. 连接模块 3.5 后端连接 3.5.2 后端连接获取与维护管理 还是那之前的流程, st=>st ...

随机推荐

  1. [HNOI2006]马步距离

    嘟嘟嘟 这题首先直接bfs可定过不了,因此可以先贪心缩小两个点的距离,直到达到某一个较小的范围(我用的是30),再bfs暴力求解. 首先我们求出这两个点的相对距离x, y,这样就相当于从(x, y) ...

  2. Yii2.0 发送文件

    1.发送文件 和浏览器跳转类似,文件发送是另一个依赖指定 HTTP 头的功能, Yii 提供方法集合来支持各种文件发送需求,它们对 HTTP 头都有内置的支持. yii\web\Response::s ...

  3. ZooKeeper学习之路 (八)ZooKeeper原理解析

    ZooKeeper中的各种角色 ZooKeeper与客户端 每个Server在工作过程中有三种状态: LOOKING:当前Server不知道leader是谁,正在搜寻 LEADING:当前Server ...

  4. [Python 多线程] Semaphore、BounedeSemaphore (十二)

    Semaphore 信号量,信号量对象内部维护一个倒计数器,每一次acquire都会减1,当acquire方法发现计数为0就阻塞请求的线程,直到其它线程对信号量release后,计数大于0,恢复阻塞的 ...

  5. vue-scroll 底部无数据时,底部出现大片的空白

    vue-scroll放在vue的项目中,实现下拉刷新的效果,但是发现,不能上拉的bug,上拉了之后,底部出现了一大段的空白,参照GitHub的问题,算是暂时解决了. 不能上拉的原因是:滑动标签里边的内 ...

  6. PHP面试系列之Linux(一) ----- Linux基础

    一.系统安全 sudo:以系统管理者的身份执行指令,也就是说,经由 sudo 所执行的指令就好像是 root 亲自执行. su:用于变更为其他使用者的身份,除 root 外,需要键入该使用者的密码. ...

  7. CSS中背景图片的background-position中的left top到底是相对于谁的?

    在学习的时候遇到了如下问题: CSS中背景图片的background-position中的left top到底是相对于谁的,content-box?padding-box?border-box? ba ...

  8. 启动 NFS 守护进程:rpc.nfsd: writing fd to kernel failed: errno 111 (Connection refused)

    启动 NFS 守护进程:rpc.nfsd: writing fd to kernel failed: errno 111 (Connection refused) rpc.nfsd: unable t ...

  9. 修改网卡MAC地址后出现问题:device eth0 does not seem to be present, delaying initialization

    修改网卡MAC地址后出现问题:device eth0 does not seem to be present, delaying initialization   1.修改网卡对应的文件,将配置文件中 ...

  10. 用 S5PV210 学习 Linux (三) SD卡下载

    学习地址:http://edu.51cto.com/lesson/id-63015.html http://blog.csdn.net/karven_/article/details/52015325 ...