重要
このページは英語版のページを機械翻訳しています。原文はこちらからご覧ください。

TiCDC Canal-JSONプロトコル

Canal-JSONは、 アリババ運河で定義されるデータ交換フォーマットプロトコルです。このドキュメントでは、TiDB拡張フィールド、Canal-JSONデータ形式の定義、公式のCanalとの比較など、Canal-JSONデータ形式がTiCDCにどのように実装されているかを学習できます。

Canal-JSONを使用する

ダウンストリームシンクとしてメッセージキュー(MQ)を使用する場合、Canal-JSONをsink-uriで指定できます。 TiCDCは、Eventを基本単位としてCanal-JSONメッセージをラップおよび構築し、TiDBデータ変更イベントをダウンストリームに送信します。

イベントには次の3つのタイプがあります。

  • DDLイベント:DDL変更レコードを表します。アップストリームDDLステートメントが正常に実行された後に送信されます。 DDLイベントは、インデックスが0のMQパーティションに送信されます。
  • DMLイベント:行データ変更レコードを表します。このタイプのイベントは、行の変更が発生したときに送信されます。変更が発生した後の行に関する情報が含まれています。
  • 透かしイベント:特別な時点を表します。これは、このポイントが完了する前に受信したイベントを示します。これはTiDB拡張フィールドにのみ適用され、 sink-urienable-tidb-extensionからtrueに設定すると有効になります。

以下は、 Canal-JSONの使用例です。

cdc cli changefeed create --pd=http://127.0.0.1:2379 --changefeed-id="kafka-canal-json" --sink-uri="kafka://127.0.0.1:9092/topic-name?kafka-version=2.6.0&protocol=canal-json"

TiDB拡張フィールド

Canal-JSONプロトコルは、もともとMySQL用に設計されています。 CommitTSトランザクションのTiDB固有の一意の識別子などの重要なフィールドは含まれていません。この問題を解決するために、TiCDCはTiDB拡張フィールドをCanal-JSONプロトコル形式に追加します。 sink-urienable-tidb-extensionからtrue (デフォルトではfalse )に設定すると、Canal-JSONメッセージを生成するときにTiCDCは次のように動作します。

  • TiCDCは、 _tidbという名前のフィールドを含むDMLイベントおよびDDLイベントメッセージを送信します。
  • TiCDCはWATERMARKイベントメッセージを送信します。

次に例を示します。

cdc cli changefeed create --pd=http://127.0.0.1:2379 --changefeed-id="kafka-canal-json-enable-tidb-extension" --sink-uri="kafka://127.0.0.1:9092/topic-name?kafka-version=2.6.0&protocol=canal-json&enable-tidb-extension=true"

メッセージ形式の定義

このセクションでは、DDLイベント、DMLイベント、およびWATERMARKイベントの形式と、コンシューマー側でデータがどのように解決されるかについて説明します。

DDLイベント

TiCDCは、DDLイベントを次のCanal-JSON形式にエンコードします。

{
    "id": 0,
    "database": "test",
    "table": "",
    "pkNames": null,
    "isDdl": true,
    "type": "QUERY",
    "es": 1639633094670,
    "ts": 1639633095489,
    "sql": "drop database if exists test",
    "sqlType": null,
    "mysqlType": null,
    "data": null,
    "old": null,
    "_tidb": {     // TiDB extension field
        "commitTs": 163963309467037594
    }
}

フィールドの説明は次のとおりです。

分野タイプ説明
id番号TiCDCのデフォルト値は0です。
データベース行が配置されているデータベースの名前
テーブル行が配置されているテーブルの名前
pkNames配列主キーを構成するすべての列の名前
isDdlブールメッセージがDDLイベントであるかどうか
タイプCanal-JSONによって定義されたイベントタイプ
es番号メッセージを生成したイベントが発生したときの13ビット(ミリ秒)のタイムスタンプ
ts番号TiCDCがメッセージを生成したときの13ビット(ミリ秒)のタイムスタンプ
sqlisDdlがtrueの場合、対応するDDLステートメントを記録します
sqlType物体isDdlがfalseの場合、各列のデータ型がJavaでどのように表されるかを記録します
mysqlType物体isDdlがfalseの場合、MySQLで各列のデータ型がどのように表されるかを記録します
データ物体isDdlがfalseの場合、各列の名前とそのデータ値を記録します
物体メッセージが更新イベントによって生成された場合のみ、更新前の各列の名前とデータ値を記録します
_tidb物体TiDB拡張フィールド。 enable-tidb-extensionからtrueに設定した場合にのみ存在します。値commitTsは、行を変更したトランザクションのTSOです。

DMLイベント

TiCDCは、DMLデータ変更イベントの行を次のようにエンコードします。

{
    "id": 0,
    "database": "test",
    "table": "tp_int",
    "pkNames": [
        "id"
    ],
    "isDdl": false,
    "type": "INSERT",
    "es": 1639633141221,
    "ts": 1639633142960,
    "sql": "",
    "sqlType": {
        "c_bigint": -5,
        "c_int": 4,
        "c_mediumint": 4,
        "c_smallint": 5,
        "c_tinyint": -6,
        "id": 4
    },
    "mysqlType": {
        "c_bigint": "bigint",
        "c_int": "int",
        "c_mediumint": "mediumint",
        "c_smallint": "smallint",
        "c_tinyint": "tinyint",
        "id": "int"
    },
    "data": [
        {
            "c_bigint": "9223372036854775807",
            "c_int": "2147483647",
            "c_mediumint": "8388607",
            "c_smallint": "32767",
            "c_tinyint": "127",
            "id": "2"
        }
    ],
    "old": null,
    "_tidb": {     // TiDB extension field
        "commitTs": 163963314122145239
    }
}

ウォーターマークイベント

TiCDCは、 enable-tidb-extensionからtrueに設定した場合にのみ、透かしイベントを送信します。 typeフィールドの値はTIDB_WATERMARKです。イベントには_tidbフィールドが含まれ、フィールドには1つのパラメーターwatermarkTsのみが含まれます。 watermarkTsの値は、イベントが送信されたときに記録されたTSOです。

このタイプのイベントを受信すると、 commitTswatermarkTs未満のすべてのイベントが送信されます。 TiCDCは「少なくとも1回」のセマンティクスを提供するため、データが繰り返し送信される可能性があります。 commitTswatermarkTs未満の後続のイベントを受信した場合は、このイベントを無視しても問題ありません。

以下は、ウォーターマークイベントの例です。

{
    "id": 0,
    "database": "",
    "table": "",
    "pkNames": null,
    "isDdl": false,
    "type": "TIDB_WATERMARK",
    "es": 1640007049196,
    "ts": 1640007050284,
    "sql": "",
    "sqlType": null,
    "mysqlType": null,
    "data": null,
    "old": null,
    "_tidb": {     // TiDB extension field
        "watermarkTs": 429918007904436226
    }
}

消費者側のデータ解決

上記の例からわかるように、Canal-JSONのデータ形式は統一されており、イベントタイプごとに異なるフィールド入力ルールがあります。統一された方法を使用してこのJSON形式のデータを解決し、フィールド値を確認してイベントタイプを判別できます。

  • isDdltrueの場合、メッセージにはDDLイベントが含まれます。
  • isDdlfalseの場合、 typeフィールドをさらにチェックする必要があります。 typeTIDB_WATERMARKの場合、それは透かしイベントです。それ以外の場合は、DMLイベントです。

フィールドの説明

Canal-JSON形式は、対応するデータ型をmysqlTypeフィールドとsqlTypeフィールドに記録します。

MySQLタイプフィールド

mysqlTypeフィールドでは、Canal-JSON形式が各列にMySQLタイプの文字列を記録します。詳細については、 TiDBデータ型を参照してください。

SQLタイプフィールド

sqlTypeフィールドには、Canal-JSON形式で各列のJava SQL Typeが記録されます。これは、JDBCのデータに対応するデータ型です。その値は、MySQLタイプと特定のデータ値によって計算できます。マッピングは次のとおりです。

MySQLタイプJavaSQLタイプコード
ブール値-6
浮く7
ダブル8
小数3
シャア1
バルチャー12
バイナリ2004年
バイナリ2004年
Tinytext2005年
文章2005年
ミディアムテキスト2005年
ロングテキスト2005年
Tinyblob2004年
ブロブ2004年
ミディアムブロブ2004年
ロングブロブ2004年
日にち91
日付時刻93
タイムスタンプ93
時間92
12
列挙型4
設定-7
少し-7
JSON12

整数型

次の表に示すように、 整数型Unsignedの制約と値のサイズがあるかどうかを考慮する必要があります。これらはそれぞれ異なるJavaSQLタイプコードに対応します。

MySQLタイプ文字列値の範囲JavaSQLタイプコード
tinyint[-128、127]-6
tinyint unsigned[0、127]-6
tinyint unsigned[128、255]5
smallint[-32768、32767]5
smallint unsigned[0、32767]5
smallint unsigned[32768、65535]4
ミディアムイント[-8388608、8388607]4
mediumint unsigned[0、8388607]4
mediumint unsigned[8388608、16777215]4
int[-2147483648、2147483647]4
int unsigned[0、2147483647]4
int unsigned[2147483648、4294967295]-5
bigint[-9223372036854775808、9223372036854775807]-5
bigint unsigned[0、9223372036854775807]-5
bigint unsigned[9223372036854775808、18446744073709551615]3

次の表は、TiCDCのJavaSQLタイプとそのコード間のマッピング関係を示しています。

JavaSQLタイプJavaSQLタイプコード
CHAR1
10進数3
整数4
SMALLINT5
本物7
ダブル8
VARCHAR12
日にち91
時間92
タイムスタンプ93
BLOB2004年
CLOB2005年
BIGINT-5
TINYINT-6
少し-7

Java SQLタイプの詳細については、 JavaSQLクラスタイプを参照してください。

TiCDC Canal-JSONと公式運河の比較

TiCDCがCanal-JSONデータ形式を実装する方法( UpdateイベントとmysqlTypeフィールドを含む)は、公式のCanalとは異なります。次の表に、主な違いを示します。

アイテムTiCDC Canal-JSON運河
Update種類のイベントoldフィールドには、すべての列データが含まれますoldフィールドには、変更された列データのみが含まれます
mysqlTypeフィールドパラメータ付きの型の場合、型パラメータの情報は含まれませんパラメータ付きの型の場合、型パラメータの完全な情報が含まれています

Updateタイプのイベント

Updateのタイプのイベントの場合:

  • TiCDCでは、 oldフィールドにすべての列データが含まれます
  • 公式運河では、 oldフィールドには変更された列データのみが含まれます

次のSQLステートメントがアップストリームTiDBで順番に実行されると想定します。

create table tp_int
(
    id          int auto_increment,
    c_tinyint   tinyint   null,
    c_smallint  smallint  null,
    c_mediumint mediumint null,
    c_int       int       null,
    c_bigint    bigint    null,
    constraint pk
        primary key (id)
);

insert into tp_int(c_tinyint, c_smallint, c_mediumint, c_int, c_bigint)
values (127, 32767, 8388607, 2147483647, 9223372036854775807);

update tp_int set c_int = 0, c_tinyint = 0 where c_smallint = 32767;

updateステートメントの場合、TiCDCは、以下に示すように、 typeUPDATEとしてイベントメッセージを出力します。 updateステートメントは、 c_int列とc_tinyint列のみを変更します。出力イベントメッセージのoldフィールドには、すべての列データが含まれています。

{
    "id": 0,
    ...
    "type": "UPDATE",
    ...
    "sqlType": {
        ...
    },
    "mysqlType": {
        ...
    },
    "data": [
        {
            "c_bigint": "9223372036854775807",
            "c_int": "0",
            "c_mediumint": "8388607",
            "c_smallint": "32767",
            "c_tinyint": "0",
            "id": "2"
        }
    ],
    "old": [                              // In TiCDC, this field contains all the column data.
        {
            "c_bigint": "9223372036854775807",
            "c_int": "2147483647",        // Modified column
            "c_mediumint": "8388607",
            "c_smallint": "32767",
            "c_tinyint": "127",           // Modified column
            "id": "2"
        }
    ]
}

公式運河の場合、出力イベントメッセージのoldフィールドには、以下に示すように、変更された列データのみが含まれます。

{
    "id": 0,
    ...
    "type": "UPDATE",
    ...
    "sqlType": {
        ...
    },
    "mysqlType": {
        ...
    },
    "data": [
        {
            "c_bigint": "9223372036854775807",
            "c_int": "0",
            "c_mediumint": "8388607",
            "c_smallint": "32767",
            "c_tinyint": "0",
            "id": "2"
        }
    ],
    "old": [                              // In Canal, this field contains only the modified column data.
        {
            "c_int": "2147483647",        // Modified column
            "c_tinyint": "127",           // Modified column
        }
    ]
}

mysqlTypeフィールド

mysqlTypeフィールドの場合、タイプにパラメーターが含まれていると、公式の運河にはタイプパラメーターの完全な情報が含まれます。 TiCDCにはそのような情報は含まれていません。

次の例では、テーブルを定義するSQLステートメントに、 decimalなどの各列のenumが含まれてvarchar char 。 TiCDCによって生成されたCanal-JSON形式と公式のCanalを比較すると、TiCDCにはmysqlTypeフィールドの基本的なMySQL情報のみが含まれていることがわかります。 typeパラメータの完全な情報が必要な場合は、他の方法で実装する必要があります。

次のSQLステートメントがアップストリームTiDBで順番に実行されると想定します。

create table t (
    id     int auto_increment,
    c_decimal    decimal(10, 4) null,
    c_char       char(16)      null,
    c_varchar    varchar(16)   null,
    c_binary     binary(16)    null,
    c_varbinary  varbinary(16) null,
    c_enum enum('a','b','c') null,
    c_set  set('a','b','c')  null,
    c_bit  bit(64)            null,
    constraint pk
        primary key (id)
);

insert into t (c_decimal, c_char, c_varchar, c_binary, c_varbinary, c_enum, c_set, c_bit)
values (123.456, "abc", "abc", "abc", "abc", 'a', 'a,b', b'1000001');

TiCDCの出力は次のとおりです。

{
    "id": 0,
    ...
    "isDdl": false,
    "sqlType": {
        ...
    },
    "mysqlType": {
        "c_binary": "binary",
        "c_bit": "bit",
        "c_char": "char",
        "c_decimal": "decimal",
        "c_enum": "enum",
        "c_set": "set",
        "c_varbinary": "varbinary",
        "c_varchar": "varchar",
        "id": "int"
    },
    "data": [
        {
            ...
        }
    ],
    "old": null,
}

公式運河の出力は次のとおりです。

{
    "id": 0,
    ...
    "isDdl": false,
    "sqlType": {
        ...
    },
    "mysqlType": {
        "c_binary": "binary(16)",
        "c_bit": "bit(64)",
        "c_char": "char(16)",
        "c_decimal": "decimal(10, 4)",
        "c_enum": "enum('a','b','c')",
        "c_set": "set('a','b','c')",
        "c_varbinary": "varbinary(16)",
        "c_varchar": "varchar(16)",
        "id": "int"
    },
    "data": [
        {
            ...
        }
    ],
    "old": null,
}

TiCDC Canalの変更-JSON

DeleteイベントのOldフィールドの変更

v5.4.0から、 Deleteのイベントのoldのフィールドが変更されました。

以下はDeleteのイベントメッセージです。 v5.4.0より前のバージョンでは、 oldフィールドには「データ」フィールドと同じ内容が含まれています。 v5.4.0以降のバージョンでは、 oldフィールドはnullに設定されています。 「データ」フィールドを使用して、削除されたデータを取得できます。

{
    "id": 0,
    "database": "test",
    ...
    "type": "DELETE",
    ...
    "sqlType": {
        ...
    },
    "mysqlType": {
        ...
    },
    "data": [
        {
            "c_bigint": "9223372036854775807",
            "c_int": "0",
            "c_mediumint": "8388607",
            "c_smallint": "32767",
            "c_tinyint": "0",
            "id": "2"
        }
    ],
    "old": null,
    // The following is an example before v5.4.0. The `old` field contains the same content as the "data" field.
    "old": [
        {
            "c_bigint": "9223372036854775807",
            "c_int": "0",
            "c_mediumint": "8388607",
            "c_smallint": "32767",
            "c_tinyint": "0",
            "id": "2"
        }
    ]
}