Azure Database for MySQLをソースとして Confluent へマネージド Debezium により CDC（変更データキャプチャー）の検証結果

## 概要
本記事では、Azure Database for MySQL をソースとして、Confluent で提供される Debezium を用いて CDC（Change Data Capture）を実行する手順と、その実行結果についてご紹介します。Azure Database for MySQL に対する SQL の実行を Azure Data Studio で実施しています。

本記事は、以下のシリーズの一部です。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/24031/1ff6386e-0d84-e62b-dd54-8e6164c4ba5b.png)

引用元：[Data in Motion 実現へ：Data Streaming Platform である Confluent の全貌 #Kafka - Qiita](https://qiita.com/manabian/items/f8281514e9afb9a8701c)

## 環境準備

### Azure Database for MySQL の環境構築

#### 1. Azure Database for MySQL Flexible Server の作成

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/24031/0fd934e9-3acb-ef91-3767-5c9299b5e7c3.png)

#### 2. `binlog_row_image`を`full`に設定

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/24031/3b2d45e1-a33c-2be2-9295-7e3c619170db.png)

#### 3. データベースを作成

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/24031/f3da38da-811b-0e0a-768b-1a6dad2070ad.png)

### Confluent Cloud での MySQL CDC Source V2 (Debezium) コネクタの作成

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/24031/a8e0bf66-5fbc-4afe-9e22-c6f0f432c6d6.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/24031/eb3984d6-60c5-d84a-d3b4-9f0e0500d18d.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/24031/90320795-0708-5252-7a79-48edc40d9423.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/24031/175284e0-2576-265d-a59e-486d566892bc.png)

## 動作検証

### 1. Primary Key を設定しているテーブルでの動作検証

#### 事前準備

まずは customers テーブルを作成します。

```sql
CREATE TABLE customers (
    id INT PRIMARY KEY,
    first_name VARCHAR(255) NOT NULL,
    last_name  VARCHAR(255) NOT NULL,
    email      VARCHAR(255) NOT NULL,
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
```

#### INSERT 操作（create events）

以下のようにレコードを INSERT します。

```sql
INSERT INTO customers (id, first_name, last_name, email)
VALUES
    (1, 'Anne', 'Kretchmar', 'annek@example.org');
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/24031/cb937806-1383-5313-fe6f-2852cdc0b7ff.png)

Confluent のトピックを確認すると、INSERT イベントが以下のように記録されています。

```json
{
  "before": null,
  "after": {
    "id": 1,
    "first_name": "Anne",
    "last_name": "Kretchmar",
    "email": "annek@example.org",
    "created_at": "2025-01-05T09:06:23Z"
  },
  "source": {
    "version": "2.4.2.Final",
    "connector": "mysql",
    "name": "debezium_mysql",
    "ts_ms": 1736067983000,
    "snapshot": "false",
    "db": "debezium",
    "sequence": null,
    "table": "customers",
    "server_id": 2287969543,
    "gtid": null,
    "file": "mysql-bin.000001",
    "pos": 1493,
    "row": 0,
    "thread": 49,
    "query": null
  },
  "op": "c",
  "ts_ms": 1736067983350,
  "transaction": null
}
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/24031/b94309d1-9f40-3e30-1090-cea4fcec3215.png)

#### UPDATE 操作（update events）

次にレコードを UPDATE します。

```sql
-- 既存レコードを更新
UPDATE customers
   SET email = 'updated_anne@example.org'
 WHERE id = 1;
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/24031/91720757-6497-5086-035d-16ffe000ef85.png)

Confluent のトピックを確認すると、UPDATE イベントが以下のように記録されています。

```json
{
  "before": {
    "id": 1,
    "first_name": "Anne",
    "last_name": "Kretchmar",
    "email": "annek@example.org",
    "created_at": "2025-01-05T09:06:23Z"
  },
  "after": {
    "id": 1,
    "first_name": "Anne",
    "last_name": "Kretchmar",
    "email": "updated_anne@example.org",
    "created_at": "2025-01-05T09:06:23Z"
  },
  "source": {
    "version": "2.4.2.Final",
    "connector": "mysql",
    "name": "debezium_mysql",
    "ts_ms": 1736068123000,
    "snapshot": "false",
    "db": "debezium",
    "sequence": null,
    "table": "customers",
    "server_id": 2287969543,
    "gtid": null,
    "file": "mysql-bin.000001",
    "pos": 1844,
    "row": 0,
    "thread": 49,
    "query": null
  },
  "op": "u",
  "ts_ms": 1736068123843,
  "transaction": null
}
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/24031/d2460180-989e-a318-6fe6-26c788e82a8e.png)

#### DELETE 操作（delete events）

次にレコードを DELETE します。

```sql
DELETE FROM customers
 WHERE id = 1;
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/24031/bf8650ad-e079-885b-e155-b83c21bb18e7.png)

Confluent のトピックを確認すると、DELETE イベントが以下のように記録されています。

```json
{
  "before": {
    "id": 1,
    "first_name": "Anne",
    "last_name": "Kretchmar",
    "email": "updated_anne@example.org",
    "created_at": "2025-01-05T09:06:23Z"
  },
  "after": null,
  "source": {
    "version": "2.4.2.Final",
    "connector": "mysql",
    "name": "debezium_mysql",
    "ts_ms": 1736068158000,
    "snapshot": "false",
    "db": "debezium",
    "sequence": null,
    "table": "customers",
    "server_id": 2287969543,
    "gtid": null,
    "file": "mysql-bin.000001",
    "pos": 2239,
    "row": 0,
    "thread": 49,
    "query": null
  },
  "op": "d",
  "ts_ms": 1736068158834,
  "transaction": null
}
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/24031/e0d125cb-2867-c9e2-3f02-ac515974426d.png)


```json
""
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/24031/393b0efb-5331-517c-a834-80c5e6cbf204.png)

#### Primary Key の値を更新（Primary key updates）

Primary key（id）の値を更新する操作を実施します。

```sql
-- 通常の INSERT
INSERT INTO customers (id, first_name, last_name, email)
VALUES
    (1, 'Anne', 'Kretchmar', 'annek@example.org');
```

```sql
UPDATE customers
   SET id = 10
 WHERE id = 1;
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/24031/bb969d62-08c5-3126-3851-4ec050dbdd66.png)

Confluent のトピックでは、id=1 のレコードを削除→id=10 のレコードを挿入する形でイベントが記録されます。

```json
{
  "before": {
    "id": 1,
    "first_name": "Anne",
    "last_name": "Kretchmar",
    "email": "annek@example.org",
    "created_at": "2025-01-05T09:11:25Z"
  },
  "after": null,
  "source": {
    "version": "2.4.2.Final",
    "connector": "mysql",
    "name": "debezium_mysql",
    "ts_ms": 1736068290000,
    "snapshot": "false",
    "db": "debezium",
    "sequence": null,
    "table": "customers",
    "server_id": 2287969543,
    "gtid": null,
    "file": "mysql-bin.000001",
    "pos": 2939,
    "row": 0,
    "thread": 49,
    "query": null
  },
  "op": "d",
  "ts_ms": 1736068290066,
  "transaction": null
}
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/24031/0f636c1f-e446-54c3-e321-c966677b4d6c.png)


```json
""
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/24031/68f4e2c5-9c25-fb78-dc9a-8f89ddbf80c6.png)

```json
{
  "before": null,
  "after": {
    "id": 10,
    "first_name": "Anne",
    "last_name": "Kretchmar",
    "email": "annek@example.org",
    "created_at": "2025-01-05T09:11:25Z"
  },
  "source": {
    "version": "2.4.2.Final",
    "connector": "mysql",
    "name": "debezium_mysql",
    "ts_ms": 1736068290000,
    "snapshot": "false",
    "db": "debezium",
    "sequence": null,
    "table": "customers",
    "server_id": 2287969543,
    "gtid": null,
    "file": "mysql-bin.000001",
    "pos": 2939,
    "row": 0,
    "thread": 49,
    "query": null
  },
  "op": "c",
  "ts_ms": 1736068290066,
  "transaction": null
}
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/24031/39c62d4a-2ea5-0dbf-530f-f408bdd39b3c.png)

#### TRUNCATE 操作（truncate events）

次にテーブルを TRUCATE します。

```sql
TRUNCATE TABLE customers;
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/24031/f4560a98-37cb-6891-97f5-b7211a754e96.png)

Confluent のトピックを確認すると、INSERT イベント（`op`フィールドの値が`c`）が以下のように記録されており、想定外の動作となりました。

```json
{
  "before": null,
  "after": {
    "id": 2,
    "first_name": "Bob",
    "last_name": "Thomas",
    "email": "bobt@example.org",
    "created_at": "2025-01-05T09:52:58Z"
  },
  "source": {
    "version": "2.4.2.Final",
    "connector": "mysql",
    "name": "debezium_mysql",
    "ts_ms": 1736070778000,
    "snapshot": "false",
    "db": "debezium",
    "sequence": null,
    "table": "customers",
    "server_id": 2287969543,
    "gtid": null,
    "file": "mysql-bin.000001",
    "pos": 8736,
    "row": 0,
    "thread": 49,
    "query": null
  },
  "op": "c",
  "ts_ms": 1736070778015,
  "transaction": null
}
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/24031/607e575f-be23-fe00-5d65-c9670315b66d.png)

### 2. 2 つのテーブルを結合した DML の動作検証

#### 事前準備

例として、`orders`テーブルと`tmp_orders`テーブルを用意します。

```sql
CREATE TABLE orders (
    order_id    INT PRIMARY KEY,
    customer_id INT NOT NULL,
    amount      INT,
    created_at  TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

CREATE TABLE tmp_orders (
    order_id    INT PRIMARY KEY,
    customer_id INT NOT NULL,
    amount      INT,
    created_at  TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/24031/64d8ec84-c6d2-e5a3-b36e-ca69573d82a3.png)

```sql
INSERT INTO orders (order_id, customer_id, amount)
VALUES
    (1, 1, 0);

INSERT INTO tmp_orders (order_id, customer_id, amount)
VALUES
    (1, 1, 100),
    (2, 2, 0);
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/24031/76ffd7d8-69e0-1a9c-34a7-393240256504.png)


#### DELETE 操作 (JOIN 例)

2 つのテーブルを JOIN して`orders`テーブルを DELETE します。

```sql
DELETE orders
  FROM orders
  JOIN tmp_orders
      ON orders.order_id = tmp_orders.order_id;
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/24031/598e966c-6587-2f05-175a-81709d5cf916.png)

```json
{
  "before": {
    "order_id": 1,
    "customer_id": 1,
    "amount": 0,
    "created_at": "2025-01-05T09:18:26Z"
  },
  "after": null,
  "source": {
    "version": "2.4.2.Final",
    "connector": "mysql",
    "name": "debezium_mysql",
    "ts_ms": 1736068788000,
    "snapshot": "false",
    "db": "debezium",
    "sequence": null,
    "table": "orders",
    "server_id": 2287969543,
    "gtid": null,
    "file": "mysql-bin.000001",
    "pos": 5134,
    "row": 0,
    "thread": 49,
    "query": null
  },
  "op": "d",
  "ts_ms": 1736068788429,
  "transaction": null
}
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/24031/69a1e281-4217-ac1c-65c5-2cc2932058cd.png)

```json
""
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/24031/2500f128-8152-27ba-19bf-887529bf3859.png)

#### INSERT SELECT 操作

`tmp_orders`テーブルのデータをもとに`orders`テーブルへ INSERT を実行します。

```sql
INSERT INTO orders
SELECT
    order_id,
    customer_id,
    amount,
    created_at
  FROM tmp_orders;
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/24031/421bc72a-196e-f037-c539-a03b9a9d76f0.png)

Confluent のトピックでは、INSERT イベントが以下のように記録されています。

```json
{
  "before": null,
  "after": {
    "order_id": 1,
    "customer_id": 1,
    "amount": 100,
    "created_at": "2025-01-05T09:18:26Z"
  },
  "source": {
    "version": "2.4.2.Final",
    "connector": "mysql",
    "name": "debezium_mysql",
    "ts_ms": 1736068970000,
    "snapshot": "false",
    "db": "debezium",
    "sequence": null,
    "table": "orders",
    "server_id": 2287969543,
    "gtid": null,
    "file": "mysql-bin.000001",
    "pos": 5443,
    "row": 0,
    "thread": 49,
    "query": null
  },
  "op": "c",
  "ts_ms": 1736068970315,
  "transaction": null
}
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/24031/f64d3a2c-fcb7-8408-982d-f7e4a241021f.png)


#### MERGE 操作

MySQL では Merge 文を実行できないため省略。

### Primary Key を設定していないテーブルでの動作検証

#### 事前準備

Primary Key を持たないテーブルを作成します。

```sql
CREATE TABLE logs (
    log_message TEXT,
    created_at  TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/24031/c4658fa7-71ae-a905-d8de-1cec90630812.png)

#### INSERT 操作（create events）

以下のようにレコードを INSERT します。

```sql
INSERT INTO logs (log_message)
VALUES
   ('System boot');
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/24031/00e22af8-1d9b-2faa-99e0-de7581268b04.png)

Confluent のトピックを確認すると、INSERT イベントが以下のように記録されています。

```json
{
  "before": null,
  "after": {
    "my_row_id": 1,
    "log_message": "System boot",
    "created_at": "2025-01-05T09:28:57Z"
  },
  "source": {
    "version": "2.4.2.Final",
    "connector": "mysql",
    "name": "debezium_mysql",
    "ts_ms": 1736069337000,
    "snapshot": "false",
    "db": "debezium",
    "sequence": null,
    "table": "logs",
    "server_id": 2287969543,
    "gtid": null,
    "file": "mysql-bin.000001",
    "pos": 7032,
    "row": 0,
    "thread": 49,
    "query": null
  },
  "op": "c",
  "ts_ms": 1736069337960,
  "transaction": null
}
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/24031/27b89322-2e84-1e86-f871-bdbb0050e134.png)

#### UPDATE 操作（update events）

Primary Key が無いテーブルでは、以下のようにレコードを特定して UPDATE します。

```sql
UPDATE logs
   SET log_message = '[UPDATED] System boot'
 WHERE log_message = 'System boot';
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/24031/2995ec15-4309-ab4f-a4c0-3d2a5aab1022.png)

Confluent のトピックでは、UPDATE イベントが以下のように記録されています。

```json
{
  "before": {
    "my_row_id": 1,
    "log_message": "System boot",
    "created_at": "2025-01-05T09:28:57Z"
  },
  "after": {
    "my_row_id": 1,
    "log_message": "[UPDATED] System boot",
    "created_at": "2025-01-05T09:28:57Z"
  },
  "source": {
    "version": "2.4.2.Final",
    "connector": "mysql",
    "name": "debezium_mysql",
    "ts_ms": 1736069435000,
    "snapshot": "false",
    "db": "debezium",
    "sequence": null,
    "table": "logs",
    "server_id": 2287969543,
    "gtid": null,
    "file": "mysql-bin.000001",
    "pos": 7352,
    "row": 0,
    "thread": 49,
    "query": null
  },
  "op": "u",
  "ts_ms": 1736069435250,
  "transaction": null
}
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/24031/39e9a83a-3849-ae67-e8d3-a71a97a4ba90.png)


#### DELETE 操作（delete events）

最後に、該当レコードを削除します。

```sql
DELETE FROM logs
 WHERE log_message = '[UPDATED] System boot';
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/24031/0a136c64-be92-ec85-1230-bffaf1bb674f.png)

Confluent のトピックでは、DELETE イベントが以下のように記録されています。

```json
{
  "before": {
    "my_row_id": 1,
    "log_message": "[UPDATED] System boot",
    "created_at": "2025-01-05T09:28:57Z"
  },
  "after": null,
  "source": {
    "version": "2.4.2.Final",
    "connector": "mysql",
    "name": "debezium_mysql",
    "ts_ms": 1736069498000,
    "snapshot": "false",
    "db": "debezium",
    "sequence": null,
    "table": "logs",
    "server_id": 2287969543,
    "gtid": null,
    "file": "mysql-bin.000001",
    "pos": 7700,
    "row": 0,
    "thread": 49,
    "query": null
  },
  "op": "d",
  "ts_ms": 1736069498076,
  "transaction": null
}
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/24031/6846c999-28d4-e78e-32d4-39abbd6fb3c6.png)


```json
""
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/24031/305f5fb0-898e-065e-1dae-b5642ef433a4.png)

## まとめ

以上が、Azure Database for MySQL から Confluent（Debezium）を用いた CDC の手順と、各種 DML 操作のイベントがどのようにトピックへ記録されるかの例となります。PK（Primary Key）の有無や、複数テーブルにまたがる DML などのパターンによって、Kafka に取り込まれるイベントの内容が異なる点にご注意ください。