一、整体架构与 Demo 思路
本 Demo 中我们会用 Docker 启动 3 个容器:
- SqlServer 2019 :存放源表
products与orders; - Elasticsearch 7.6.0:作为 Flink CDC 的 sink;
- Kibana 7.6.0:用来查看 ES 中的数据。
数据流向非常简单:
SqlServer → Flink SqlServer CDC → Flink SQL 中 Join → Elasticsearch 索引
enriched_orders_1→ Kibana 实时查看
二、用 Docker Compose 启动 SqlServer + ES + Kibana
在一个空目录下创建 docker-compose.yml:
yaml
version: '2.1'
services:
sqlserver:
image: mcr.microsoft.com/mssql/server:2019-latest
container_name: sqlserver
ports:
- "1433:1433"
environment:
- "MSSQL_AGENT_ENABLED=true"
- "MSSQL_PID=Standard"
- "ACCEPT_EULA=Y"
- "SA_PASSWORD=Password!"
elasticsearch:
image: elastic/elasticsearch:7.6.0
container_name: elasticsearch
environment:
- cluster.name=docker-cluster
- bootstrap.memory_lock=true
- "ES_JAVA_OPTS=-Xms512m -Xmx512m"
- discovery.type=single-node
ports:
- "9200:9200"
- "9300:9300"
ulimits:
memlock:
soft: -1
hard: -1
nofile:
soft: 65536
hard: 65536
kibana:
image: elastic/kibana:7.6.0
container_name: kibana
ports:
- "5601:5601"
volumes:
- /var/run/docker.sock:/var/run/docker.sock
启动所有容器:
bash
docker-compose up -d
验证容器状态:
bash
docker ps
浏览器访问:http://localhost:5601,能看到 Kibana 正常启动,就说明 ES + Kibana 环境 OK。
实验结束后记得用:
bash
docker-compose down
来关闭并移除所有容器。
三、准备 Flink 和 CDC Connector
在 Flink 集群所在机器,将以下两个 JAR 复制到 <FLINK_HOME>/lib:
flink-sql-connector-elasticsearch7-3.0.1-1.17.jarflink-sql-connector-sqlserver-cdc-3.0-SNAPSHOT.jar
注意:3.0-SNAPSHOT 一般需要从对应分支源码自行构建,稳定版本才有直接下载地址。
接着启动 Flink 集群(以 Standalone 为例):
bash
cd $FLINK_HOME
./bin/start-cluster.sh
浏览器打开 Flink UI:http://localhost:8081,看到界面说明集群正常。
启动 Flink SQL CLI:
bash
./bin/sql-client.sh
四、在 SqlServer 中准备业务表和 CDC 配置
连接 SqlServer 容器,你可以用本机的 SQL Server 客户端(例如 SQL Server Management Studio / sqlcmd)直接连:
hostname = localhostport = 1433user = sapassword = Password!
下面的 SQL 在 SqlServer 中执行即可。
1. 创建数据库并开启 CDC
sql
-- 创建数据库
CREATE DATABASE inventory;
GO
-- 切库
USE inventory;
GO
-- 在数据库级开启 CDC
EXEC sys.sp_cdc_enable_db;
GO
2. 创建并初始化 products 表
sql
-- 创建产品表
CREATE TABLE products (
id INTEGER IDENTITY(101,1) NOT NULL PRIMARY KEY,
name VARCHAR(255) NOT NULL,
description VARCHAR(512),
weight FLOAT
);
-- 插入测试数据
INSERT INTO products(name,description,weight)
VALUES ('scooter','Small 2-wheel scooter',3.14);
INSERT INTO products(name,description,weight)
VALUES ('car battery','12V car battery',8.1);
INSERT INTO products(name,description,weight)
VALUES ('12-pack drill bits','12-pack of drill bits with sizes ranging from #40 to #3',0.8);
INSERT INTO products(name,description,weight)
VALUES ('hammer','12oz carpenter''s hammer',0.75);
INSERT INTO products(name,description,weight)
VALUES ('hammer','14oz carpenter''s hammer',0.875);
INSERT INTO products(name,description,weight)
VALUES ('hammer','16oz carpenter''s hammer',1.0);
INSERT INTO products(name,description,weight)
VALUES ('rocks','box of assorted rocks',5.3);
INSERT INTO products(name,description,weight)
VALUES ('jacket','water resistent black wind breaker',0.1);
INSERT INTO products(name,description,weight)
VALUES ('spare tire','24 inch spare tire',22.2);
-- 为 products 表开启 CDC
EXEC sys.sp_cdc_enable_table
@source_schema = 'dbo',
@source_name = 'products',
@role_name = NULL,
@supports_net_changes = 0;
GO
3. 创建并初始化 orders 表
sql
-- 创建订单表
CREATE TABLE orders (
id INTEGER IDENTITY(10001,1) NOT NULL PRIMARY KEY,
order_date DATE NOT NULL,
purchaser INTEGER NOT NULL,
quantity INTEGER NOT NULL,
product_id INTEGER NOT NULL,
FOREIGN KEY (product_id) REFERENCES products(id)
);
-- 插入订单数据
INSERT INTO orders(order_date,purchaser,quantity,product_id)
VALUES ('16-JAN-2016', 1001, 1, 102);
INSERT INTO orders(order_date,purchaser,quantity,product_id)
VALUES ('17-JAN-2016', 1002, 2, 105);
INSERT INTO orders(order_date,purchaser,quantity,product_id)
VALUES ('19-FEB-2016', 1002, 2, 106);
INSERT INTO orders(order_date,purchaser,quantity,product_id)
VALUES ('21-FEB-2016', 1003, 1, 107);
-- 为 orders 表开启 CDC
EXEC sys.sp_cdc_enable_table
@source_schema = 'dbo',
@source_name = 'orders',
@role_name = NULL,
@supports_net_changes = 0;
GO
到这里,SqlServer 侧准备好了:
- 一个
inventory库; - 两张表:
dbo.products和dbo.orders; - 在数据库和表级都开启了 CDC,Flink 就可以读取变更日志了。
五、在 Flink SQL 中配置 SqlServer CDC 源表和 ES Sink 表
回到 Flink SQL CLI。
1. 开启 checkpoint
sql
-- Flink SQL
SET execution.checkpointing.interval = 3s;
2. 创建 SqlServer CDC 源表:products
sql
CREATE TABLE products (
id INT,
name STRING,
description STRING,
PRIMARY KEY (id) NOT ENFORCED
) WITH (
'connector' = 'sqlserver-cdc',
'hostname' = 'localhost',
'port' = '1433',
'username' = 'sa',
'password' = 'Password!',
'database-name' = 'inventory',
'table-name' = 'dbo.products'
);
关键点:
connector = 'sqlserver-cdc':使用 SqlServer 专用 CDC 连接器;database-name = 'inventory';table-name = 'dbo.products':带 schema 限定;PRIMARY KEY (id) NOT ENFORCED:提供 Flink 语义上的主键,便于下游 Upsert。
3. 创建 SqlServer CDC 源表:orders
sql
CREATE TABLE orders (
id INT,
order_date DATE,
purchaser INT,
quantity INT,
product_id INT,
PRIMARY KEY (id) NOT ENFORCED
) WITH (
'connector' = 'sqlserver-cdc',
'hostname' = 'localhost',
'port' = '1433',
'username' = 'sa',
'password' = 'Password!',
'database-name' = 'inventory',
'table-name' = 'dbo.orders'
);
这两张表定义好之后,Flink 会:
- 启动任务时读取
products与orders的当前快照; - 后续订阅 SQL Server CDC 数据表,实时捕获 INSERT / UPDATE / DELETE。
4. 创建 Elasticsearch Sink 表:enriched_orders
sql
CREATE TABLE enriched_orders (
order_id INT,
order_date DATE,
purchaser INT,
quantity INT,
product_name STRING,
product_description STRING,
PRIMARY KEY (order_id) NOT ENFORCED
) WITH (
'connector' = 'elasticsearch-7',
'hosts' = 'http://localhost:9200',
'index' = 'enriched_orders_1'
);
这会在 ES 中创建 / 使用名为 enriched_orders_1 的索引,主键是 order_id,用于 Upsert。
六、用 Flink SQL 实时 Join 并写入 Elasticsearch
核心逻辑是一条 INSERT INTO ... SELECT,把订单和商品 Join 成一张"宽表":
sql
INSERT INTO enriched_orders
SELECT
o.id AS order_id,
o.order_date,
o.purchaser,
o.quantity,
p.name AS product_name,
p.description AS product_description
FROM orders AS o
LEFT JOIN products AS p
ON o.product_id = p.id;
提交后,Flink Job 会一直运行:
- 通过
sqlserver-cdc源持续消费 SqlServer CDC 数据; - 在流上完成
orders与products的 Join; - 将 Join 后的结果 Upsert 到 ES 索引
enriched_orders_1中。
七、在 Kibana 中查看 enriched_orders 数据
1. 创建 Index Pattern
打开 Kibana:
- 访问:
http://localhost:5601/app/kibana#/management/kibana/index_pattern - 新建一个 index pattern:
enriched_orders_1
2. 查看数据
切换到 Discover 页面:
- 访问:
http://localhost:5601/app/kibana#/discover
选择 enriched_orders_1 索引模式,你应该能看到类似这样的字段:
- 订单信息:
order_id / order_date / purchaser / quantity - 商品信息:
product_name / product_description
这就说明 Flink CDC 已经把 SqlServer 的数据成功同步并 Join 到 ES 里了。
八、在 SqlServer 中修改数据,验证实时同步效果
接下来在 SqlServer 中执行几条 DML,看一下 Kibana 是否能实时反映变更。
在 inventory 数据库中执行:
sql
-- 1)插入一条新订单
INSERT INTO orders(order_date,purchaser,quantity,product_id)
VALUES ('22-FEB-2016', 1006, 22, 107);
GO
-- 2)更新一条订单的数量
UPDATE orders
SET quantity = 11
WHERE id = 10001;
GO
-- 3)删除一条订单
DELETE FROM orders
WHERE id = 10004;
GO
每执行一步:
-
SqlServer 侧会将变更写入 CDC 日志;
-
Flink SqlServer CDC Connector 会捕获这些变更;
-
Join 后的宽表结果被同步更新到 ES 索引;
-
Kibana 中的
enriched_orders_1结果列表会实时发生变化:- Insert:多了一条新订单;
- Update :对应订单的
quantity数量被更新; - Delete :对应
order_id的那条记录被删除。
整个过程不需要重启任务,也不需要人工干预。
九、清理环境
实验结束后,别忘了清理资源。
关闭并移除 Docker 中的 SqlServer / ES / Kibana:
bash
docker-compose down
停止 Flink 集群(在 <FLINK_HOME> 目录下):
bash
./bin/stop-cluster.sh
十、小结:SqlServer 也能"优雅地"进入实时世界
至此,我们完成了一条完整的链路:
SqlServer inventory 库(dbo.products / dbo.orders)
→ 数据库/表级开启 CDC
→ Flink SqlServer CDC Connector 捕获变更
→ Flink SQL 中实时 Join 成 enriched_orders
→ 写入 Elasticsearch enriched_orders_1 索引
→ 使用 Kibana 实时查看数据变化
如果你已经看完 / 实操过本系列里 MySQL、PolarDB-X、Oracle、OceanBase、Db2 等 CDC Demo,你会发现一件很舒服的事:
上游不管是啥库,
在 Flink 这边基本就是:
换一个 connector 名,改几行 WITH 参数,其余 SQL 模板几乎一模一样。
这也是 Flink CDC 真正的价值所在:
让各种"老系统 + 异构数据库"
在进入"实时数仓 / 实时搜索 / 实时分析"世界的路上,变得非常统一、可控、可复制。