<TL;DR> 正在接收多少字节总共当SELECT *
针对(小)表发出命令时?
</TL;DR>
我有一张 PostgreSQL 表 - 非常简单(参见 fiddle这里):
CREATE TABLE test
(
a INT NOT NULL,
b TEXT NOT NULL
);
已填充:
INSERT INTO test VALUES
(1, 'afasdfasdf'),
(2, 'afasdfasdafasdfasf'),
(3, 'afasdfasdfasdfasdfsdf'),
(4, 'af'),
(5, 'afasdfa'),
(6, '我爱你');
并运行此查询
SELECT
SUM(PG_COLUMN_SIZE(temp.a)) AS int_sz,
SUM(CHARACTER_LENGTH(temp.b)) AS char_len,
SUM(OCTET_LENGTH(temp.b)) AS oct_len,
SUM(OCTET_LENGTH(temp.*::TEXT)) AS oct_len_total
FROM
(SELECT * FROM test) AS temp;
给出结果:
int_sz char_len oct_len oct_len_total
24 61 67 91
因此,我们可以看到char_len
!= oct_len
(每个汉字占用 3 个字节 - UTF8)——所以字节的总长度为 91——很好。
然后我用这个文章设置 wireshark 客户端来监控psql
(PostgreSQL 命令行客户端)流量。我首先启动数据库并登录,以便此流量(握手、数据库启动消息等)不会出现在 wireshark 日志中。然后我运行(以 root 身份在另一个终端窗口中):
tshark -i any -f 'tcp port 5432' -d tcp.port==5432,pgsql -O pgsql
并在psql
窗口中发出命令(此时我应该提到,我不确定这些Frame
命令是否来自客户端或者服务器,但就我的目的而言,这并不重要——我只是想知道数据总量已传输):
SELECT * FROM test;
wireshark 日志中出现以下内容:
Frame 1: 93 bytes on wire (744 bits), 93 bytes captured (744 bits) on interface any, id 0
Linux cooked capture v1
Internet Protocol Version 4, Src: 192.168.234.75, Dst: 192.168.234.75
Transmission Control Protocol, Src Port: 30050, Dst Port: 5432, Seq: 1, Ack: 1, Len: 25
PostgreSQL
Type: Simple query
Length: 24
Query: SELECT * FROM test;
现在查询字符串本身的长度为 19 个字节。但是,我有:
- 线路上 93 字节
- 莱恩 25
- 长度 24
在这种情况下,数据总量是多少(我很确定 - 由客户端传输) - 不同数字的含义是什么?
之后Frame 1
,我们Frame 2
在wireshark日志中看到:
Frame 2
由两部分组成,一个标题,然后是各个行的行数据(相当确定这是由客户端接收并由服务器传输的)。
标头:
Frame 2: 298 bytes on wire (2384 bits), 298 bytes captured (2384 bits) on interface any, id 0
Linux cooked capture v1
Internet Protocol Version 4, Src: 192.168.234.75, Dst: 192.168.234.75
Transmission Control Protocol, Src Port: 5432, Dst Port: 30050, Seq: 1, Ack: 26, Len: 230
PostgreSQL
Type: Row description
Length: 46
Field count: 2
Column name: a
Table OID: 24583
Column index: 1
Type OID: 23
Column length: 4 -- The length of an INT - 4 bytes.
Type modifier: -1
Format: Text (0)
Column name: b
Table OID: 24583
Column index: 2
Type OID: 25
Column length: -1 -- means it's a TEXT field with no specified length
Type modifier: -1
Format: Text (0)
因此,标头包含298 bytes on wire
,并且Len: 230
- 因此,这似乎是客户端从服务器接收的总数据的摘要。这是正确的吗?
总数是 298 还是 230 - 相对于表格本身的 91?请解释一下这些数字,我们将不胜感激。
两行数据:
First row:
PostgreSQL
Type: Data row
Length: 25
Field count: 2
Column length: 1 -- an INT (i.e. test.a) is 4 bytes - why 1?
Data: 31
Column length: 10 -- corresponds exactly to the size of the TEXT column (i.e. test.b in fiddle) in bytes
Data: 61666173646661736466
Length:
25、31Data:
和Column Length:
1,第二个的长度Data:
是20个字符?
这些数字代表什么意思?
Second row:
PostgreSQL
Type: Data row
Length: 33
Field count: 2
Column length: 1
Data: 32
Column length: 18 -- corresponds exactly to the size of the column (t.b) in bytes
Data: 616661736466617364616661736466617366
...
... snipped for brevity - see bottom of post for the other 4 records.
...
与第 1 行的问题相同——究竟收到了什么?
在第 2 帧结束时,我们有以下内容(清理/握手?):
PostgreSQL
Type: Command completion
Length: 13
Tag: SELECT 6
服务器告诉客户端,数据传输完毕?还有:
PostgreSQL
Type: Ready for query
Length: 5
Status: Idle (73)
服务器告诉客户端,它已准备好执行更多工作?并且此Frame 3
部分出现在每个命令之后:
Frame 3: 68 bytes on wire (544 bits), 68 bytes captured (544 bits) on interface any, id 0
Linux cooked capture v1
Internet Protocol Version 4, Src: 192.168.234.75, Dst: 192.168.234.75
Transmission Control Protocol, Src Port: 30050, Dst Port: 5432, Seq: 26, Ack: 231, Len: 0
那么,客户端运行命令时接收的数据总量SELECT
是 3 帧的总和吗bytes on wire
?还是不同的数字?
如能提供任何解释、资源指针、URL、参考资料等,我们将不胜感激。
====================== 上面的 wireshark 日志摘录中未包含 4 条记录 ===========
PostgreSQL
Type: Data row
Length: 36
Field count: 2
Column length: 1
Data: 33
Column length: 21
Data: 616661736466617364666173646661736466736466
PostgreSQL
Type: Data row
Length: 17
Field count: 2
Column length: 1
Data: 34
Column length: 2
Data: 6166
PostgreSQL
Type: Data row
Length: 22
Field count: 2
Column length: 1
Data: 35
Column length: 7
Data: 61666173646661
PostgreSQL
Type: Data row
Length: 24
Field count: 2
Column length: 1
Data: 36
Column length: 9 -- 3 Chinese character = 9 bytes, i.e. 3 per character
Data: e68891e788b1e4bda0
答案1
在这种情况下,数据总量是多少(我很确定 - 由客户端传输) - 不同数字的含义是什么?
不同数字的含义是有效载荷大小在每一层。您有一个 Postgres 协议数据包,指示其自己的有效负载长度(标签/长度/有效负载格式,显然带有一个未计算的 1 字节“类型”标签头),并且这些数据包中的几个进入指示其自己的有效负载长度的 TCP 段,并且该 TCP 段(带有其标头)进入指示其自身长度的 IP 数据包,并且该 IP 数据包进入 Wireshark 指示其全长的以太网帧。
Ethernet frame [93]
├─ Ethernet header [always 14]
└─ Payload: IPv4 packet [79]
├─ IPv4 header [usually 20]
└─ Payload: TCP segment [59]
├─ TCP segment header [34? (20 + options)]
└─ Payload: Postgres data [25]
├─ Postgres packet [25]
│ ├─ Tag field (packet type) [1]
│ ├─ Length field ┐ (range of the
│ └─ Data field ┘ "Length" field)
├─ (same TCP segment could have than one Postgres packet)
└─ (etc)
并在 psql 窗口中发出命令(我应该在此时提到,我不确定帧是来自客户端还是服务器,但就我的目的而言,这并不重要 - 我只想知道传输的总数据量):
从技术上讲,“帧长度”可以告诉你客户端计算机最终接收的字节数,但当同一个 IP 数据包跨不同的链路类型时,帧开销会逐跳变化(例如,Wi-Fi 连接的客户端将产生与以太网连接的服务器接收的长度不同的帧)。
只有帧有效负载(IP 数据包)在整个路径上保持不变,因此通常后者的测量更有意义。
Wireshark 不会在其摘要中报告 IP 数据包(帧有效负载)的大小,而且它似乎没有一个字段可以直接提供该信息,因此您必须专门向它询问ip.len
和ipv6.plen
字段。(然而,这两者有一个重要的区别;ip.len
对于 IPv4,IPv6 包括 IP 标头本身,而ipv6.plen
IPv6 则不包括,因此您必须向后者添加固定的 40 个字节。)
例如,要获取单个字段(包括 ip.version 以允许计算 IPv6 的完整长度):
tshark -i wlan0 -T fields -e ip.version -e ip.len -e ipv6.plen
列长度:1 - INT(即 test.a)是 4 个字节 - 为什么是 1?
数据包格式不必与存储格式相同。如果值适合 1 个字节,Postgres 显然会将其传输为 1 个字节 - 无论是否包含前导零,它仍然是相同的整数。
Column length: 10 -- corresponds exactly to the size of the TEXT column (i.e. test.b in fiddle) in bytes Data: 61666173646661736466
长度:25,数据:31 和列长度:1,第二个数据的长度:是 20 个字符?
Wireshark 正在向您显示 20十六进制数字,而不是 20 个字符。一个十六进制数字代表四位,因此每个二数字代表一个字节,为您提供 10 个字节的实际数据。
Wireshark 的“数据包树”输出是为了便于阅读,不是为了精确。此时我建议将捕获的数据加载到图形化的 Wireshark 中,这样您就可以单击数据包树中的每个字段,并查看“原始字节”窗格中突出显示的相应字节 - 这会告诉您例如“列长度”本身是否占用 1 个字节、2 个或 4 个字节,或者“数据”字段是 ASCII 还是十六进制字节、整数还是其他内容。