此文已由作者張鎬薪授權網易雲社群釋出。
歡迎訪問網易雲社群,瞭解更多網易技術產品運營經驗。
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正常解析的步驟
更多網易技術、產品、運營經驗分享請點選。
相關文章:
【推薦】 掃臉動畫
【推薦】 selenium下拉框踩坑埋坑
【推薦】 大資料、資料探勘在交通領域的應用