FDW(FOREIGN DATA WRAPPER,外部数据包装器)可使得数据库可以访问不同的远程数据存储。
本提案将严格遵循 SQL/MED,通过对 Table Lifecycle 注入 Hook(复用 TiDB plan,在执行器 builder 时候使用自定义算子逻辑)使用 TiDB Plugin 等手段,使 TiDB 支持 FDW 功能。从而大幅度降低 TiDB 支持远程数据源的难度,提升 TiDB 社区活跃性。
TiDB 访问远程数据源是一个经久不衰的话题。不完全统计,两届 Hackathon 中,有各类远程数据源访问的项目。例如: CSV,Elasticsearch,Prometheus,Presto 等。但这类需求存在:与 TiDB 生态结合度不强,难以进行稳定官方维护,难以合并至 master 分支落地等问题。
本草案结合前人的项目经验,设计了一套 TiDB FDW 方案。同时有极高的落地性和可扩展性,吸引更多人来构建 TiDB 生态。
将 FDW 功能加入到 TiDB 中。严格循序 PostgreSQL FDW 与 SQL/MED。
用户可以通过以下流程实现一个 FDW 扩展。
- 遵守 plugin framework,创建 Engine Plugin。
- 实现 plugin 接口(具体参考 Implementation 部分 )
- build plugin
后即可使用 FDW 功能
同时为了提供样例和简化实现 FDW 流程,本提案将实现一个基于 gRPC 的 FDW 实现。用户使用自己擅长的语言,只需要掌握 gRPC,无需 Fork TiDB 和自行产生 plugin 即可开箱即用。
具体流程为:
TiDB -> gRPC_FDW -> gRPC_SERVER
READ
WIRTE
...
这里举使用 FDW csv 作为例子,用户的使用流程为:
# 标准 FDW 方式创建 使用 csv engine 的table
CREATE EXTENSION csv_fdw;
CREATE SERVER test_logs FOREIGN DATA WRAPPER csv_fdw;
CREATE FOREIGN TABLE people (city int, name char(255)) SERVER test_logs
OPTIONS ( filename '<YOUR_CSV_FILE_PATH>');
# 使用 MySQL 兼容方案创建 table
CREATE TABLE people(city int, name char(255)) ENGINE = csv ENGINE_ATTRIBUTE = "path=<YOUR_CSV_FILE_PATH>"
# 支持 insert
INSERT INTO people values(0, 'lfkdsk');
INSERT INTO people values(1, 'john');
INSERT INTO people values(2, 'tom');
INSERT INTO people values(3, 'iguory');
# 支持 select
SELECT * FROM people;
SELECT * FROM people where city = 2;
# 支持与 TiDB 普通 table: city 进行 join
SELECT city.cityName, people.name FROM city INNER JOIN people ON city.id=people.city
# 使用 EXPLAIN 可看到逻辑有下推到 Engine !
EXPLAIN SELECT city.cityName, people.name FROM city INNER JOIN people ON city.id=people.city
> ID | task |
Project_0 root
|_ HashRightJoin root
|- TableReader root
\- TableScan cop[tikv]
\_P 12 root
\_TableReader root
\_Selection cop[csv]
\_TableScan cop[csv]
# 支持 custom string query,这里例如 TSDB
SELECT * FROM TiDB_Metric where q=`avg(rate(TiDB_Index_Size{table=*,app='*'}[15m]) `;
Mysql 通过内置的方式实现了多个 Engine,尚未有很好的流程支持远程数据存储。MySQL 官方只支持使用 MySQL 协议的远程数据存储。具有很大的局限性。
PostgreSQL 制定 SQL/MED,实现了 FDW,通过 extension 的方式可以动态的开启相关 FDW 功能。
Prometheus 自身 store 支持 remote read/write,proms 生态存在大量的围绕该功能对 proms 进行拓展的第三方库。
TiDB 通过 Mysql 协议的兼容,实现了部分 engine API,但自身 Engine API 和自身生态 (TiKV/TiTan) 强绑定。无良好的插拔性。
虽然本提案的计划只对 TiDB 主分支代码进行一些 Hook 埋点。作为一种对 Mysql SQL 的扩展,对已有功能未有任何修改与破坏,不存在兼容性问题。
但因为以下原因,需要额外工作量。
- 但因为部分函数存在私有方法,需要批量重命名。
- 部分库和 Plugin 存在循环引用,需要针对性调整。
- 当前版本
TestSuits
不支持与Plugin Framework
的协同工作,需要针对性的优化。 - Mysql 的语法中并没有针对
SQL/MED
的设计,本提案遵循了Postgresql
接口,使得 TiDB FDW 功能并未遵循 mysql 的时间。 针对该问题,本提案中的实施细节中有方案进行解决与兼容。
同时因为涉及到 plugin lib 的修改,原有 plugin 都需要重新 build 后方可使用。 (源于 plugin framework 的局限)
实现分为两部分
-
对 parser 改造,支持新的扩增 FDW 语法
-
对 Engine 和 SQL Lifecycle 进行 hook 注入。可 engine 可以参与 SQL 执行,实现注入算子下推等功能。
-
扩展 SQL 语法,支持 PostgreSQL FDW 增加的语法。
对 TiDB Parser 进行提升,新增
CREATE EXTENSION
,CREATE SERVER
,CREATE FOREIGN TABLE
等功能。对 where 语句实现
where q=`custon query`
功能。 -
通过少量增加 TiDB engine hook。
- 对 DDL 进行 Hook 埋点,用于控制创建/删除 基于 FDW 的使用自定义 engine Table。
-
创建 Custom Engine 算子,实现数据的读与写
- CustomEngineScanExec,对应
PhysicalTableScan
- CustomEnginSelectionExec,对应
PhysicalSelection
- CustomEngineInsertExec,对应
Insert
- CustomEngineAggExec,对应
PhysicalHashAgg
- CustomEngineInedexReaderExec,对应
PhysicalIndexReader
- CustomEngineInedexLookUpReaderExec,对应
PhysicalIndexLookUpReader
- CustomEngineScanExec,对应
-
创建 Custom Engine Plan
TODO 还没想到用什么优雅的方式生成新的 Plan
-
使用 plugin framework,定义 engine plugin。
plugin 增加一个新的类型: Engine
实现接口
# DDL OnDropTable func(tb *model.TableInfo) error OnCreateTable func(tb *model.TableInfo) error # CustomEngineInsertExec OnInsertOpen func(ctx context.Context, meta *ExecutorMeta) error OnInsertNext func(ctx context.Context, rows [][]expression.Expression, meta *ExecutorMeta) error OnInsertClose func(meta *ExecutorMeta) error # CustomEngineScanExec OnReaderOpen func(ctx context.Context, meta *ExecutorMeta) error OnReaderNext func(ctx context.Context, chk *chunk.Chunk, meta *ExecutorMeta) error OnReaderClose func(meta ExecutorMeta) # CustomEnginSelectionExec OnSelectReaderNext func(ctx context.Context, chk *chunk.Chunk, filter []expression.Expression, meta *ExecutorMeta) error OnSelectReaderOpen func(ctx context.Context, filter []expression.Expression, meta *ExecutorMeta) error ...
优化已有的 test suits (当前调研发现,TiDB 当前的测试无法在使用 plugin 的情况下正常运行,将会在 hackathon 中修复该问题),一些库循环应用的修复。
TODO