mysql connector 执行 select 和 shardingshpere-proxy 的处理过程

use java mysql connector

// fake mysql select code
// ... datasource init
Connection conn = datasource.getConnection();
PreparedStatement pst = conn.prepareStatement("select id, task_name from t_task where id = ?");
pst.setLong(1, 31);
pst.executeQuery();

按照直觉, 既然用了 preparedStatement, 执行过一次后会在服务端缓存好预编译的语句, 之后就能省去这个解析过程,直接提交参数执行就好了

  1. 但是, mysql connector 默认创建的是 ClientPreparedStatement
public PreparedStatement prepareStatement(String sql, int resultSetType, int resultSetConcurrency) throws SQLException {
        try {
            synchronized(this.getConnectionMutex()) {
                this.checkClosed();
                ClientPreparedStatement pStmt = null;
                boolean canServerPrepare = true;
                String nativeSql = (Boolean)this.processEscapeCodesForPrepStmts.getValue() ? this.nativeSQL(sql) : sql;
                if ((Boolean)this.useServerPrepStmts.getValue() && (Boolean)this.emulateUnsupportedPstmts.getValue()) {
                    canServerPrepare = this.canHandleAsServerPreparedStatement(nativeSql);
                }
              // useServerPrepStmts = false
                if ((Boolean)this.useServerPrepStmts.getValue() && canServerPrepare) {
                      // ... 省略一些代码
                    } else {
                    	//... 省略一些代码
                            pStmt = (ClientPreparedStatement)this.clientPrepareStatement(nativeSql, resultSetType, resultSetConcurrency, false);
                        }
                    }
                } else {
                    pStmt = (ClientPreparedStatement)this.clientPrepareStatement(nativeSql, resultSetType, resultSetConcurrency, false);
                }

                return (PreparedStatement)pStmt;
            }
        } catch (CJException var17) {
            throw SQLExceptionsMapping.translateException(var17, this.getExceptionInterceptor());
        }
    }

因为默认并没有设置 useServerPrepStmts = true, 默认是false 去指定要求服务端缓存 创建的 clientPrepareStatement 客户端语句

  1. 是不是 ClientPreparedStatement 看着客户端侧进行一些元数据的缓存?
    pst.executeQuery(); 代码中有一段逻辑, 如果cacheResultSetMetadata=true的话,会缓存元数据,但是并没有
                  boolean cacheResultSetMetadata = (Boolean)locallyScopedConn.getPropertySet().getBooleanProperty(PropertyKey.cacheResultSetMetadata).getValue();
                    String origSql = ((PreparedQuery)this.query).getOriginalSql();
                    if (cacheResultSetMetadata) {
                        cachedMetadata = locallyScopedConn.getCachedMetaData(origSql);
                    }

所以,虽然每次还是从服务端拿返回 参数 和 resultSet 的一些元数据

  1. 最终发往服务端(这边场景是proxy) 的 sql 其实是非参数化的 com_query 命令
    image

  2. proxy 接收到 com_query 交由 MySQLComQueryPacketExecutor 处理

   public Collection<DatabasePacket<?>> execute() throws SQLException {
        ResponseHeader responseHeader = proxyBackendHandler.execute();
        if (responseHeader instanceof QueryResponseHeader) {
            return processQuery((QueryResponseHeader) responseHeader);
        }
        responseType = ResponseType.UPDATE;
        return processUpdate((UpdateResponseHeader) responseHeader);
    }

MySQLComQueryPacketExecutor 常规文本查询,在 proxy frontend 处理过程较为的简单,直接交给 backend 执行,后续就和 sharding-jdbc 核心处理逻辑一致了,比如 分库分表、读写分离、单库单表直接执行

扩展:

posted @ 2023-04-18 15:50  mushishi  阅读(112)  评论(3编辑  收藏  举报