MyCAT single database single table insert

1 Overview

The content form is mainly based on sequence diagram + core code.
If there is something wrong or unclear, please leave a message.
I am very confused about the form of content. If you have any suggestions, you are especially welcome.
WeChat ID: wangwenbin-server.

This article explains the code involved in [single library single table] insertion. The interaction is as follows:

Single library single table insertion diagram

The whole process, MyCAT Server process is as follows:

  1. Receive MySQL Client requests and parse SQL.
  2. Obtain routing results and perform routing.
  3. Get MySQL connection, execute SQL.
  4. In response to the execution result, send the result to MySQL Client.

Let's analyze it step by step and take a look at the source code together.

2. Receive the request and parse the SQL

[Single library single table] Insert (01 main process)

【 1 - 2 】

Receive a  MySQL command. Before [1], there was also a request for data to be read and split into a single MySQL SQL.

【 3 】

Different MySQL commands are distributed to different methods for execution. The core code is as follows:

 1: // 【FrontendCommandHandler.java】
 2: public class FrontendCommandHandler implements NIOHandler {
 3: 
 4:     @Override
 5:     public void handle(byte[] data) {
 6:     
 7:         // .... 省略部分代码
 8:         switch (data[4]) // 
 9:         {
10:             case MySQLPacket.COM_INIT_DB:
11:                 commands.doInitDB();
12:                 source.initDB(data);
13:                 break;
14:             case MySQLPacket.COM_QUERY: // 查询命令
15:                 // 计数查询命令
16:                 commands.doQuery();
17:                 // 执行查询命令
18:                 source.query(data);
19:                 break;
20:             case MySQLPacket.COM_PING:
21:                 commands.doPing();
22:                 source.ping();
23:                 break;
24:             // .... 省略部分case
25:         }
26:     }
27: 
28: }

INSERTSELECT// /  etc. SQL belongs to  , for details UPDATE, see: "MySQL Protocol Analysis #4.2 Client Command Request Message (Client -> Server)" .DELETEMySQLPacket.COM_QUERY

##【 4 】

Parse binary arrays into SQL. The core code is as follows:

 1: // 【FrontendConnection.java】
 2: public void query(byte[] data) {
 3: 	// 取得语句
 4: 	String sql = null;		
 5: 	try {
 6: 		MySQLMessage mm = new MySQLMessage(data);
 7: 		mm.position(5);
 8: 		sql = mm.readString(charset);
 9: 	} catch (UnsupportedEncodingException e) {
10: 		writeErrMessage(ErrorCode.ER_UNKNOWN_CHARACTER_SET, "Unknown charset '" + charset + "'");
11: 		return;
12: 	}		
13: 	// 执行语句
14: 	this.query( sql );
15: }

##【 5 】

Parse SQL types. The core code is as follows:

 1: // 【ServerQueryHandler.java】
 2: @Override
 3: public void query(String sql) {
 4: 	// 解析 SQL 类型
 5: 	int rs = ServerParse.parse(sql);
 6: 	int sqlType = rs & 0xff;
 7: 	
 8: 	switch (sqlType) {
 9: 	//explain sql
10: 	case ServerParse.EXPLAIN:
11: 		ExplainHandler.handle(sql, c, rs >>> 8);
12: 		break;
13: 	// .... 省略部分case
14: 		break;
15: 	case ServerParse.SELECT:
16: 		SelectHandler.handle(sql, c, rs >>> 8);
17: 		break;
18: 	// .... 省略部分case
19: 	default:
20: 		if(readOnly){
21: 			LOGGER.warn(new StringBuilder().append("User readonly:").append(sql).toString());
22: 			c.writeErrMessage(ErrorCode.ER_USER_READ_ONLY, "User readonly");
23: 			break;
24: 		}
25: 		c.execute(sql, rs & 0xff);
26: 	}
27: }
28: 
29:
30: //【ServerParse.java】
31: public static int parse(String stmt) {
32: 	int length = stmt.length();
33: 	//FIX BUG FOR SQL SUCH AS /XXXX/SQL
34: 	int rt = -1;
35: 	for (int i = 0; i < length; ++i) {
36: 		switch (stmt.charAt(i)) {
37: 		// .... 省略部分case			case 'I':
38: 		case 'i':
39: 			rt = insertCheck(stmt, i);
40: 			if (rt != OTHER) {
41: 				return rt;
42: 			}
43: 			continue;
44: 			// .... 省略部分case
45: 		case 'S':
46: 		case 's':
47: 			rt = sCheck(stmt, i);
48: 			if (rt != OTHER) {
49: 				return rt;
50: 			}
51: 			continue;
52: 			// .... 省略部分case
53: 		default:
54: 			continue;
55: 		}
56: 	}
57: 	return OTHER;
58: }

##【 6 】

Execute SQL, see below for detailed analysis, the core code is as follows:

 1: // 【ServerConnection.java】
 2: public class ServerConnection extends FrontendConnection {
 3: 	public void execute(String sql, int type) {
 4: 		// .... 省略代码
 5: 		SchemaConfig schema = MycatServer.getInstance().getConfig().getSchemas().get(db);
 6: 		if (schema == null) {
 7: 			writeErrMessage(ErrorCode.ERR_BAD_LOGICDB,
 8: 					"Unknown MyCAT Database '" + db + "'");
 9: 			return;
10: 		}
11: 
12: 		// .... 省略代码
13: 
14: 		// 路由到后端数据库,执行 SQL
15: 		routeEndExecuteSQL(sql, type, schema);
16: 	}
17: 	
18:     public void routeEndExecuteSQL(String sql, final int type, final SchemaConfig schema) {
19: 		// 路由计算
20: 		RouteResultset rrs = null;
21: 		try {
22: 			rrs = MycatServer
23: 					.getInstance()
24: 					.getRouterservice()
25: 					.route(MycatServer.getInstance().getConfig().getSystem(),
26: 							schema, type, sql, this.charset, this);
27: 
28: 		} catch (Exception e) {
29: 			StringBuilder s = new StringBuilder();
30: 			LOGGER.warn(s.append(this).append(sql).toString() + " err:" + e.toString(),e);
31: 			String msg = e.getMessage();
32: 			writeErrMessage(ErrorCode.ER_PARSE_ERROR, msg == null ? e.getClass().getSimpleName() : msg);
33: 			return;
34: 		}
35: 
36: 		// 执行 SQL
37: 		if (rrs != null) {
38: 			// session执行
39: 			session.execute(rrs, rrs.isSelectForUpdate() ? ServerParse.UPDATE : type);
40: 		}
41: 		
42:  	}
43: 
44: }

3. Get routing results

[Single database single table] Insert (02 get route)

【 1 - 2 】【 12 】

Get the routing main process. The core code is as follows:

 1: // 【RouteService.java】
 2: public RouteResultset route(SystemConfig sysconf, SchemaConfig schema,
 3: 		int sqlType, String stmt, String charset, ServerConnection sc)
 4: 		throws SQLNonTransientException {
 5: 	RouteResultset rrs = null;
 6: 	// .... 省略代码
 7: 	int hintLength = RouteService.isHintSql(stmt);
 8: 	if(hintLength != -1){ // TODO 待读:hint
 9: 		// .... 省略代码
10: 		}
11: 	} else {
12: 		stmt = stmt.trim();
13: 		rrs = RouteStrategyFactory.getRouteStrategy().route(sysconf, schema, sqlType, stmt,
14: 				charset, sc, tableId2DataNodeCache);
15: 	}
16: 
17: 	// .... 省略代码		return rrs;
18: }
19: // ⬇️⬇️⬇️【AbstractRouteStrategy.java】
20: @Override
21: public RouteResultset route(SystemConfig sysConfig, SchemaConfig schema, int sqlType, String origSQL,
22: 		String charset, ServerConnection sc, LayerCachePool cachePool) throws SQLNonTransientException {
23: 
24: 	// .... 省略代码
25: 
26: 	// 处理一些路由之前的逻辑;全局序列号,父子表插入
27: 	if (beforeRouteProcess(schema, sqlType, origSQL, sc) ) {
28: 		return null;
29: 	}
30: 
31: 	// .... 省略代码
32: 
33: 	// 检查是否有分片
34: 	if (schema.isNoSharding() && ServerParse.SHOW != sqlType) {
35: 		rrs = RouterUtil.routeToSingleNode(rrs, schema.getDataNode(), stmt);
36: 	} else {
37: 		RouteResultset returnedSet = routeSystemInfo(schema, sqlType, stmt, rrs);
38: 		if (returnedSet == null) {
39: 			rrs = routeNormalSqlWithAST(schema, stmt, rrs, charset, cachePool,sqlType,sc);
40: 		}
41: 	}
42: 
43: 	return rrs;
44: }

For detailed routing  analysis, we will open a separate article to avoid too much content and affect your understanding of the [insert] process and logic.

【 3 - 6 】

Routing preprocessing . When the following three situations are met, processing is performed:

{ 1 } Use a global sequence number :

insert into table (id, name) values (NEXT VALUE FOR MYCATSEQ_ID, 'name')

{ 2 } ER sub-table insert
{ 3 } Primary key insert using auto-incrementing ID:

insert into table (name) values ('name')
===>
insert into table (id, name) values (NEXT VALUE FOR MYCATSEQ_ID, 'name')
```  
情况 { 1 } { 3 } 情况类似,使用全局序列号。
核心代码如下:
```Java
  1: // ⬇️⬇️⬇️【AbstractRouteStrategy.java】
  2: private boolean beforeRouteProcess(SchemaConfig schema, int sqlType, String origSQL, ServerConnection sc)
  3: 		throws SQLNonTransientException {
  4: 	return  // 处理 id 使用 全局序列号
  5:             RouterUtil.processWithMycatSeq(schema, sqlType, origSQL, sc)
  6:             // 处理 ER 子表
  7: 			|| (sqlType == ServerParse.INSERT && RouterUtil.processERChildTable(schema, origSQL, sc))
  8:             // 处理 id 自增长
  9: 			|| (sqlType == ServerParse.INSERT && RouterUtil.processInsert(schema, sqlType, origSQL, sc));
 10: }

RouterUtil.java When dealing with SQL, considering performance, the implementation will be more C-style, and the code will not be posted. Portal: https://github.com/YunaiV/Mycat-Server/blob/1.6/src/main/java/io/mycat/ route/util/RouterUtil.java. 

(The warehouse has been gradually improved from the official Fork, and the Chinese annotations are gradually improved. Welcome to Star)

【 7 - 11 】

Added to the global sequencer ( ) when the pre- route handles global sequence numbers . MyCATSequnceProcessorThe processor generates IDs asynchronously, replacing  NEXT VALUE FOR MYCATSEQ_ regular expressions within the SQL. E.g:

insert into table (id, name) values (NEXT VALUE FOR MYCATSEQ_ID, 'name')
===>
insert into table (id, name) values (868348974560579584, 'name')

After asynchronous processing, call  ServerConnection#routeEndExecuteSQL(sql, type, schema) the method to re-execute the SQL.

The core code is as follows:

 1: // 【RouterUtil.java】
 2: public static void processSQL(ServerConnection sc,SchemaConfig schema,String sql,int sqlType){
 3: 	SessionSQLPair sessionSQLPair = new SessionSQLPair(sc.getSession2(), schema, sql, sqlType);
 4: 	MycatServer.getInstance().getSequnceProcessor().addNewSql(sessionSQLPair);
 5: }
 6: // ⬇️⬇️⬇️【MyCATSequnceProcessor.java】
 7: public class MyCATSequnceProcessor {
 8: 	private LinkedBlockingQueue<SessionSQLPair> seqSQLQueue = new LinkedBlockingQueue<SessionSQLPair>();
 9: 	private volatile boolean running=true;
10: 	
11: 	public void addNewSql(SessionSQLPair pair) {
12: 		seqSQLQueue.add(pair);
13: 	}
14: 
15: 	private void executeSeq(SessionSQLPair pair) {
16: 		try {
17: 			
18: 			// 使用Druid解析器实现sequence处理  @兵临城下
19: 			DruidSequenceHandler sequenceHandler = new DruidSequenceHandler(MycatServer
20: 					.getInstance().getConfig().getSystem().getSequnceHandlerType());
21: 
22: 			// 生成可执行 SQL :目前主要是生成 id
23: 			String charset = pair.session.getSource().getCharset();
24: 			String executeSql = sequenceHandler.getExecuteSql(pair.sql,charset == null ? "utf-8":charset);
25: 
26: 			// 执行 SQL
27: 			pair.session.getSource().routeEndExecuteSQL(executeSql, pair.type,pair.schema);
28: 		} catch (Exception e) {
29: 			LOGGER.error("MyCATSequenceProcessor.executeSeq(SesionSQLPair)",e);
30: 			pair.session.getSource().writeErrMessage(ErrorCode.ER_YES,"mycat sequnce err." + e);
31: 			return;
32: 		}
33: 	}
34: 	
35: 	class ExecuteThread extends Thread {
36: 		
37: 		public ExecuteThread() {
38: 			setDaemon(true); // 设置为后台线程,防止throw RuntimeExecption进程仍然存在的问题
39: 		}
40: 		
41: 		public void run() {
42: 			while (running) {
43: 				try {
44: 					SessionSQLPair pair=seqSQLQueue.poll(100,TimeUnit.MILLISECONDS);
45: 					if(pair!=null){
46:                         executeSeq(pair);
47: 					}
48: 				} catch (Exception e) {
49: 					LOGGER.warn("MyCATSequenceProcessor$ExecutorThread",e);
50: 				}
51: 			}
52: 		}
53: 	}
54: }

There is a question here: MyCATSequnceProcessor it is a single thread, will it have a certain impact on the insertion performance? We will do the performance test later.

4. Get MySQL connection and execute SQL

[Single database single table] Insert (03 execute SQL)

【 1 - 8 】

Get MySQL connection.

  • PhysicalDBNode : The physical database node.
  • PhysicalDatasource : The physical database data source.

【 9 - 13 】

Send SQL to MySQL Server, execute SQL.

5. Response to execute SQL result

[Single library single table] Insert (04 execution response)

【 1 - 4 】

Processes MySQL Server response packets.

【 5 - 8 】

Send the insert success result to MySQL Client.

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=325126778&siteId=291194637