更新時間:2021-09-17 10:57:32 來源:動力節點 瀏覽2483次
Mycat原理的內容有很多,這篇主要來說說Mycat對SQL語句的處理。
Mycat接收到客戶端的sql語句時,會統一使用ServerQueryHandler.query(String sql)方法來處理,ServerQueryHandler主要做了兩件事情。
1,.確定sql的類型。比如:SELECT、UPDATE、INSERT、SHOW等
2.將不同類型的sql交給不同的處理器進行處理
@Override
public void query(String sql) {
ServerConnection c = this.source;
//確定sql類型
int rs = ServerParse.parse(sql);
int sqlType = rs & 0xff;
//將不同類型的sql交給不同的處理器進行處理
switch (sqlType) {
//explain sql
case ServerParse.EXPLAIN:
ExplainHandler.handle(sql, c, rs >>> 8);
break;
//explain2 datanode=? sql=?
case ServerParse.EXPLAIN2:
Explain2Handler.handle(sql, c, rs >>> 8);
break;
case ServerParse.SET:
SetHandler.handle(sql, c, rs >>> 8);
break;
case ServerParse.SHOW:
ShowHandler.handle(sql, c, rs >>> 8);
break;
case ServerParse.SELECT:
SelectHandler.handle(sql, c, rs >>> 8);
break;
case ServerParse.START:
StartHandler.handle(sql, c, rs >>> 8);
break;
case ServerParse.BEGIN:
BeginHandler.handle(sql, c);
break;
//不支持oracle的savepoint事務回退點
case ServerParse.SAVEPOINT:
SavepointHandler.handle(sql, c);
break;
case ServerParse.KILL:
KillHandler.handle(sql, rs >>> 8, c);
break;
//不支持KILL_Query
case ServerParse.KILL_QUERY:
LOGGER.warn(new StringBuilder().append("Unsupported command:").append(sql).toString());
c.writeErrMessage(ErrorCode.ER_UNKNOWN_COM_ERROR,"Unsupported command");
break;
case ServerParse.USE:
UseHandler.handle(sql, c, rs >>> 8);
break;
case ServerParse.COMMIT:
c.commit();
break;
case ServerParse.ROLLBACK:
c.rollback();
break;
case ServerParse.HELP:
LOGGER.warn(new StringBuilder().append("Unsupported command:").append(sql).toString());
c.writeErrMessage(ErrorCode.ER_SYNTAX_ERROR, "Unsupported command");
break;
case ServerParse.MYSQL_CMD_COMMENT:
c.write(c.writeToBuffer(OkPacket.OK, c.allocate()));
break;
case ServerParse.MYSQL_COMMENT:
c.write(c.writeToBuffer(OkPacket.OK, c.allocate()));
break;
case ServerParse.LOAD_DATA_INFILE_SQL:
c.loadDataInfileStart(sql);
break;
case ServerParse.MIGRATE:
MigrateHandler.handle(sql,c);
break;
case ServerParse.LOCK:
c.lockTable(sql);
break;
case ServerParse.UNLOCK:
c.unLockTable(sql);
break;
default:
if(readOnly){
LOGGER.warn(new StringBuilder().append("User readonly:").append(sql).toString());
c.writeErrMessage(ErrorCode.ER_USER_READ_ONLY, "User readonly");
break;
}
c.execute(sql, rs & 0xff);
}
}
確定sql類型的工作是交給ServerParse.parse(sql);來完成的。ServerParse的算法很簡單,就是一個字符串的匹配過程,下面拿insert和show兩種類型的sql來分析。
insert語句的匹配過程:
static int insertCheck(String stmt, int offset) {
//因為insert的長度最小是6,所以首先判斷sql語句的長度是不是大于6
if (stmt.length() > offset + 6) {
char c1 = stmt.charAt(++offset);
char c2 = stmt.charAt(++offset);
char c3 = stmt.charAt(++offset);
char c4 = stmt.charAt(++offset);
char c5 = stmt.charAt(++offset);
char c6 = stmt.charAt(++offset);
//一個字符一個字符進行匹配
if ((c1 == 'N' || c1 == 'n') && (c2 == 'S' || c2 == 's')
&& (c3 == 'E' || c3 == 'e') && (c4 == 'R' || c4 == 'r')
&& (c5 == 'T' || c5 == 't')
&& (c6 == ' ' || c6 == '\t' || c6 == '\r' || c6 == '\n')) {
//【注意】這里是直接返回INSERT
return INSERT;
}
}
return OTHER;
}
show語句的匹配過程:
static int sCheck(String stmt, int offset) {
//判斷字符s的下一個字符
if (stmt.length() > ++offset) {
switch (stmt.charAt(offset)) {
case 'A':
case 'a':
return savepointCheck(stmt, offset);
case 'E':
case 'e':
return seCheck(stmt, offset);
case 'H':
case 'h':
//下一個字符是s,繼續判斷是不是show
return showCheck(stmt, offset);
case 'T':
case 't':
return startCheck(stmt, offset);
default:
return OTHER;
}
}
return OTHER;
}
// SHOW' '
static int showCheck(String stmt, int offset) {
//因為show的長度最小是4,并且offset是從字符h開始的,所以首先判斷sql語句的長度是不是大于offset+3
if (stmt.length() > offset + 3) {
char c1 = stmt.charAt(++offset);
char c2 = stmt.charAt(++offset);
char c3 = stmt.charAt(++offset);
//逐個字符進行判斷
if ((c1 == 'O' || c1 == 'o') && (c2 == 'W' || c2 == 'w')
&& (c3 == ' ' || c3 == '\t' || c3 == '\r' || c3 == '\n')) {
//【注意】這里是offset左移8位再和SHOW進行或操作的結果。
//這個設計的巧妙之處就在于通過一個返回值,返回了兩個信息:offset和sql類型。
//唯一需要注意的就是sql類型的常量值不能超過8位,也就是0~255
return (offset << 8) | SHOW;
}
}
return OTHER;
}
//下面是Mycat目前支持的sql類型對應的常量值。
public static final int OTHER = -1;
public static final int BEGIN = 1;
public static final int COMMIT = 2;
public static final int DELETE = 3;
public static final int INSERT = 4;
public static final int REPLACE = 5;
public static final int ROLLBACK = 6;
public static final int SELECT = 7;
public static final int SET = 8;
public static final int SHOW = 9;
public static final int START = 10;
public static final int UPDATE = 11;
public static final int KILL = 12;
public static final int SAVEPOINT = 13;
public static final int USE = 14;
public static final int EXPLAIN = 15;
public static final int EXPLAIN2 = 151;
public static final int KILL_QUERY = 16;
public static final int HELP = 17;
public static final int MYSQL_CMD_COMMENT = 18;
public static final int MYSQL_COMMENT = 19;
public static final int CALL = 20;
public static final int DESCRIBE = 21;
public static final int LOCK = 22;
public static final int UNLOCK = 23;
public static final int LOAD_DATA_INFILE_SQL = 99;
public static final int DDL = 100;
public static final int MIGRATE = 203;
經過上面的分析,我們可以看出,ServerParse.parse(sql)的返回值有下面兩種情況:
1.直接返回sql類型。例如INSERT、DDL
2.返回sql類型+offset。例如SHOW、SELECT
所以,對于ServerParse.parse(sql)的返回值,后續的處理方式是下面這樣的。
int rs = ServerParse.parse(sql);
//獲取sql類型
int sqlType = rs & 0xff;
//獲取offset
int offset = rs >>> 8;
SQL的處理方式,大致可以分為下面幾種:
1.對于大部分的sql,都有專門的處理器就行處理。
EXPLAIN——ExplainHandler
EXPLAIN2——Explain2Handler
SET——SetHandler
SHOW——ShowHandler
SELECT——SelectHandler
START——StartHandler
BEGIN——BeginHandler
SAVEPOINT——SavepointHandler
KILL——KillHandler
USE——UseHandler
MIGRATE——MigrateHandler
2.對于不支持的sql,直接返回錯誤信息
KILL_QUERY、HELP
3.Mycat能直接處理掉的,直接處理
COMMIT、ROLLBACK、LOCK、UNLOCK、MYSQL_CMD_COMMENT、MYSQL_COMMENT
4.需要通過MySQL來處理的,執行execute
UPDATE、DELETE、DDL
在動力節點的Mycat教程當中還有更多的內容在等著大家去學習,當然也有相關的配套視頻教程能夠免費下載,課程內容詳細,通俗易懂,適合初學者,希望對大家的學習能夠有所幫助哦。
0基礎 0學費 15天面授
有基礎 直達就業
業余時間 高薪轉行
工作1~3年,加薪神器
工作3~5年,晉升架構
提交申請后,顧問老師會電話與您溝通安排學習