原文链接
- 作为Greenplum所有ETL操作基本原理,我们有必要稍微多介绍一些gpfdist的细节,以便于理解为什么它比其他工具速度更快,以及将来我们应如何对其进行改进。
- 本文将聚焦在gpfdist server和Greenplum 可读外部表之间的通信细节上,介绍数据流以及gpfdist外部表协议。
- Gpfdist使用HTTP协议和Greenplum segment通信。Gpfdist作为HTTP Server,负责将静态文件内容分发到GPDB,或从GPDB接收内容。每个Segment作为一个HTTP client,通过get或post从gpfdist获取数据。
- Gpfdist 协议使用特殊的HTTP Header传递GPDB和Gpfdist之间必要信息。下表列出了gpfdist 可读外部表用到的所有特殊HTTP Hader。
Header |
Message type |
Required |
description |
X-GP-XID |
Request |
Y |
transaction id |
X-GP-CID |
Request |
Y |
command id |
X-GP-SN |
Request |
Y |
scan counter |
X-GP-SEGMENT-ID |
Request |
N |
segment id |
X-GP-SEGMENT-COUNT |
Request |
N |
Segment count |
X-GP-LINE-DELIM-LENGTH |
Request |
N |
length of line ending |
X-GP-PROTO |
Request/Response |
Y |
protocol version, 0 or 1 |
X-GP-MASTER_HOST |
Request |
N |
GPDB master host address |
X-GP-MASTER_PORT |
Request |
N |
GPDB master port |
X-GP-CSVOPT |
Request |
Y |
CSV option format |
X-GP_SEG_PG_CONF |
Request |
N |
config path of segment |
X-GP_SEG_DATADIR |
Request |
N |
data directory of segment |
X-GP-DATABASE |
Request |
N |
current database name |
X-GP-USER |
Request |
N |
current login user |
X-GP-SEG-PORT |
Request |
N |
Segment port |
X-GP-SESSION-ID |
Request |
N |
gp_session_id |
X-GPFDIST-VERSION |
Response |
Y |
gpfdist server version |
- 上述表中
Message type
一列表示对应的Header字段应该出现在什么位置。
-
Request
表示出现在从Greenplum到gpfdist的HTTP请求头中
-
Response
表示出现在gpfdist的响应头中
- 并非所有字段都是必须的,具体参见
required
列
- 接下来将介绍最重要的几个字段:
X-GP-SN
- Greenplum外部表目前只支持顺序扫描,因此一些self-join的查询会对一个外部文件进行两次扫描。
X-GP-SN
用来记录当前扫描计数。Gpfdist将会使用该参数(连同command id和transaction id)来决定两个连接是否属于同一个session。
X-GP-PROTO
- 可读外部表有两种不同的协议,Protocol 0 和 Protocol 1 目前都是可用的。我们稍后会介绍他们的区别。该参数是gpfdist识别一个请求是否来自GPDB还是其他工具的重要依据。Gpfdist会拒绝没有
X-GP-PROTO
的连接。
X-GP-CSVOPT
- GPfdist 支持text和csv格式。
- Text很方便处理。每解析一行就发送数据给GPDB就可以了。
- 对于CSV格式,gpfdist需要知道更多关于转义(escape)或引号符(quote character)的信息。其格式为
m.x.q.n.h.
。各小写字母的含义如下:
Letter |
Meaning |
Example |
m |
Csv or not |
1 or 0 |
x |
Decimal number of escape byte |
9 (TAB), 124 (|), etc |
q |
Decimal number of quote byte |
34(“) |
n |
EOL type |
0-(EOL_UNKNOWN), 1-(EOL_LF), 2-(EOL_CR), 3-(EOL_CRLF) |
h |
With header or not |
1 or 0 |
可读外部表如何工作
- 当用户在可读外部表上执行一个select查询时,GPDB的每个segment各自发送已填好Header字段的HTTP GET请求到gpfdist,获取和此次查询相关的数据。
- 当gpfdist收到来自GPDB segment的请求后,按照每个请求中的<
transaction id
,command id
,scan count
> 参数,分组到各个session。所有属于同一个session的请求都是服务于同一个查询的,且由不同的segment并行发送而来。gpfdist会根据URL路径,读取本地文件的数据,直到已经读取足够多的数据到缓存之后才会发送。默认缓存大小是32KB,可通过-m
参数修改。如果gpfdist正在读的文件是一个管道,且管道输入很慢,它会持续等待,且不会响应其他请求。这是gpfdist当前版本的一个局限,在后续版本中将会得到改进。
可读外部表GUC
gp_external_max_segs
- 用于控制每个查询中,允许连接到单个gpfdist上的最大segment数量。即对于同一个查询session,最多允许
gp_external_max_segs
个segment连接到单个gpfdist server。默认值是64. Gpfdist在开始发送数据之前不需要等待所有segment都连接上来。每个segment一次只会创建一个到gpfdist的连接。第一个到gpfdist的连接会在gpfdist中创建一个session,之后属于同一个查询的连接都会加入这个session。只要session非空,gpfdist就会轮询地(round robin)通过这些连接向segment发送数据。
readable_external_table_timeout
- 该值控制GPDB在取消链接之前的等待时间。如果一个基于gpfdist的查询执行了很长时间,将会回错误
intermittent network connectivity issues
。用户可自己设置readable_external_table_timeout
的值。
可读外部表工作流
- 可读外部表的工作流是很简单的。GPDB发送HTTP请求到gpfdist,gpfdist返回HTTP响应和请求的数据。
- 有两个版本的协议可用于读外部表,通过
X-GP-PROTO
区分。我们接下来将会解释二者的区别。下面是可读外部表在单个segment和gpfdist之间的工作流。
Protocol 0
-
Protocol 0
很简单:使用HTTP头部字段来传递所有元数据。GPDB Segments 发送 GET请求,gpfdist向segment响应原始数据(raw data)
-
Protocol 0
最大的缺陷在于当gpfdist内部发生错误时,没有途径告知GPDB。如果gpfdist被用户杀掉,或原始文件损坏,gpfdist无法将此类消息告知GPDB。它唯一能做的就是立即关闭socket以结束HTTP连接。然后,这和普通的读成功时表现一致,因此GPDB无法识别读成功还是失败。
Protocol 1
-
Protocol 1
的目的就是解决Protocol 0
最致命的缺陷。它为每个数据块(称之为package)定义了一个新的数据格式,封装后再发送给GPDB。Gpfdist和GPDB不会直接发送原始数据。所有数据通过特殊格式封装,然后通过一个个package发送。Package 由消息构成。有3中类型的package和4种类型的消息。每种消息有3个字段:message type
,content length
,content
。
Field name |
field length |
Message type |
1 |
Content length |
4 |
Content data |
Value of Content length |
Message Type |
Full name |
content |
F |
filename |
file name that related data belong to |
O |
offset |
approx office(译注:疑为笔误,应该是offset) in file of current data |
D |
data |
the real data |
E |
error |
Error message of gpfdist |
L |
line number |
approx line number in file |
- 由于gpfdist从文件夹中查找文件时支持通配符,
F
消息(filename)总是显示数据所在的正确文件名。 Offset(O)和line number(L)是对数据在文件中位置的一个估值,在向用户展示错误信息的时候很有意义,比如当Greenplum解析数据内容时发现格式错误。可以通过该信息知道文件中发生错误的大概位置。
- Package 可对数据或消息进行封装。
- 对于数据package,消息序列是F,O,L,D。
- 如果没有更多消息可发送,gpfdist将发送空的package并携带长度为0的D消息,用于关闭连接。
- 如果有错误,gpfdist发送包含错误详细信息的E消息
Package type |
Message content |
Data |
FOLD |
End |
zero-length D |
Error |
E |
- 下图为Protocol 1的连接的典型示例
l
- 如果对HTTP协议熟悉,你会注意到上述消息和HTTP Chunked-mode很类似,但实际上二者是完全不同的,尽管其目的类似。
- Chunked-mode 非常适合传输流式数据
- Gpfdist的Protoco1响应,借鉴了HTTP并添加了其专有的特殊目的元数据。
概述
- 本文介绍了gpfdist可读外部表的两种协议。可通过wireshark工具理解这些协议,该工具能捕获TCP报文并以图形化方式展示通信过程。建议和gpfdist相同的的主机上运行wireshark,以观察来自GPDB segment的请求和响应。
- 新写一个基于上述协议、能够发送数据到GPDB的简单版本gpfdist并不难。事实上目前已有一些gpfdist的实现,比如Gemfire GPDB Connector 或者 一个C#版本的工具(https://github.com/pf-qiu/gpfdist.net)
参考
https://gpdb.docs.pivotal.io/43160/ref_guide/config_params/guc-list.html
https://github.com/greenplum-db/gpdb/tree/master/src/bin/gpfdist