Flink CDC 用 Oracle CDC 实时同步数据到 Elasticsearch

一、整体架构与 Demo 思路

本 Demo 会启动 3 个容器:

  • Oracle 19c:作为上游业务库;
  • Elasticsearch 7.6.0:存储 Flink 处理后的结果;
  • Kibana 7.6.0:可视化 Elasticsearch 数据。

数据流非常简单直观:

Oracle PRODUCTS & ORDERS → Flink Oracle CDC → Flink SQL 中做 Join → 写入 ES 索引 enriched_orders_1 → Kibana 展示。

二、用 Docker Compose 一键拉起 Oracle + ES + Kibana

先在一个目录中创建 docker-compose.yml

yaml 复制代码
version: '2.1'
services:
  oracle:
    image: goodboy008/oracle-19.3.0-ee:non-cdb
    ports:
      - "1521:1521"

  elasticsearch:
    image: elastic/elasticsearch:7.6.0
    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
    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 正常。

实验结束后记得:

bash 复制代码
docker-compose down

在 Flink 集群所在机器,将以下 JAR 拷贝到 <FLINK_HOME>/lib/ 目录:

  • flink-sql-connector-elasticsearch7-3.0.1-1.17.jar
  • flink-sql-connector-oracle-cdc-3.0-SNAPSHOT.jar

注意:3.0-SNAPSHOT 版本通常需要你从对应分支自己构建,只有稳定版本才会提供直接下载链接。

然后启动 Flink 集群(Standalone 示例):

bash 复制代码
cd $FLINK_HOME
./bin/start-cluster.sh

打开 Flink SQL CLI:

bash 复制代码
./bin/sql-client.sh

四、在 Oracle 中准备源表和测试数据

1. 连接到 Oracle 容器

使用预置的用户登录(文档示例是 debezium/dbz):

bash 复制代码
docker-compose exec oracle \
  sqlplus debezium/dbz@localhost:1521/ORCLCDB

注意:Flink DDL 里用的是 username = 'dbzuser',如果你数据库中实际用户是 debezium,记得把 Flink 里的 username 配置改成 debezium,保持一致。

2. 创建 PRODUCTS 表

先删后建,保证环境干净可重复:

sql 复制代码
BEGIN
  EXECUTE IMMEDIATE 'DROP TABLE DEBEZIUM.PRODUCTS';
EXCEPTION
  WHEN OTHERS THEN
    IF SQLCODE != -942 THEN
      RAISE;
    END IF;
END;
/

CREATE TABLE DEBEZIUM.PRODUCTS (
  ID NUMBER(9, 0) NOT NULL,
  NAME VARCHAR2(255) NOT NULL,
  DESCRIPTION VARCHAR2(512),
  WEIGHT FLOAT,
  PRIMARY KEY(ID)
);

3. 创建 ORDERS 表

同样方式:

sql 复制代码
BEGIN
  EXECUTE IMMEDIATE 'DROP TABLE DEBEZIUM.ORDERS';
EXCEPTION
  WHEN OTHERS THEN
    IF SQLCODE != -942 THEN
      RAISE;
    END IF;
END;
/

CREATE TABLE DEBEZIUM.ORDERS (
  ID NUMBER(9, 0) NOT NULL,
  ORDER_DATE TIMESTAMP(3) NOT NULL,
  PURCHASER VARCHAR2(255) NOT NULL,
  QUANTITY NUMBER(9, 0) NOT NULL,
  PRODUCT_ID NUMBER(9, 0) NOT NULL,
  PRIMARY KEY(ID)
);

4. 为 CDC 开启补充日志(SUPPLEMENTAL LOG)

Oracle CDC 要依赖 Redo Log + Supplemental Log,否则变更记录里没有足够的信息。这里我们给表级别加上补充日志:

sql 复制代码
ALTER TABLE DEBEZIUM.PRODUCTS ADD SUPPLEMENTAL LOG DATA (ALL) COLUMNS;
ALTER TABLE DEBEZIUM.ORDERS   ADD SUPPLEMENTAL LOG DATA (ALL) COLUMNS;

实际生产环境通常还会开启 database 级别的 supplemental logging,这里 Demo 只展示表级配置。

5. 插入测试数据

插入一些商品和订单数据做演示:

sql 复制代码
INSERT INTO DEBEZIUM.PRODUCTS VALUES (101, 'scooter', 'Small 2-wheel scooter', 3.14);
INSERT INTO DEBEZIUM.PRODUCTS VALUES (102, 'car battery', '12V car battery', 8.1);
INSERT INTO DEBEZIUM.PRODUCTS VALUES (103, '12-pack drill bits', '12-pack of drill bits with sizes ranging from #40 to #3', 0.8);
INSERT INTO DEBEZIUM.PRODUCTS VALUES (104, 'hammer', '12oz carpenter''s hammer', 0.75);
INSERT INTO DEBEZIUM.PRODUCTS VALUES (105, 'hammer', '14oz carpenter''s hammer', 0.875);
INSERT INTO DEBEZIUM.PRODUCTS VALUES (106, 'hammer', '16oz carpenter''s hammer', 1.0);
INSERT INTO DEBEZIUM.PRODUCTS VALUES (107, 'rocks', 'box of assorted rocks', 5.3);
INSERT INTO DEBEZIUM.PRODUCTS VALUES (108, 'jacket', 'water resistent black wind breaker', 0.1);
INSERT INTO DEBEZIUM.PRODUCTS VALUES (109, 'spare tire', '24 inch spare tire', 22.2);

INSERT INTO DEBEZIUM.ORDERS VALUES (1001, TO_TIMESTAMP('2020-07-30 10:08:22.001000', 'YYYY-MM-DD HH24:MI:SS.FF'), 'Jark',   1, 101);
INSERT INTO DEBEZIUM.ORDERS VALUES (1002, TO_TIMESTAMP('2020-07-30 10:11:09.001000', 'YYYY-MM-DD HH24:MI:SS.FF'), 'Sally',  2, 102);
INSERT INTO DEBEZIUM.ORDERS VALUES (1003, TO_TIMESTAMP('2020-07-30 12:00:30.001000', 'YYYY-MM-DD HH24:MI:SS.FF'), 'Edward', 2, 103);
INSERT INTO DEBEZIUM.ORDERS VALUES (1004, TO_TIMESTAMP('2020-07-30 15:22:00.001000', 'YYYY-MM-DD HH24:MI:SS.FF'), 'Jark',   1, 104);

至此,Oracle 侧准备完毕:一个商品表 + 一个订单表,后续会用 Flink CDC 将其 Join 后写入 ES。

回到 Flink SQL CLI,先打开 checkpoint(CDC 必备):

sql 复制代码
-- 每 3 秒做一次 checkpoint
SET execution.checkpointing.interval = 3s;

1. 定义 Oracle CDC 源表:products

sql 复制代码
CREATE TABLE products (
    ID INT,
    NAME STRING,
    DESCRIPTION STRING,
    PRIMARY KEY (ID) NOT ENFORCED
) WITH (
    'connector' = 'oracle-cdc',
    'hostname' = 'localhost',
    'port' = '1521',
    'username' = 'dbzuser',
    'password' = 'dbz',
    'database-name' = 'ORCLCDB',
    'schema-name' = 'DEBEZIUM',
    'table-name' = 'products'
);

参数说明要点:

  • connector = 'oracle-cdc':使用 Oracle CDC 源连接器;
  • database-name = 'ORCLCDB':CDB 名;
  • schema-name = 'DEBEZIUM':模式名;
  • table-name = 'products':表名,不区分大小写(实际映射到 DEBEZIUM.PRODUCTS);
  • username/password:CDC 用户(Demo 中是 dbzuser/dbz,若你实际用 debezium/dbz,要同步修改)。

2. 定义 Oracle CDC 源表:orders

sql 复制代码
CREATE TABLE orders (
   ID INT,
   ORDER_DATE TIMESTAMP(3),
   PURCHASER STRING,
   QUANTITY INT,
   PRODUCT_ID INT,
   ORDER_STATUS BOOLEAN
) WITH (
   'connector' = 'oracle-cdc',
   'hostname' = 'localhost',
   'port' = '1521',
   'username' = 'dbzuser',
   'password' = 'dbz',
   'database-name' = 'ORCLCDB',
   'schema-name' = 'DEBEZIUM',
   'table-name' = 'orders'
);

这里多了个 ORDER_STATUS 字段,Demo 里先空着不写入 Oracle,主要是为了展示 Flink 侧 schema 可以比源库宽一些(也可以不定义)。

3. 定义 Elasticsearch 结果表:enriched_orders

我们要在 ES 里存一张"订单 + 商品信息"的宽表:

sql 复制代码
CREATE TABLE enriched_orders (
   ORDER_ID INT,
   ORDER_DATE TIMESTAMP(3),
   PURCHASER STRING,
   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)。

4. 启动实时 Join 任务:Oracle → ES

核心 SQL 就一条 INSERT ... SELECT

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 会做几件事:

  1. 读取 PRODUCTSORDERS表快照
  2. 持续订阅 redo 日志,捕获后续 INSERT/UPDATE/DELETE;
  3. 在流上按 PRODUCT_ID = ID 做实时 Join;
  4. 将 Join 后的结果 Upsert 到 Elasticsearch 索引 enriched_orders_1 中。

六、在 Kibana 中查看同步结果

打开 Kibana:

  1. 访问:
    http://localhost:5601/app/kibana#/management/kibana/index_pattern

  2. 创建一个 index pattern:enriched_orders_1

  3. 然后到:
    http://localhost:5601/app/kibana#/discover

    选择刚才的 index pattern,即可看到:

    • ORDER_ID / ORDER_DATE / PURCHASER / QUANTITY
    • 以及 Join 后补充的 PRODUCT_NAME / PRODUCT_DESCRIPTION

如果你更习惯 curl,也可以:

bash 复制代码
curl -X GET "localhost:9200/enriched_orders_1/_search?pretty"

七、在 Oracle 中修改数据,观察 Elasticsearch 实时变化

现在来验证 CDC 是否生效。

重新进入 Oracle 容器(这次直接用 Debezium 用户即可):

bash 复制代码
docker-compose exec oracle \
  sqlplus debezium/dbz@localhost:1521/ORCLCDB

依次执行几条 DML:

sql 复制代码
-- 1)插入一条新订单
INSERT INTO DEBEZIUM.ORDERS
VALUES (
  1005,
  TO_TIMESTAMP('2020-07-30 15:22:00.001000', 'YYYY-MM-DD HH24:MI:SS.FF'),
  'Jark',
  5,
  105
);

-- 2)修改一条订单数量
UPDATE DEBEZIUM.ORDERS
SET QUANTITY = 10
WHERE ID = 1002;

-- 3)删除一条订单
DELETE FROM DEBEZIUM.ORDERS
WHERE ID = 1004;

每执行完一条语句:

  • Oracle 会产生 redo 日志;

  • Flink Oracle CDC Connector 通过 LogMiner/XStream(取决于模式)捕获到这些变更;

  • 实时 Join 逻辑重新计算对应订单的宽表数据;

  • ES 索引 enriched_orders_1 中的文档被插入 / 更新 / 删除;

  • Kibana Discover 刷新页面即可看到:

    • 新增:多了一条 ORDER_ID = 1005 的记录,商品名称来自 Products 表中 ID=105;
    • 更新:ORDER_ID = 1002QUANTITY 字段变为 10;
    • 删除:ORDER_ID = 1004 的文档从索引中消失(或被标记删除,视 connector 模式而定)。

这就是典型的 Oracle → Flink CDC → 实时宽表 → Elasticsearch 实战闭环。

八、清理环境

实验完成后记得清理资源。

关闭 Docker 中的 Oracle / ES / Kibana:

bash 复制代码
docker-compose down

停止 Flink 集群:

bash 复制代码
cd $FLINK_HOME
./bin/stop-cluster.sh

九、小结与扩展思路

通过这个 Demo,我们搭建并验证了一条简单但完整的链路:

Oracle(CDB = ORCLCDB,schema = DEBEZIUM)

→ Flink Oracle CDC Connector 捕获 PRODUCTS/ORDERS 的快照与变更

→ Flink SQL 中做实时 Join,构建"订单 + 商品信息"宽表

Elasticsearch enriched_orders_1 索引

Kibana 实时查询与展示

相关推荐
熊猫钓鱼>_>2 小时前
数据处理的艺术:从Kafka到实时流处理平台的技术深度剖析
分布式·flink·kafka·数据治理·状态管理·管道·数据工程师
Elastic 中国社区官方博客3 小时前
开始使用 Elastic Agent Builder 和 Microsoft Agent Framework
数据库·人工智能·elasticsearch·microsoft·搜索引擎·ai·全文检索
野生技术架构师4 小时前
数据库连接池爆满如何排查
网络·数据库·oracle
百***81274 小时前
从 SQL 语句到数据库操作
数据库·sql·oracle
百***3285 小时前
数据库(MySQL):使用命令从零开始在Navicat创建一个数据库及其数据表(一).创建基础表
数据库·mysql·oracle
青靴7 小时前
Git Hooks 实现 CI/CD 进阶实践 -- 根据实际需求添加功能
git·elasticsearch·ci/cd
bigdata-rookie7 小时前
Flink Checkpoint 和 Spark Checkpoint 的区别
大数据·flink·spark
0***R5157 小时前
SpringBoot集成Elasticsearch实战
java·spring boot·elasticsearch