当golang中使用sql查询数据库获取多条结果时,一般采用db.Query() + rows.Next() + rows.Scan()三个函数调用,那每个函数完成的具体功能到底是啥呢?
接口与driver
golang中数据库的实现方式为:database/sql提供公共的数据库操作接口,具体的数据库驱动由各个数据库自己实现,比如go-sql-driver/mysql,关系图如下:

图片来源:https://draveness.me/golang/docs/part4-advanced/ch09-stdlib/golang-database-sql/
mysql客户端-服务端交互
1 | |
引用:https://gohalo.me/post/mysql-protocol.html
响应报文格式
响应报文采用统一格式,如下:
1 | |
说明:
- 3字节:报文长度;
- 1字节:序号,每次客户端发起请求时,序号值都会从 0 开始计算。当一个请求由多个响应报文组成时,用来确定报文顺序,如果接收报文失序,则报错。
- N字节:具体数据部分。
引用:https://gohalo.me/post/mysql-protocol.html
ResultSet报文
响应报文包含三种:OK报文, Error报文, Result Set报文,下面主要说明最复杂的ResultSet报文:

说明: 图表包含了sql请求的所有响应情况,其中最左边方框+EOF/ERR报文就表示一个完整的ResultSet报文,主要包括五组报文:
- ResultSetHeaderPacket:结果集头报文,包含列的个数信息,报文个数=1;
- ColumnsPackets:列说明报文,包含每个列的具体说明,报文个数=column_count;
- EOFPacket:表示列说明报文结束,报文个数=1;
- RowsPackets:行内容报文,包含一行的具体内容,报文个数=row_num;
- EOFPacket/ERRPacket:表示整个Result Set报文的结束,报文个数=1;
参考:https://dev.mysql.com/doc/internals/en/com-query-response.html#packet-ProtocolText::Resultset
ResultSet报文例子
1 | |
说明: 包含5种报文:
-
length = 01 00 00, sequence_id = 01(ResultSetHeaderPacket)
- column_count = 01 (1)
-
length = 27 00 00, sequence_id = 02(ColumnsPackets)
-
catalog = 03 64 65 66 (“def”)
-
schema = 00 (“”)
-
table = 00 (“”)
-
org_table = 00 (“”)
-
name = 11 40 40 76 65 72 73 69 6f 6e 5f 63 6f 6d 6d 65 6e 74 (“@@version_comment”)
-
org_name = 00 (“”)
-
filler_1 = 0c
-
character_set = 08 00 (latin1_swedish_ci)
-
column_length = 1c 00 00 00 (28)
-
column_type = fd (Protocol::MYSQL_TYPE_VAR_STRING)
-
flags = 00 00
-
decimals = 1f (127)
-
filler_2 00 00
-
-
length = 05 00 00, sequence_id = 03(EOFPacket)
-
fe (EOF indicator)
-
warning_count = 00 00 (0)
-
status_flags = 02 00 (Protocol::StatusFlags = SERVER_STATUS_AUTOCOMMIT )
-
-
length = 05 00 00, sequence_id = 04(RowsPackets)
-
(ProtocolText::ResultsetRow)
-
1c 4d 79 53 51 4c 20 43 6f 6d 6d 75 6e 69 74 79 20 53 65 72 76 65 72 20 28 47 50 4c 29 (length = 28, string = “MySQL Community Server (GPL)”)
-
-
length = 05 00 00, sequence_id = 05(EOFPacket)
-
fe (EOF indicator)
-
warning_count = 00 00 (0)
-
status_flags = 02 00 (Protocol::StatusFlags = SERVER_STATUS_AUTOCOMMIT )
-
参考:https://dev.mysql.com/doc/internals/en/protocoltext-resultset.html
db.Query
1 | |
rows.Next
1 | |
rows.Scan
1 | |