Flink MongoDB SQL Connector Scan/Lookup/Sink 全打通,Upsert、分片集群与缓存一篇讲透

官方文档目前明确提示:Flink 2.2 还没有可用的 MongoDB connector 。 (Apache Nightlies)

同时,MongoDB connector 不在 Flink 二进制发行包 里,跑集群需要你自己把 connector jar 带上(放 lib/ 或打成 uber-jar)。 (Apache Nightlies)

如果你在稳定版本线上落地(例如 Flink 1.20),Maven 中央仓库能找到对应依赖,例如:

  • DataStream 连接器:org.apache.flink:flink-connector-mongodb(例如 2.0.0-1.20) (Maven Repository)
  • SQL 连接器:org.apache.flink:flink-sql-connector-mongodb(例如 2.0.0-1.20) (Maven Repository)

2、三种能力先记住一句话:主键决定 Sink 模式

MongoDB SQL Connector 的写入有两种模式:

  • DDL 定义了 PRIMARY KEY :Sink 进入 Upsert 模式,可以接收 UPDATE/DELETE 的 changelog
  • DDL 不定义 PRIMARY KEY :只能 Append 模式 ,只允许 INSERT(追加写) (Apache Nightlies)

这点决定了你后面到底能不能用 INSERT INTO ... SELECT ... 去承接一个会产生更新/撤回的 Flink SQL 结果(比如聚合、Join、TopN、维表更新等)。

3、快速开始:建表、写入、查询、维表 Join 一次跑通

3.1 注册 MongoDB 表(推荐 Upsert:显式主键)

sql 复制代码
CREATE TABLE MyUserTable (
  _id STRING,
  name STRING,
  age INT,
  status BOOLEAN,
  PRIMARY KEY (_id) NOT ENFORCED
) WITH (
  'connector' = 'mongodb',
  'uri' = 'mongodb://user:password@127.0.0.1:27017',
  'database' = 'my_db',
  'collection' = 'users'
);

NOT ENFORCED 的含义是:Flink 不帮你在运行时强制校验唯一性,但会把这个主键用于 changelog/upsert 语义与下游映射。

3.2 写入 MongoDB(从别的表 T 写入)

sql 复制代码
INSERT INTO MyUserTable
SELECT _id, name, age, status FROM T;

3.3 扫描读取 MongoDB(有界 Scan)

sql 复制代码
SELECT _id, name, age, status FROM MyUserTable;

3.4 作为维表做 temporal join(Lookup Source,同步)

sql 复制代码
SELECT *
FROM myTopic
LEFT JOIN MyUserTable FOR SYSTEM_TIME AS OF myTopic.proctime
ON myTopic.key = MyUserTable._id;

以上 DDL 与用法是官方文档给出的标准示例。 (Apache Nightlies)

4、主键与 _id 映射机制:为什么 Upsert 天生"幂等"

MongoDB 里 _id 是集合的保留主键(唯一且不可变),MongoDB connector 会用你在 DDL 定义的主键字段来生成 MongoDB 文档 _id,从而实现幂等 Upsert:

  • 主键只有 1 列 :直接把该字段转成 BSON 值作为 _id
  • 主键多列 :把多列按 DDL 顺序组合成一个 BSON document 作为 _id,形如:_id: {f1: v1, f2: v2} (Apache Nightlies)

注意两个很容易踩的点:

1)如果 DDL 里有 _id 字段,但主键却不是 _id,会产生歧义(到底谁是 MongoDB 的 _id)。官方建议:要么把 _id 作为主键,要么改名避免冲突。 (Apache Nightlies)

2)MongoDB 的 _id 不能是 Array,且如果 _id 是嵌套子字段,子字段名不能以 $ 开头。 (Apache Nightlies)

Flink 发生失败恢复后,会从最近一次成功 checkpoint 之后重放一段数据(at-least-once 常态),这会带来"重放写入"。Upsert 写入因为 _id 稳定,可以天然幂等,避免重复数据与唯一键冲突。 (Apache Nightlies)

另外一个隐藏坑:INSERT OVERWRITE 写 MongoDB 时会强制使用 Upsert;如果你没在 DDL 定义主键,会被拒绝。 (Apache Nightlies)

MongoDB 对分片集合的更新/Upsert 有额外约束:Upsert 的 filter 必须包含 shard key 。因此在 Flink SQL 建 sink 表时,需要用 PARTITIONED BY 声明 shard key,让 connector 在运行时把 shard key 写进 filter。 (Apache Nightlies)

示例(官方思路):

sql 复制代码
CREATE TABLE MySinkTable (
    _id       BIGINT,
    shardKey0 STRING,
    shardKey1 STRING,
    status    STRING,
    PRIMARY KEY (_id) NOT ENFORCED
) PARTITIONED BY (shardKey0, shardKey1) WITH (
    'connector' = 'mongodb',
    'uri' = 'mongodb://user:password@127.0.0.1:27017',
    'database' = 'my_db',
    'collection' = 'users'
);

INSERT INTO MySinkTable
SELECT _id, shardKey0, shardKey1, status FROM T;

还支持静态/动态分区写入:

sql 复制代码
-- 静态分区
INSERT INTO MySinkTable PARTITION(shardKey0 = 'value0', shardKey1 = 'value1')
SELECT 1, 'INIT';

-- shardKey0 静态 + shardKey1 动态
INSERT INTO MySinkTable PARTITION(shardKey0 = 'value0')
SELECT 1, 'value1', 'INIT';

重要限制(非常关键):虽然 MongoDB 4.2+ shard key 可变,但在 Flink SQL 的 upsert 场景下,如果 shard key 发生更新,connector 拿不到"旧 shard key"去构造 filter,可能导致重复记录风险,因此仍建议 shard key 保持不可变。 (Apache Nightlies)

6、Scan Source 并行加速:4 种分区策略怎么选

MongoDB 批量扫描(Scan Source)支持并行分区读取,核心配置是:

  • scan.partition.strategy:single / sample / split-vector / sharded / default
  • scan.partition.size:分区粒度(默认 64mb)
  • scan.fetch-size:每次 round-trip 拉多少文档(默认 2048)
  • scan.cursor.no-timeout:是否禁用 10 分钟 cursor 超时(默认 true,但超过 30 分钟处理一个 batch 仍可能导致 session 过期) (Apache Nightlies)

策略选择建议(按文档特性翻译成工程话):

  • single:全表一个分区,最稳但最慢(小表/测试)
  • sample:抽样生成分区,速度快但可能不均匀(数据分布很怪时慎用)
  • split-vector:非分片集合首选,分区快且更均匀,但需要 splitVector 权限
  • sharded:分片集合首选,直接用 config.chunks 的 chunk 范围做分区,快且均匀,但需要读 config 库权限
  • default:分片集合走 sharded,否则走 split-vector (Apache Nightlies)

一个常用的"批量导入/离线回灌"模板:

sql 复制代码
CREATE TABLE mongo_scan_users (
  _id STRING,
  name STRING,
  age INT,
  status BOOLEAN,
  PRIMARY KEY (_id) NOT ENFORCED
) WITH (
  'connector' = 'mongodb',
  'uri' = 'mongodb://user:password@127.0.0.1:27017',
  'database' = 'my_db',
  'collection' = 'users',
  'scan.partition.strategy' = 'default',
  'scan.partition.size' = '64mb',
  'scan.fetch-size' = '2048'
);

7、Lookup 维表缓存:PARTIAL 缓存让 temporal join 性能起飞,但要权衡新鲜度

MongoDB connector 的 Lookup Source 目前只支持**同步(sync)**模式。为了降低维表 join 对 MongoDB 的压力,提供了 lookup.cache = PARTIAL 的"进程内缓存"(每个 TaskManager 一份)。 (Apache Nightlies)

关键配置项:

  • lookup.cache = PARTIAL:开启缓存
  • lookup.partial-cache.max-rows:最大缓存行数,超出会淘汰最旧的
  • lookup.partial-cache.expire-after-write:写入后 TTL
  • lookup.partial-cache.expire-after-access:访问后 TTL
  • lookup.partial-cache.caching-missing-key:是否缓存"查不到"的空结果(默认 true)
  • lookup.max-retries / lookup.retry.interval:失败重试 (Apache Nightlies)

典型的维表 join DDL(带缓存):

sql 复制代码
CREATE TABLE dim_users (
  _id STRING,
  name STRING,
  age INT,
  status BOOLEAN,
  PRIMARY KEY (_id) NOT ENFORCED
) WITH (
  'connector' = 'mongodb',
  'uri' = 'mongodb://user:password@127.0.0.1:27017',
  'database' = 'my_db',
  'collection' = 'users',
  'lookup.cache' = 'PARTIAL',
  'lookup.partial-cache.max-rows' = '200000',
  'lookup.partial-cache.expire-after-write' = '10 min',
  'lookup.partial-cache.caching-missing-key' = 'true',
  'lookup.max-retries' = '3',
  'lookup.retry.interval' = '1 s'
);

权衡点务必写在博客里:缓存可能不是最新数据,TTL 越短越"新鲜",但数据库压力越大;TTL 越长吞吐越高,但维表可能"旧"。 (Apache Nightlies)

8、Filter Pushdown:把能下推的过滤交给 MongoDB

MongoDB connector 支持将部分 Flink SQL 过滤条件下推到 MongoDB,从而减少网络 IO 与 Flink 端处理量。你可以用:

支持的过滤映射包括:=, <>, >, >=, <, <=, IS NULL, IS NOT NULL, OR, AND 等,对应 MongoDB 的 $eq/$ne/$gt/$gte/$lt/$lte/$or/$and。 (Apache Nightlies)

建议:

如果你的 MongoDB 集群压力大、你又担心下推导致执行计划不可控,可以临时设成 never 来做对比压测与排障。

9、Sink 写入:批量 flush、重试、并行度与交付语义

MongoDB sink 常用参数如下:

  • sink.buffer-flush.max-rows(默认 1000):每批最多缓存多少行
  • sink.buffer-flush.interval(默认 1s):定时 flush
  • sink.max-retries(默认 3)/ sink.retry.interval(默认 1s):写失败重试
  • sink.parallelism:sink 并行度(默认跟上游链)
  • sink.delivery-guarantee = none | at-least-once目前不支持 exactly-once (Apache Nightlies)

实战建议(写入侧):

  • 你追求低延迟:调小 interval(例如 200ms~500ms),适当减小 max-rows
  • 你追求高吞吐:调大 max-rows(例如 5k~20k),interval 可保持 1s~5s
  • 强烈建议配合 Flink checkpoint 使用(否则 at-least-once 缓冲/恢复行为不可控),并尽量选择 Upsert 模式提升幂等性

MongoDB 本质是 BSON 类型系统,connector 给出了典型映射:

  • ObjectId → STRING
  • DateTime → TIMESTAMP_LTZ(3)
  • Object → ROW
  • Array → ARRAY
  • Decimal128 → DECIMAL
    以及一些特殊 BSON 类型会用 Extended JSON 映射到 STRING(如 Regex、Symbol、DbPointer 等)。 (Apache Nightlies)

实战建议:

如果你希望 Flink 侧 schema 稳定,尽量避免在同一个字段上混用 BSON 类型(例如同字段既可能是 String 又可能是 Object),否则映射与序列化会很难控。

11、一份"生产落地清单"

  1. 先确认版本:Flink 2.2 当前仍提示无可用 connector(别在依赖上空耗)。 (Apache Nightlies)
  2. 写入推荐 Upsert:定义 PRIMARY KEY,让 _id 稳定,失败重放也幂等。 (Apache Nightlies)
  3. 分片集合 Upsert:必须把 shard key 用 PARTITIONED BY 声明,并保证 shard key 不变。 (Apache Nightlies)
  4. Scan 并行读取:优先 default,需要权限时提前申请 splitVector / config.chunks 读取权限。 (Apache Nightlies)
  5. 维表 join:开启 lookup.cache=PARTIAL,用 TTL 在"新鲜度 vs 吞吐"之间找到平衡。 (Apache Nightlies)
  6. 打包部署:connector 不在二进制发行包里,集群必须能加载到 jar。 (Apache Nightlies)
相关推荐
Mr_Xuhhh2 小时前
存储过程和触发器的总结
sql
熬夜敲代码的小N3 小时前
MySQL数据可视化实战:从SQL雕琢到图表绽放
sql·mysql·信息可视化
茁壮成长的露露3 小时前
Percona Backup for MongoDB备份恢复操作
数据库·mongodb
l1t3 小时前
一个在postgresql中运行很快,但是在duckdb中运行很慢的SQL
数据库·sql·postgresql·duckdb
码界奇点3 小时前
深入解析MySQL9主从复制架构详解从原理到实战
数据库·sql·架构·可用性测试
Knight_AL4 小时前
Flink 核心算子详解:map / flatMap / filter / process
大数据·python·flink
茁壮成长的露露4 小时前
MongoDB分片集群部署
数据库·mongodb
roman_日积跬步-终至千里4 小时前
【大数据框架】Calcite 基础概念:从 SQL 到执行计划的思维路径
java·大数据·sql
狂龙骄子4 小时前
MySQL表字段批量修改SQL实战技巧
数据库·sql·mysql·alter table·批量修改·sql实战技巧