数据库路由中间件MyCat - 源代码篇(15)
此文已由作者张镐薪授权网易云社区发布。
欢迎访问网易云社区,了解更多网易技术产品运营经验。
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)的更多相关文章
- 数据库路由中间件MyCat - 源代码篇(1)
此文已由作者张镐薪授权网易云社区发布. 欢迎访问网易云社区,了解更多网易技术产品运营经验. 进入了源代码篇,我们先从整体入手,之后拿一个简单流程前端连接建立与认证作为例子,理清代码思路和设计模式.然后 ...
- 数据库路由中间件MyCat - 源代码篇(13)
此文已由作者张镐薪授权网易云社区发布. 欢迎访问网易云社区,了解更多网易技术产品运营经验. 4.配置模块 4.2 schema.xml 接上一篇,接下来载入每个schema的配置(也就是每个MyCat ...
- 数据库路由中间件MyCat - 源代码篇(7)
此文已由作者张镐薪授权网易云社区发布. 欢迎访问网易云社区,了解更多网易技术产品运营经验. 3. 连接模块 3.4 FrontendConnection前端连接 构造方法: public Fronte ...
- 数据库路由中间件MyCat - 源代码篇(17)
此文已由作者张镐薪授权网易云社区发布. 欢迎访问网易云社区,了解更多网易技术产品运营经验. 调用processInsert(sc,schema,sqlType,origSQL,tableName,pr ...
- 数据库路由中间件MyCat - 源代码篇(14)
此文已由作者张镐薪授权网易云社区发布. 欢迎访问网易云社区,了解更多网易技术产品运营经验. 对于表的dataNode对应关系,有个特殊配置即类似dataNode="distributed(d ...
- 数据库路由中间件MyCat - 源代码篇(4)
此文已由作者易国强授权网易云社区发布. 欢迎访问网易云社区,了解更多网易技术产品运营经验. 2. 前端连接建立与认证 Title:MySql连接建立以及认证过程client->MySql:1.T ...
- 数据库路由中间件MyCat - 源代码篇(2)
此文已由作者张镐薪授权网易云社区发布. 欢迎访问网易云社区,了解更多网易技术产品运营经验. 2. 前端连接建立与认证 Title:MySql连接建立以及认证过程client->MySql:1.T ...
- 数据库路由中间件MyCat - 源代码篇(16)
此文已由作者张镐薪授权网易云社区发布. 欢迎访问网易云社区,了解更多网易技术产品运营经验. 5. 路由模块 真正取得RouteResultset的步骤:AbstractRouteStrategy的ro ...
- 数据库路由中间件MyCat - 源代码篇(10)
此文已由作者张镐薪授权网易云社区发布. 欢迎访问网易云社区,了解更多网易技术产品运营经验. 3. 连接模块 3.5 后端连接 3.5.2 后端连接获取与维护管理 还是那之前的流程, st=>st ...
随机推荐
- [USACO09MAR]Sand Castle
嘟嘟嘟 太水了,大佬们就绕道吧…… 就是m, b数组分别排个序,然后更改对应位置的m[i]和b[i],就行了. 因为如果m[i]不改为b[i]而是b[i + 1]的话,那么必定要将m[j] (j &g ...
- 【luogu P1850 换教室】 题解
题目链接:https://www.luogu.org/problemnew/show/P1850 难的不在状态上,难在转移方程. (话说方程写错居然还有84分= =) #include <cst ...
- iOS10 语音播报填坑详解(解决串行播报中断问题)
iOS10 语音播报填坑详解(解决串行播报中断问题) 在来聊这类需求的解决方案之前,咱们还是先来聊一聊这类需求的真实使用场景:语音播报.语音播报需求运用最为广泛的应该是收银对账了,就类似于支付宝.微信 ...
- c#网络传输
接着前面简单讲的,给大家聊聊服务开发. 网络传输 先说网络传输开发,总体来说,可以看成4中模型 我们把传输过程看做网线,那么在通过传输的过程中.2边就涉及池化问题,也就是我们常见的异步传输. 在业务端 ...
- jQuery.validate.js表单验证插件
jQuery.validate.js表单验证插件的使用 效果: 代码: <!DOCTYPE html> <html lang="en"> <head& ...
- Java使用多线程实现Socket多客户端的通信
要想详细了解socket,大家请自行百度,我这里只简单介绍. 在网络中,我们可以利用ip地址+协议+端口号唯一标示网络中的一个进程.而socket编程就是为了完成两个唯一进程之间的通信(一个是客户端, ...
- python3 用户登录验证的小功能
用户登录验证,记录一下,还需要修改黑名单不合理 #!/usr/bin/env python3 ''' 需求:编写登录接口 1.输入用户名和密码 2.验证用户密码成功后输出欢迎消息 3.3次没有验证通过 ...
- 从零开始一个http服务器(五)-模拟cgi
从零开始一个http服务器-模拟cgi(五) 代码地址 : https://github.com/flamedancer/cserver git checkout step5 运行: make cle ...
- [Golang学习笔记] 01 工作区和GOPATH
Go语言3个环境变量: GOROOT:GO语言按照根路径,也就是GO语言的安装路径. GOPATH:若干工作区目录的路径.是我自己定义的工作空间. GOBIN:GO程序生成的可执行文件(executa ...
- HTTP1.0,HTTP1.1,HTTP2.0的主要特征对比
HTTP1.0 是一种无状态.无连接的应用层协议. HTTP1.0规定浏览器和服务器保持短暂的连接,浏览器的每次请求都需要与服务器建立一个TCP连接,服务器处理完成后立即断开TCP连接(无连接),服务 ...