介绍 Apache Arrow Flight SQL:加速数据库访问
发布于 2022年2月16日
作者 José Almeida, James Duong, Vinicius Fraga, Juscelino Junior, David Li, Kyle Porter, Rafael Telles
我们想向大家介绍 Flight SQL,这是一个由 Apache Arrow 社区开发的、用于与 SQL 数据库交互的新型客户端-服务器协议。它利用了 Arrow 内存中列式格式和 Flight RPC 框架。
Flight SQL 旨在提供与现有 API(如 JDBC 和 ODBC)大体相似的功能,包括执行查询、创建预处理语句(prepared statements),以及获取有关支持的 SQL 方言、可用类型、已定义表等的元数据。然而,通过基于 Apache Arrow 构建,Flight SQL 使客户端能够轻松地与原生支持 Arrow 的数据库通信,而无需进行数据转换。通过使用 Flight,它提供了一种高效的网络传输格式实现,该实现开箱即用地支持加密和身份验证等功能,同时还允许进行并行数据访问等进一步优化。
虽然它可以直接用于数据库访问,但它并非 JDBC/ODBC 的直接替代品。相反,Flight SQL 是一个具体的网络协议/驱动实现,可以为 JDBC/ODBC 驱动提供支持,并减轻数据库的实现负担。
动机
尽管像 JDBC 和 ODBC 这样的标准几十年来一直为用户提供了良好的服务,但对于希望使用 Apache Arrow 或列式数据的数据库和客户端来说,它们存在不足。像 JDBC 或 PEP 249 这样的行式 API 在这种情况下需要对数据进行转置。对于本身就是列式存储的数据库而言,这意味着数据必须被转置两次——一次是为了以行式呈现给 API,另一次是消费者为了将其恢复为列式。同时,虽然像 ODBC 这样的 API 确实提供了对结果缓冲区的批量访问,但这些数据仍必须被复制到 Arrow 数组中,才能与更广泛的 Arrow 生态系统(例如 Turbodbc 这样的项目)一起使用。Flight SQL 的目标就是消除这些中间步骤。
Flight SQL 意味着数据库服务器可以实现一个从一开始就围绕 Apache Arrow 和列式数据设计的标准接口。就像 Arrow 提供了一个标准的内存格式一样,Flight SQL 让开发者不必从头设计和实现一个全新的网络协议。如前所述,Flight 已经实现了诸如网络传输加密和请求认证等功能,数据库无需重复实现这些功能。
对于客户端而言,Flight SQL 提供了对查询结果的批量访问,而无需从其他 API 或格式转换数据。此外,通过将实现网络协议的工作推给 Flight 和 Flight SQL 库,每种客户端语言或驱动程序需要编写的代码就更少了。并且,通过底层使用 Flight,客户端和服务器可以协作实现并行数据访问等优化,这是 Flight 最初的目标之一。数据库可以向 Flight SQL 客户端返回多个“端点”(endpoints),客户端随后可以并行地从所有端点拉取数据,从而使数据库后端能够水平扩展。
Flight SQL 基础
Flight SQL 充分利用了 Flight RPC 框架及其可扩展性,通过 Protobuf 定义了额外的请求/响应消息。我们将简要介绍 Flight SQL 协议,但 C++ 和 Java 已经实现了管理大部分此类工作的客户端。完整的协议可以在 GitHub 上找到。
大多数请求遵循以下模式:
- 客户端使用一个已定义的 Protobuf 消息构造一个请求。
- 客户端通过 GetSchema RPC 方法(获取响应的模式)或 GetFlightInfo RPC 方法(执行请求)发送请求。
- 客户端向 GetFlightInfo 返回的端点发出一个或多个请求以获取响应。
Flight SQL 定义了查询数据库元数据、执行查询或操作预处理语句的方法。
元数据请求
- CommandGetCatalogs: 列出数据库中的目录(catalogs)。
- CommandGetCrossReference: 列出引用了特定其他表的外键列。
- CommandGetDbSchemas: 列出目录中的模式(schemas)。
- CommandGetExportedKeys: 列出引用了某个表的外键。
- CommandGetImportedKeys: 列出某个表的外键。
- CommandGetPrimaryKeys: 列出某个表的主键。
- CommandGetSqlInfo: 获取关于数据库本身及其支持的 SQL 方言的信息。
- CommandGetTables: 列出目录/模式中的表。
- CommandGetTableTypes: 列出支持的表类型(例如,表、视图、系统表)。
查询
- CommandStatementQuery: 执行一次性的 SQL 查询。
- CommandStatementUpdate: 执行一次性的 SQL 更新查询。
预处理语句
- ActionClosePreparedStatementRequest: 关闭一个预处理语句。
- ActionCreatePreparedStatementRequest: 创建一个新的预处理语句。
- CommandPreparedStatementQuery: 执行一个预处理语句。
- CommandPreparedStatementUpdate: 执行一个更新数据的预处理语句。
例如,要列出所有表:
执行一个查询:
创建并执行一个预处理语句来插入行:
入门
请注意,虽然 Flight SQL 作为 Apache Arrow 7.0.0 的一部分发布,但它仍在开发中,详细文档即将推出。不过,C++ 和 Java 中已经提供了实现,它们提供了一个可用的底层客户端以及一个可供实现的服务器框架。
对于感兴趣的人,源代码中提供了一个封装了 Apache Derby 的服务器实现和一个封装了 SQLite 的实现。此外,还提供了一个演示客户端的简单命令行界面 (CLI)。最后,我们可以看一个执行查询并获取结果的简短示例:
flight::FlightCallOptions call_options;
// Execute the query, getting a FlightInfo describing how to fetch the results
std::cout << "Executing query: '" << FLAGS_query << "'" << std::endl;
ARROW_ASSIGN_OR_RAISE(std::unique_ptr<flight::FlightInfo> flight_info,
client->Execute(call_options, FLAGS_query));
// Fetch each partition sequentially (though this can be done in parallel)
for (const flight::FlightEndpoint& endpoint : flight_info->endpoints()) {
// Here we assume each partition is on the same server we originally queried, but this
// isn't true in general: the server may split the query results between multiple
// other servers, which we would have to connect to.
// The "ticket" in the endpoint is opaque to the client. The server uses it to
// identify which part of the query results to return.
ARROW_ASSIGN_OR_RAISE(auto stream, client->DoGet(call_options, endpoint.ticket));
// Read all results into an Arrow Table, though we can iteratively process record
// batches as they arrive as well
std::shared_ptr<arrow::Table> table;
ARROW_RETURN_NOT_OK(stream->ReadAll(&table));
std::cout << "Read one partition:" << std::endl;
std::cout << table->ToString() << std::endl;
}
完整源代码可在 GitHub 上获取。
下一步计划 & 如何参与
与现有的库(如 PyODBC)相比,Arrow Flight 的速度已经快了多达 20 倍(约在视频 00:21:00 处)。Flight SQL 将把这些性能优势打包到一个标准接口中,供客户端和数据库实现。
预计协议将得到进一步的完善和扩展。其中一些工作是为了能够在 Flight SQL 之上实现像 JDBC 这样的 API;一个 JDBC 驱动程序正在积极开发中。虽然这会再次引入数据转换的开销,但它意味着一个数据库只需实现 Flight SQL,就可以同时为原生支持 Arrow 的客户端和传统客户端提供服务。未来的其他改进可能包括 Python 绑定、ODBC 驱动程序等。