读书人

H2数据库client-server协议全解(草稿-

发布时间: 2012-12-17 09:31:40 作者: rapoo

H2数据库client-server协议全解(草稿-不定期补充)

?

H2数据库的版本: 1.3.169

?

server端有一个listen线程,

org.h2.server.TcpServer.listen()

?

此线程一直在监听客户端连接,

一但收到客户端连接请求就开一个新的线程处理它(并未使用线程池、每连接每线程)

?

?

协议共有18条命令(从0到17)

?

?

1. 初始化阶段(或握手阶段)

?

client先发数据:

int     minClientVersionint     maxClientVersionString  db               //数据库名String  URLString  userNamebyte[]  userPasswordHash //32字节,使用SHA256算法对 userName + "@" + userPassword 进行hash,作为用户保存到数据库中的密码byte[]  filePasswordHash //32字节,使用SHA256算法对 "file@" + filePassword 进行hash,用来加密数据库文件int     keys length //url中的key-value参数个数keys length个{String keyString value}
?

?

当前支持的协议版本是6、7、8、9、10、11、12

?

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 valueString  messageString  sqlint     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)

------------------------------------

?

?

上面的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类似,实际上就是一个递增计数器)

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响应:

------------------------------------

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相关的所有资源(这条命令会释放大量资源)

?

?

读书人网 >其他数据库

热点推荐