H2数据库的版本: 1.3.169 server端有一个listen线程, org.h2.server.TcpServer.listen() 此线程一直在监听客户端连接, 一但收到客户端连接请求就开一个新的线程处理它(并未使用线程池、每连接每线程) 协议共有18条命令(从0到17) 1. 初始化阶段(或握手阶段) client先发数据: int minClientVersion int maxClientVersion String db //数据库名 String URL String userName byte[] userPasswordHash //32字节,使用SHA256算法对 userName + "@" + userPassword 进行hash,作为用户保存到数据库中的密码 byte[] filePasswordHash //32字节,使用SHA256算法对 "file@" + filePassword 进行hash,用来加密数据库文件 int keys length //url中的key-value参数个数 keys length个 { String key String value } 当前支持的协议版本是6到15 minClientVersion和maxClientVersion用来告诉server端当前client能支持的最小和最大协议版本是多少, 根据这两个参数,server端会选择一个合适的协议版本与client通信, 选择合适的协议版本的方法如下: 6<=minClientVersion<=12, 如果maxClientVersion>=12,那么就用12,否则使用minClientVersion server响应: STATUS_OK 或 STATUS_ERROR STATUS_OK: ------------------------------------ int STATUS_OK(1) int clientVersion ------------------------------------ STATUS_ERROR: ------------------------------------ int STATUS_ERROR(0) String SQLState value String message String sql int errorCode String trace ------------------------------------ 见org.h2.server.TcpServerThread.sendError(Throwable) 2. sessionId设置 client自己生成一个长度为64个字符的sessionId,然后传给server, client给server发送的是一个SESSION_SET_ID数据包 sessionId在server端会在内存中保存,当client调用org.h2.jdbc.JdbcStatement.cancel()时才用到 ------------------------------------ int SESSION_SET_ID(12) String sessionId ------------------------------------ server响应: 只有STATUS_OK ------------------------------------ int STATUS_OK(1) boolean autoCommit //从协议版本15开始支持 ------------------------------------ 上面的1和2在client和server之间每次建立一个新的connection(或称为session)时都会固定发送, (见: org.h2.engine.SessionRemote.initTransfer(ConnectionInfo, String, String)) 3. prepare阶段 接下来按执行SQL语句的类别分成两类: QUERY和UPDATE 执行SQL之前,需要先进行prepare, client给server发送的是一个SESSION_PREPARE_READ_PARAMS或SESSION_PREPARE数据包,两者格式几本上一样, SESSION_PREPARE_READ_PARAMS需要server响应的数据包中包含SQL语句中的参数元数据,SESSION_PREPARE则不需要, SESSION_PREPARE用在第二次对同一个SQL进行prepare时。 SESSION_PREPARE_READ_PARAMS ------------------------------------ int SESSION_PREPARE_READ_PARAMS(11) int id(执行当前sql时为此操作生成的一个计数器) String sql ------------------------------------ server响应: ------------------------------------ int STATUS_OK 或 STATUS_OK_STATE_CHANGED boolean isQuery boolean readonly int sql parameter size Parameter MetaData[] ------------------------------------ SESSION_PREPARE ------------------------------------ int SESSION_PREPARE(0) int id(执行当前sql时为此操作生成的一个计数器) String sql ------------------------------------ server收到上面两个包后,会按id把对应的SQL缓存起来, server响应: ------------------------------------ int STATUS_OK 或 STATUS_OK_STATE_CHANGED boolean isQuery boolean readonly int sql parameter size ------------------------------------ 相关源代码见: org.h2.command.CommandRemote.prepare(SessionRemote, boolean) 4. update 当进行java.sql.Statement.executeUpdate之类的操作时通常会触发update COMMAND_EXECUTE_UPDATE ------------------------------------ int COMMAND_EXECUTE_UPDATE(3) int id(对应prepare阶段生成的id) int sql parameter size Parameter[] ------------------------------------ server响应: ------------------------------------ int STATUS_OK 或 STATUS_OK_STATE_CHANGED int updateCount boolean autoCommit ------------------------------------ 相关源代码见: org.h2.command.CommandRemote.executeUpdate() 5. query (TODO 需要举例完善) 当进行java.sql.Statement.executeQuery之类的操作时通常会触发query COMMAND_EXECUTE_QUERY ------------------------------------ int COMMAND_EXECUTE_QUERY(2) int id(对应prepare阶段生成的id) int objectId(跟id类似,实际上就是一个递增计数器,在server端缓存查询结果集时,这个objectId就是结果集的缓存key) int maxRows int fetchSize int sql parameter size Parameter[] ------------------------------------ server响应: ------------------------------------ int STATUS_OK 或 STATUS_OK_STATE_CHANGED int columnCount int rowCount ResultColumn[] 结果集中列的元数据 ResultColumn { String Alias String SchemaName String TableName String ColumnName int ColumnType long ColumnPrecision int ColumnScale int DisplaySize boolean isAutoIncrement int Nullable } row result { boolean hasNext 如果hasNext==true 字段值,... } ------------------------------------ 相关源代码见: org.h2.command.CommandRemote.executeQuery(int, boolean) 6. 其他命令 6.1 获取结果集元数据 COMMAND_GET_META_DATA ------------------------------------ int COMMAND_GET_META_DATA(10) int id(对应prepare阶段生成的id) int objectId(跟id类似,实际上就是一个递增计数器,在server端缓存元数据结果集时,这个objectId就是结果集的缓存key) ------------------------------------ server响应: ------------------------------------ int STATUS_OK int columnCount int rowCount (固定是0) ResultColumn[] 结果集中列的元数据 ResultColumn { String Alias String SchemaName String TableName String ColumnName int ColumnType long ColumnPrecision int ColumnScale int DisplaySize boolean isAutoIncrement int Nullable } } ------------------------------------ 相关源代码见: org.h2.command.CommandRemote.getMetaData() 6.2 COMMAND_CLOSE COMMAND_CLOSE ------------------------------------ int COMMAND_CLOSE(4) int id(对应prepare阶段生成的id) ------------------------------------ server清除对应id的缓存,无响应包 相关源代码见: org.h2.command.CommandRemote.close() 6.3 RESULT_FETCH_ROWS RESULT_FETCH_ROWS ------------------------------------ int RESULT_FETCH_ROWS(5) int id(对应Query阶段生成的id) int fetchSize ------------------------------------ server响应: ------------------------------------ int STATUS_OK int columnCount row result { boolean hasNext 如果hasNext==true 字段值,... } ------------------------------------ 相关源代码见: org.h2.result.ResultRemote.fetchRows(boolean) 6.4 RESULT_CLOSE RESULT_CLOSE ------------------------------------ int RESULT_CLOSE(7) int id(对应Query阶段生成的id) ------------------------------------ server清除对应id的缓存,无响应包 相关源代码见: org.h2.result.ResultRemote.sendClose() 6.5 RESULT_RESET 在进行java.sql.ResultSet的first、beforeFirst、absolute会触发此操作 RESULT_RESET ------------------------------------ int RESULT_RESET(6) int id(对应Query阶段生成的id) ------------------------------------ server端的Result对象被reset,无响应包 相关源代码见: org.h2.result.ResultRemote.reset() 6.6 COMMAND_COMMIT 在集群环境下会自动提交模式会变成false,当执行操作完后,client端会自动发此命令给所有server,通知他们提交. COMMAND_COMMIT ------------------------------------ int COMMAND_COMMIT(8) ------------------------------------ server响应: ------------------------------------ int STATUS_OK 或 STATUS_OK_STATE_CHANGED ------------------------------------ 相关源代码见: org.h2.engine.SessionRemote.autoCommitIfCluster() 6.7 CHANGE_ID 通常在server端每个session的cache大小都是有限制的,默认是64个,当结果集的id很小(也就意味着很旧了) 那么需要从当前的SessionRemote得到一个新的id,然后通知server用这个新id与原来cache中的结果集重新映射一次. CHANGE_ID ------------------------------------ int CHANGE_ID(9) int 旧ID int 新ID ------------------------------------ server响应: ------------------------------------ server端无响应包 ------------------------------------ 相关源代码见: org.h2.result.ResultRemote.remapIfOld() 6.8 SESSION_SET_AUTOCOMMIT 改变autoCommit模式 SESSION_SET_AUTOCOMMIT ------------------------------------ int SESSION_SET_AUTOCOMMIT(15) boolean true or false ------------------------------------ server响应: ------------------------------------ int STATUS_OK ------------------------------------ 相关源代码见: org.h2.engine.SessionRemote.setAutoCommitSend(boolean) 6.9 SESSION_UNDO_LOG_POS 关闭连接时,看看server端是否还有撤消日志,如果有,则执行rollback操作 SESSION_UNDO_LOG_POS ------------------------------------ int SESSION_UNDO_LOG_POS(16) ------------------------------------ server响应: ------------------------------------ int STATUS_OK int Undo Log Pos ------------------------------------ 相关源代码见: org.h2.engine.SessionRemote.getUndoLogPos() 6.10 LOB_READ TODO 7 misc 还有两种特殊情况: 在org.h2.server.TcpServerThread.run()中特殊情理,对应if (db == null && originalURL == null)这个语句 SessionRemote.SESSION_CANCEL_STATEMENT ------------------------------------ int minClientVersion int maxClientVersion String null String null String sessionId int SessionRemote.SESSION_CANCEL_STATEMENT(13) int statement id ------------------------------------ 见: org.h2.engine.SessionRemote.cancelStatement(int) server响应: ------------------------------------ 不需要响应 ------------------------------------ SessionRemote.SESSION_CHECK_KEY ------------------------------------ int minClientVersion int maxClientVersion String null String null String sessionId int SessionRemote.SESSION_CHECK_KEY(14) ------------------------------------ 见: org.h2.store.FileLock.checkServer() server响应: STATUS_OK: ------------------------------------ int STATUS_OK(1) int clientVersion ------------------------------------ STATUS_ERROR: ------------------------------------ int STATUS_ERROR(0) String SQLState value String message String sql int errorCode String trace ------------------------------------ 见org.h2.server.TcpServerThread.sendError(Throwable) 第一个握手包处理完之后,就可以一直使用长连接来发命令了: 命令协议包: int operation 每个命令都有自己的格式 具体见: org.h2.server.TcpServerThread.process() client端发的第一个命令一般是SessionRemote.SESSION_PREPARE_READ_PARAMS/SESSION_PREPARE 具体见: org.h2.command.CommandRemote.prepare(SessionRemote, boolean) 三种类型的close命令,按级别由小到大分别是: RESULT_CLOSE 通知server关闭结果集,并在session的cache中删除结果集缓存 COMMAND_CLOSE 通知server关闭SQL命令,并在session的cache中删除命令缓存 SESSION_CLOSE 通知server关闭session,停掉线程,删除与session相关的所有资源(这条命令会释放大量资源) 流程: org.h2.server.TcpServer.listen() => org.h2.server.TcpServerThread.run() => org.h2.engine.Engine.createSession(ConnectionInfo) => org.h2.server.TcpServerThread.process()