{"id":34481,"date":"2026-08-05T06:00:17","date_gmt":"2026-08-05T13:00:17","guid":{"rendered":"https:\/\/www.pingcap.com\/?p=34481"},"modified":"2026-08-05T08:31:25","modified_gmt":"2026-08-05T15:31:25","slug":"debezium-cdc-to-tidb","status":"publish","type":"post","link":"https:\/\/www.pingcap.com\/ko\/blog\/debezium-cdc-to-tidb\/","title":{"rendered":"Real-Time Data Migration into TiDB with Debezium CDC"},"content":{"rendered":"<h2 class=\"wp-block-heading\"><span class=\"ez-toc-section\" id=\"Key_Takeaways\"><\/span>Key Takeaways<span class=\"ez-toc-section-end\"><\/span><\/h2>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>One pipeline, any source.<\/strong>\u00a0Debezium captures from MySQL, PostgreSQL, Oracle, and seven other databases; only the source connector changes.<\/li>\n\n\n\n<li><strong>No proprietary drivers.<\/strong>\u00a0TiDB speaks the MySQL protocol, so the Debezium JDBC sink writes over a standard\u00a0<code>jdbc:mysql:\/\/<\/code>\u00a0connection.<\/li>\n\n\n\n<li><strong>Migrate and stream in one setup.<\/strong>\u00a0The initial snapshot seeds existing rows, then switches to streaming live changes.<\/li>\n\n\n\n<li><strong>Near-zero downtime.<\/strong>\u00a0Existing data and live changes arrive as one gap-free stream.<\/li>\n\n\n\n<li><strong>Use TiDB DM for MySQL-only moves.<\/strong>\u00a0Reach for Debezium and Kafka when the source is not MySQL, or you need a reusable change stream.<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\"><br>Moving data into a new database is rarely a one-shot copy. Migrating off a legacy system, adopting a distributed SQL database, carrying out a heterogeneous database migration, or standing up an analytical replica all share the same challenge. You have to move a large, already-populated dataset and keep it continuously in sync until you are ready to cut over, or stream the changes indefinitely, ideally with near-zero downtime.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Change data capture (CDC) solves both halves of that problem. Instead of re-copying tables on a schedule, CDC tails the source database&#8217;s transaction log and streams every committed row-level change to the target.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Debezium reads the native transaction log of each supported database and turns every committed insert, update, and delete into a structured event on Apache Kafka. That change stream is a natural backbone for event-driven architectures. Instead of services polling the database, each data change becomes a durable, ordered event. Independent consumers subscribe to that stream and react on their own, including microservices, cache invalidation, search and vector indexing, data lakes, and real-time analytics.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Because TiDB is&nbsp;<a href=\"https:\/\/docs.pingcap.com\/tidb\/stable\/mysql-compatibility\/\">MySQL \ud638\ud658<\/a>, the open-source Debezium JDBC sink connector applies the streamed changes over standard JDBC. This guide presents a reusable architecture: any Debezium source \u2192 Apache Kafka \u2192 Debezium JDBC sink \u2192 TiDB, validated with a PostgreSQL example. To use a different source, change only the source connector.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\"><span class=\"ez-toc-section\" id=\"Supported_Debezium_source_connectors\"><\/span>Supported Debezium source connectors<span class=\"ez-toc-section-end\"><\/span><\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Debezium documents the source connectors below, and any of them can feed this pipeline into TiDB. The only difference between sources is how capture is enabled on the database itself. The delivery side, Kafka to JDBC sink to TiDB, is identical for all relational sources.<\/p>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><thead><tr><th>Source database<\/th><th>How Debezium captures changes<\/th><th>Connector docs<\/th><\/tr><\/thead><tbody><tr><td>MySQL<\/td><td>Binary log (binlog) in ROW format<\/td><td><a href=\"https:\/\/debezium.io\/documentation\/reference\/stable\/connectors\/mysql.html\">mysql<\/a><\/td><\/tr><tr><td>MariaDB<\/td><td>Binary log (binlog)<\/td><td><a href=\"https:\/\/debezium.io\/documentation\/reference\/stable\/connectors\/mariadb.html\">mariadb<\/a><\/td><\/tr><tr><td>PostgreSQL<\/td><td>Logical decoding of the WAL (pgoutput)<\/td><td><a href=\"https:\/\/debezium.io\/documentation\/reference\/stable\/connectors\/postgresql.html\">postgresql<\/a><\/td><\/tr><tr><td>Oracle<\/td><td>Redo logs via LogMiner (or XStream)<\/td><td><a href=\"https:\/\/debezium.io\/documentation\/reference\/stable\/connectors\/oracle.html\">oracle<\/a><\/td><\/tr><tr><td>SQL Server<\/td><td>Native CDC change tables<\/td><td><a href=\"https:\/\/debezium.io\/documentation\/reference\/stable\/connectors\/sqlserver.html\">sqlserver<\/a><\/td><\/tr><tr><td>Db2<\/td><td>SQL-replication (ASN) capture tables<\/td><td><a href=\"https:\/\/debezium.io\/documentation\/reference\/stable\/connectors\/db2.html\">db2<\/a><\/td><\/tr><tr><td>Informix<\/td><td>Logical transaction logs<\/td><td><a href=\"https:\/\/debezium.io\/documentation\/reference\/stable\/connectors\/informix.html\">informix<\/a><\/td><\/tr><tr><td>Vitess<\/td><td>VStream (built on MySQL binlog)<\/td><td><a href=\"https:\/\/debezium.io\/documentation\/reference\/stable\/connectors\/vitess.html\">vitess<\/a><\/td><\/tr><tr><td>Spanner<\/td><td>Change streams<\/td><td><a href=\"https:\/\/debezium.io\/documentation\/reference\/stable\/connectors\/spanner.html\">spanner<\/a><\/td><\/tr><tr><td>CockroachDB<\/td><td>Changefeeds<\/td><td><a href=\"https:\/\/debezium.io\/documentation\/reference\/stable\/connectors\/cockroachdb.html\">cockroachdb<\/a><\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<h2 class=\"wp-block-heading\"><span class=\"ez-toc-section\" id=\"Solution_architecture\"><\/span>Solution architecture<span class=\"ez-toc-section-end\"><\/span><\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">The pipeline has three logical stages: capture, transport, and delivery. Each is handled by a dedicated component.<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Capture.<\/strong>&nbsp;A Debezium source connector reads the source database&#8217;s transaction log and turns each committed change into a structured change event.<\/li>\n\n\n\n<li><strong>Transport.<\/strong>&nbsp;Apache Kafka stores those events durably in per-table topics, decoupling source from target and enabling replay and fan-out to multiple consumers.<\/li>\n\n\n\n<li><strong>Delivery.<\/strong>&nbsp;The Debezium JDBC sink connector consumes the events and applies the equivalent INSERT, UPDATE, and DELETE statements to TiDB over the MySQL protocol.<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">All connectors run as plugins inside Kafka Connect, the worker framework that manages connector lifecycle, offsets, and restarts. On first start, the source connector performs a consistent initial snapshot of existing data, then switches to streaming from the transaction-log position it recorded when the snapshot began. Existing rows and future changes arrive as one continuous, gap-free stream.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Architecture diagram<\/h3>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"492\" src=\"https:\/\/static.pingcap.com\/files\/2026\/08\/05054720\/image-1-1024x492.png\" alt=\"\" class=\"wp-image-34483\" srcset=\"https:\/\/static.pingcap.com\/files\/2026\/08\/05054720\/image-1-1024x492.png 1024w, https:\/\/static.pingcap.com\/files\/2026\/08\/05054720\/image-1-300x144.png 300w, https:\/\/static.pingcap.com\/files\/2026\/08\/05054720\/image-1-768x369.png 768w, https:\/\/static.pingcap.com\/files\/2026\/08\/05054720\/image-1-18x9.png 18w, https:\/\/static.pingcap.com\/files\/2026\/08\/05054720\/image-1.png 1271w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><\/figure>\n\n\n\n<p class=\"has-text-align-center wp-block-paragraph\"><em>Fig. 1: The capture \u2192 transport \u2192 delivery pipeline. Only the capture stage changes between source databases.<\/em><\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Source database.<\/strong>&nbsp;The primary database system. Capture must be enabled on it.<\/li>\n\n\n\n<li><strong>Debezium source connector.<\/strong>&nbsp;Connects to the source, takes the initial snapshot, then streams change events. Choose the connector that matches your source.<\/li>\n\n\n\n<li><strong>Apache Kafka.<\/strong>&nbsp;The durable event backbone. Each captured table gets its own topic, retained for replay.<\/li>\n\n\n\n<li><strong>Kafka Connect.<\/strong>&nbsp;Hosts both connectors, exposes a REST API for configuration, and persists offsets and schema history.<\/li>\n\n\n\n<li><strong>Debezium JDBC sink connector.<\/strong>&nbsp;Applies changes to TiDB and understands the Debezium envelope natively.<\/li>\n\n\n\n<li><strong>Target database.<\/strong>&nbsp;The streaming target, a TiDB distributed SQL cluster.<\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\"><span class=\"ez-toc-section\" id=\"Before_you_begin\"><\/span>Before you begin<span class=\"ez-toc-section-end\"><\/span><\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">You need:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>A source database supported by a Debezium connector, with log-based capture enabled and a user that can read its change feed.<\/li>\n\n\n\n<li>A TiDB cluster (TiDB Self-Managed or&nbsp;<a href=\"https:\/\/tidbcloud.com\/\">TiDB Cloud<\/a>) reachable on port 4000, with a user that can create databases and tables.<\/li>\n\n\n\n<li>Docker and Docker Compose, on a host that can reach both the source and TiDB.<\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\"><span class=\"ez-toc-section\" id=\"Migrate_data_into_TiDB_with_Debezium_CDC\"><\/span>Migrate data into TiDB with Debezium CDC<span class=\"ez-toc-section-end\"><\/span><\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">The example streams a PostgreSQL table into TiDB. To migrate from a different source, change&nbsp;<code>connector.class<\/code>&nbsp;and the source-specific keys in Step 3. Every other step is identical.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Step 1: Start Kafka and Kafka Connect<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Save this as&nbsp;<code>docker-compose.yml<\/code>&nbsp;and start it. The Debezium Connect image already bundles the source connectors and the JDBC sink.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>version: '3'\nservices:\n  zookeeper:\n    image: quay.io\/debezium\/zookeeper:2.7\n    ports: &#91;\"2181:2181\"]\n  kafka:\n    image: quay.io\/debezium\/kafka:2.7\n    ports: &#91;\"9092:9092\"]\n    depends_on: &#91;zookeeper]\n    environment:\n      ZOOKEEPER_CONNECT: zookeeper:2181\n  connect:\n    image: quay.io\/debezium\/connect:2.7\n    ports: &#91;\"8083:8083\"]\n    depends_on: &#91;kafka]\n    environment:\n      BOOTSTRAP_SERVERS: kafka:9092\n      GROUP_ID: cdc-demo\n      CONFIG_STORAGE_TOPIC: connect_configs\n      OFFSET_STORAGE_TOPIC: connect_offsets\n      STATUS_STORAGE_TOPIC: connect_statuses\n      KEY_CONVERTER: org.apache.kafka.connect.json.JsonConverter\n      VALUE_CONVERTER: org.apache.kafka.connect.json.JsonConverter\n<\/code><\/pre>\n\n\n\n<pre class=\"wp-block-code\"><code>docker-compose up -d\n<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">Step 2: Enable capture on the source<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">For PostgreSQL, enable logical replication and create a role Debezium can connect and replicate with:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>-- postgresql.conf (self-managed) or DB parameter group (Aurora\/RDS PostgreSQL)\nwal_level = logical\n\nCREATE ROLE debezium WITH LOGIN REPLICATION PASSWORD '&lt;source-password&gt;';\nGRANT SELECT ON ALL TABLES IN SCHEMA public TO debezium;\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Create a sample table and a publication for Debezium to read:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>CREATE DATABASE cdc_demo;\n\\c cdc_demo\nCREATE TABLE orders (\n  id BIGINT PRIMARY KEY, customer VARCHAR(64), amount NUMERIC(10,2)\n);\nINSERT INTO orders VALUES\n  (1,'alice',10.50), (2,'bob',20.00), (3,'carol',33.33);\n\nCREATE PUBLICATION dbz_pub FOR TABLE orders;\n<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">Step 3: Configure the source connector<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Save the source connector configuration as&nbsp;<code>source.json<\/code>. This example captures PostgreSQL. For another database, change&nbsp;<code>connector.class<\/code>&nbsp;and its source-specific keys.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>{\n  \"name\": \"src-postgres-cdc_demo\",\n  \"config\": {\n    \"connector.class\": \"io.debezium.connector.postgresql.PostgresConnector\",\n    \"database.hostname\": \"&lt;source-host&gt;\",\n    \"database.port\": \"5432\",\n    \"database.user\": \"debezium\",\n    \"database.password\": \"&lt;source-password&gt;\",\n    \"database.dbname\": \"cdc_demo\",\n    \"topic.prefix\": \"dbz\",\n    \"table.include.list\": \"public.orders\",\n    \"plugin.name\": \"pgoutput\",\n    \"publication.name\": \"dbz_pub\",\n    \"slot.name\": \"dbz_slot\",\n    \"snapshot.mode\": \"initial\",\n    \"decimal.handling.mode\": \"double\"\n  }\n}\n<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">Step 4: Configure the JDBC sink connector for TiDB<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Create the target database (the sink auto-creates the table), then save the sink configuration as&nbsp;<code>sink.json<\/code>. Because TiDB is MySQL-compatible, the connection URL is a standard&nbsp;<code>jdbc:mysql:\/\/<\/code>&nbsp;URL on port 4000. This step is the same for every source.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>mysql -h &lt;tidb-host&gt; -P4000 -uroot -p&lt;tidb-password&gt; -e \"CREATE DATABASE cdc_demo;\"\n<\/code><\/pre>\n\n\n\n<pre class=\"wp-block-code\"><code>{\n  \"name\": \"sink-tidb-cdc_demo\",\n  \"config\": {\n    \"connector.class\": \"io.debezium.connector.jdbc.JdbcSinkConnector\",\n    \"topics\": \"dbz.public.orders\",\n    \"connection.url\": \"jdbc:mysql:\/\/&lt;tidb-host&gt;:4000\/cdc_demo\",\n    \"connection.username\": \"root\",\n    \"connection.password\": \"&lt;tidb-password&gt;\",\n    \"insert.mode\": \"upsert\",\n    \"primary.key.mode\": \"record_key\",\n    \"primary.key.fields\": \"id\",\n    \"delete.enabled\": \"true\",\n    \"schema.evolution\": \"basic\",\n    \"transforms\": \"route\",\n    \"transforms.route.type\": \"org.apache.kafka.connect.transforms.RegexRouter\",\n    \"transforms.route.regex\": \"dbz\\\\.public\\\\.orders\",\n    \"transforms.route.replacement\": \"orders\"\n  }\n}\n<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">Step 5: Register the connectors<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">POST both configurations to Kafka Connect, then confirm each reports&nbsp;<code>RUNNING<\/code>:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>curl -X POST -H \"Content-Type: application\/json\" --data @source.json \\\n  http:&#47;&#47;localhost:8083\/connectors\ncurl -X POST -H \"Content-Type: application\/json\" --data @sink.json \\\n  http:\/\/localhost:8083\/connectors\n\ncurl -s http:\/\/localhost:8083\/connectors\/src-postgres-cdc_demo\/status\ncurl -s http:\/\/localhost:8083\/connectors\/sink-tidb-cdc_demo\/status\n<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\"><span class=\"ez-toc-section\" id=\"Verify_the_initial_snapshot\"><\/span>Verify the initial snapshot<span class=\"ez-toc-section-end\"><\/span><\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">The snapshot is a one-time step that seeds the target with whatever already exists. As soon as both connectors are running, the source connector&#8217;s initial snapshot copies the three existing rows through Kafka and into TiDB. Query the target:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>mysql -h &lt;tidb-host&gt; -P4000 -u root -p&lt;tidb-password&gt; \\\n  -e \"SELECT * FROM cdc_demo.orders ORDER BY id;\"\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">The pre-existing rows are already present in TiDB:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>+----+----------+--------+\n| id | customer | amount |\n+----+----------+--------+\n|  1 | alice    |   10.5 |\n|  2 | bob      |     20 |\n|  3 | carol    |  33.33 |\n+----+----------+--------+\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Debezium applies the snapshot logically to the target database. On the source it reads each existing row with a consistent SELECT and emits it as a change event. The JDBC sink then writes those rows into TiDB as ordinary batched SQL over the MySQL protocol.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">For large databases, seed with a native import instead. A logical, row-by-row snapshot is convenient but slow and load-heavy once a table runs to tens of millions of rows or more. In that case, load the pre-existing data with a physical or bulk tool. Export it with the source&#8217;s native backup or dump utility and import it into TiDB with&nbsp;<a href=\"https:\/\/docs.pingcap.com\/tidb\/stable\/sql-statement-import-into\/\">IMPORT INTO<\/a>.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Then let Debezium handle only the ongoing stream. Set the source connector&#8217;s&nbsp;<code>snapshot.mode<\/code>&nbsp;to&nbsp;<code>no_data<\/code>&nbsp;(capture schema, skip the row snapshot) or&nbsp;<code>never<\/code>, and align the connector&#8217;s start position with the export&#8217;s consistency point, the WAL LSN or binlog GTID at which the backup was taken, so no changes are missed between the bulk load and the start of CDC.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\"><span class=\"ez-toc-section\" id=\"Real-time_changes\"><\/span>Real-time changes<span class=\"ez-toc-section-end\"><\/span><\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Now apply a mix of changes on the PostgreSQL source: an insert, an update, and a delete.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>INSERT INTO orders VALUES (4,'dave',44.44);\nUPDATE orders SET amount=99.99, customer='alice-updated' WHERE id=1;\nDELETE FROM orders WHERE id=2;\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Within a few seconds, re-query TiDB. All three change types have propagated:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>+----+---------------+--------+\n| id | customer      | amount |\n+----+---------------+--------+\n|  1 | alice-updated |  99.99 |\n|  3 | carol         |  33.33 |\n|  4 | dave          |  44.44 |\n+----+---------------+--------+\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Row 4 was inserted, row 1 was updated, and row 2 was deleted, exactly matching the source. The pipeline now streams continuously. Every subsequent committed change on the source appears in TiDB in near real time.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\"><span class=\"ez-toc-section\" id=\"Monitoring_and_troubleshooting\"><\/span>Monitoring and troubleshooting<span class=\"ez-toc-section-end\"><\/span><\/h2>\n\n\n\n<h3 class=\"wp-block-heading\">Connector status<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Check the health of each connector and its tasks through the REST API. A healthy connector and task both report&nbsp;<code>RUNNING<\/code>:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>curl -s http:\/\/localhost:8083\/connectors\/src-postgres-cdc_demo\/status\ncurl -s http:\/\/localhost:8083\/connectors\/sink-tidb-cdc_demo\/status\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">If a task has&nbsp;<code>FAILED<\/code>, its status includes the stack trace. You can restart a failed task without recreating the connector:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>curl -s -X POST http:\/\/localhost:8083\/connectors\/sink-tidb-cdc_demo\/tasks\/0\/restart\n<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">Clean up<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">To release resources, delete the connectors and tear down the stack when you are done:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code># Remove the connectors\ncurl -s -X DELETE http:\/\/localhost:8083\/connectors\/sink-tidb-cdc_demo\ncurl -s -X DELETE http:\/\/localhost:8083\/connectors\/src-postgres-cdc_demo\n\n# Stop and remove all containers and their Kafka state\ndocker-compose down -v\n\n# Optionally drop the demo databases on the source and on TiDB\n#   DROP DATABASE cdc_demo;\n<\/code><\/pre>\n\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<p class=\"wp-block-paragraph\"><strong>Note.<\/strong>&nbsp;<code>docker-compose down -v<\/code>&nbsp;also deletes the Kafka topics, connector offsets, and schema history. The next start therefore performs a fresh initial snapshot. Preserve the volumes if you want to resume streaming from where you left off.<\/p>\n<\/blockquote>\n\n\n\n<h2 class=\"wp-block-heading\"><span class=\"ez-toc-section\" id=\"Conclusion\"><\/span>Conclusion<span class=\"ez-toc-section-end\"><\/span><\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">This guide presented one reusable, open-source architecture for streaming real-time change data into TiDB from any of Debezium&#8217;s supported source databases. The architecture is a Debezium source connector, Apache Kafka, and the Debezium JDBC sink. Only Step 1, enabling capture, and a few source-specific connector properties change between databases. The transport and delivery into TiDB stay the same. Because TiDB is MySQL-compatible, the JDBC sink delivers changes with no proprietary drivers or adapters, and the same pipeline migrates pre-existing data through Debezium&#8217;s initial snapshot. That makes it suitable for both one-time migration and ongoing replication and fan-out.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">For a homogeneous MySQL-to-TiDB migration where you do not need Kafka or multiple downstream consumers, consider TiDB&#8217;s purpose-built&nbsp;<a href=\"https:\/\/docs.pingcap.com\/tidb\/stable\/dm-overview\/\">TiDB Data Migration (DM)<\/a>&nbsp;tool. Choose Debezium and Kafka when your source is not MySQL, or when you want a durable change stream that can also feed search indexes, data lakes, caches, or other services alongside TiDB.<\/p>\n\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<p class=\"wp-block-paragraph\"><em>Spin up a MySQL-compatible&nbsp;<a href=\"https:\/\/tidbcloud.com\/\">TiDB Cloud<\/a>&nbsp;cluster in minutes and point this Debezium pipeline at it today. To plan a production cutover, read the&nbsp;<a href=\"https:\/\/docs.pingcap.com\/tidb\/stable\/migration-overview\/\">TiDB migration overview<\/a>.<\/em><\/p>\n<\/blockquote>","protected":false},"excerpt":{"rendered":"<p>Key Takeaways Moving data into a new database is rarely a one-shot copy. Migrating off a legacy system, adopting a distributed SQL database, carrying out a heterogeneous database migration, or standing up an analytical replica all share the same challenge. You have to move a large, already-populated dataset and keep it continuously in sync until [&hellip;]<\/p>\n","protected":false},"author":354,"featured_media":34489,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[6],"tags":[158,54,513,433,14,514,111],"class_list":["post-34481","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-engineering","tag-cdc","tag-data-migration","tag-debezium","tag-kafka","tag-mysql","tag-postgresql","tag-tidb"],"acf":[],"featured_image_src":"https:\/\/static.pingcap.com\/files\/2026\/08\/05071543\/TiDB-CDC-landing-1800x600-1.png","author_info":{"display_name":"Pankaj Choudhary","author_link":"https:\/\/www.pingcap.com\/ko\/blog\/author\/pankaj-choudhary\/"},"yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v27.6 - https:\/\/yoast.com\/product\/yoast-seo-wordpress\/ -->\n<title>Debezium CDC to TiDB: Real-Time Migration Guide<\/title>\n<meta name=\"description\" content=\"Stream real-time changes from PostgreSQL, MySQL, Oracle, and more into TiDB using Debezium CDC and Kafka.\" \/>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/www.pingcap.com\/ko\/blog\/debezium-cdc-to-tidb\/\" \/>\n<meta property=\"og:locale\" content=\"ko_KR\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Debezium CDC to TiDB: Real-Time Migration Guide\" \/>\n<meta property=\"og:description\" content=\"Stream real-time changes from PostgreSQL, MySQL, Oracle, and more into TiDB using Debezium CDC and Kafka.\" \/>\n<meta property=\"og:url\" content=\"https:\/\/www.pingcap.com\/ko\/blog\/debezium-cdc-to-tidb\/\" \/>\n<meta property=\"og:site_name\" content=\"TiDB\" \/>\n<meta property=\"article:publisher\" content=\"https:\/\/facebook.com\/pingcap2015\" \/>\n<meta property=\"article:published_time\" content=\"2026-08-05T13:00:17+00:00\" \/>\n<meta property=\"article:modified_time\" content=\"2026-08-05T15:31:25+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/static.pingcap.com\/files\/2026\/08\/05071601\/TiDB-CDC-featured-1600x900-2.png\" \/>\n\t<meta property=\"og:image:width\" content=\"1600\" \/>\n\t<meta property=\"og:image:height\" content=\"900\" \/>\n\t<meta property=\"og:image:type\" content=\"image\/png\" \/>\n<meta name=\"author\" content=\"Pankaj Choudhary\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:creator\" content=\"@PingCAP\" \/>\n<meta name=\"twitter:site\" content=\"@PingCAP\" \/>\n<meta name=\"twitter:label1\" content=\"\uae00\uc4f4\uc774\" \/>\n\t<meta name=\"twitter:data1\" content=\"Pankaj Choudhary\" \/>\n\t<meta name=\"twitter:label2\" content=\"\uc608\uc0c1 \ub418\ub294 \ud310\ub3c5 \uc2dc\uac04\" \/>\n\t<meta name=\"twitter:data2\" content=\"10\ubd84\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\\\/\\\/schema.org\",\"@graph\":[{\"@type\":\"Article\",\"@id\":\"https:\\\/\\\/www.pingcap.com\\\/blog\\\/debezium-cdc-to-tidb\\\/#article\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/www.pingcap.com\\\/blog\\\/debezium-cdc-to-tidb\\\/\"},\"author\":{\"name\":\"Pankaj Choudhary\",\"@id\":\"https:\\\/\\\/www.pingcap.com\\\/#\\\/schema\\\/person\\\/a6a7f055b5c40cf3ad18c742a747c49c\"},\"headline\":\"Real-Time Data Migration into TiDB with Debezium CDC\",\"datePublished\":\"2026-08-05T13:00:17+00:00\",\"dateModified\":\"2026-08-05T15:31:25+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\\\/\\\/www.pingcap.com\\\/blog\\\/debezium-cdc-to-tidb\\\/\"},\"wordCount\":1523,\"publisher\":{\"@id\":\"https:\\\/\\\/www.pingcap.com\\\/#organization\"},\"image\":{\"@id\":\"https:\\\/\\\/www.pingcap.com\\\/blog\\\/debezium-cdc-to-tidb\\\/#primaryimage\"},\"thumbnailUrl\":\"https:\\\/\\\/static.pingcap.com\\\/files\\\/2026\\\/08\\\/05071543\\\/TiDB-CDC-landing-1800x600-1.png\",\"keywords\":[\"CDC\",\"Data Migration\",\"Debezium\",\"Kafka\",\"MySQL\",\"PostgreSQL\",\"TiDB\"],\"articleSection\":[\"Engineering\"],\"inLanguage\":\"ko-KR\"},{\"@type\":\"WebPage\",\"@id\":\"https:\\\/\\\/www.pingcap.com\\\/blog\\\/debezium-cdc-to-tidb\\\/\",\"url\":\"https:\\\/\\\/www.pingcap.com\\\/blog\\\/debezium-cdc-to-tidb\\\/\",\"name\":\"Debezium CDC to TiDB: Real-Time Migration Guide\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/www.pingcap.com\\\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\\\/\\\/www.pingcap.com\\\/blog\\\/debezium-cdc-to-tidb\\\/#primaryimage\"},\"image\":{\"@id\":\"https:\\\/\\\/www.pingcap.com\\\/blog\\\/debezium-cdc-to-tidb\\\/#primaryimage\"},\"thumbnailUrl\":\"https:\\\/\\\/static.pingcap.com\\\/files\\\/2026\\\/08\\\/05071543\\\/TiDB-CDC-landing-1800x600-1.png\",\"datePublished\":\"2026-08-05T13:00:17+00:00\",\"dateModified\":\"2026-08-05T15:31:25+00:00\",\"description\":\"Stream real-time changes from PostgreSQL, MySQL, Oracle, and more into TiDB using Debezium CDC and Kafka.\",\"breadcrumb\":{\"@id\":\"https:\\\/\\\/www.pingcap.com\\\/blog\\\/debezium-cdc-to-tidb\\\/#breadcrumb\"},\"inLanguage\":\"ko-KR\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\\\/\\\/www.pingcap.com\\\/blog\\\/debezium-cdc-to-tidb\\\/\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"ko-KR\",\"@id\":\"https:\\\/\\\/www.pingcap.com\\\/blog\\\/debezium-cdc-to-tidb\\\/#primaryimage\",\"url\":\"https:\\\/\\\/static.pingcap.com\\\/files\\\/2026\\\/08\\\/05071543\\\/TiDB-CDC-landing-1800x600-1.png\",\"contentUrl\":\"https:\\\/\\\/static.pingcap.com\\\/files\\\/2026\\\/08\\\/05071543\\\/TiDB-CDC-landing-1800x600-1.png\",\"width\":1800,\"height\":600},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\\\/\\\/www.pingcap.com\\\/blog\\\/debezium-cdc-to-tidb\\\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\\\/\\\/www.pingcap.com\\\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Real-Time Data Migration into TiDB with Debezium CDC\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\\\/\\\/www.pingcap.com\\\/#website\",\"url\":\"https:\\\/\\\/www.pingcap.com\\\/\",\"name\":\"TiDB\",\"description\":\"TiDB | SQL at Scale\",\"publisher\":{\"@id\":\"https:\\\/\\\/www.pingcap.com\\\/#organization\"},\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\\\/\\\/www.pingcap.com\\\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"ko-KR\"},{\"@type\":\"Organization\",\"@id\":\"https:\\\/\\\/www.pingcap.com\\\/#organization\",\"name\":\"PingCAP\",\"url\":\"https:\\\/\\\/www.pingcap.com\\\/\",\"logo\":{\"@type\":\"ImageObject\",\"inLanguage\":\"ko-KR\",\"@id\":\"https:\\\/\\\/www.pingcap.com\\\/#\\\/schema\\\/logo\\\/image\\\/\",\"url\":\"https:\\\/\\\/static.pingcap.com\\\/files\\\/2021\\\/11\\\/pingcap-logo.png\",\"contentUrl\":\"https:\\\/\\\/static.pingcap.com\\\/files\\\/2021\\\/11\\\/pingcap-logo.png\",\"width\":811,\"height\":232,\"caption\":\"PingCAP\"},\"image\":{\"@id\":\"https:\\\/\\\/www.pingcap.com\\\/#\\\/schema\\\/logo\\\/image\\\/\"},\"sameAs\":[\"https:\\\/\\\/facebook.com\\\/pingcap2015\",\"https:\\\/\\\/x.com\\\/PingCAP\",\"https:\\\/\\\/linkedin.com\\\/company\\\/pingcap\",\"https:\\\/\\\/youtube.com\\\/channel\\\/UCuq4puT32DzHKT5rU1IZpIA\"]},{\"@type\":\"Person\",\"@id\":\"https:\\\/\\\/www.pingcap.com\\\/#\\\/schema\\\/person\\\/a6a7f055b5c40cf3ad18c742a747c49c\",\"name\":\"Pankaj Choudhary\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"ko-KR\",\"@id\":\"https:\\\/\\\/static.pingcap.com\\\/files\\\/2022\\\/10\\\/17234942\\\/avatar.jpg\",\"url\":\"https:\\\/\\\/static.pingcap.com\\\/files\\\/2022\\\/10\\\/17234942\\\/avatar.jpg\",\"contentUrl\":\"https:\\\/\\\/static.pingcap.com\\\/files\\\/2022\\\/10\\\/17234942\\\/avatar.jpg\",\"caption\":\"Pankaj Choudhary\"},\"description\":\"Distributed Database Specialist\",\"url\":\"https:\\\/\\\/www.pingcap.com\\\/ko\\\/blog\\\/author\\\/pankaj-choudhary\\\/\"}]}<\/script>\n<!-- \/ Yoast SEO plugin. -->","yoast_head_json":{"title":"Debezium CDC to TiDB: Real-Time Migration Guide","description":"Stream real-time changes from PostgreSQL, MySQL, Oracle, and more into TiDB using Debezium CDC and Kafka.","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/www.pingcap.com\/ko\/blog\/debezium-cdc-to-tidb\/","og_locale":"ko_KR","og_type":"article","og_title":"Debezium CDC to TiDB: Real-Time Migration Guide","og_description":"Stream real-time changes from PostgreSQL, MySQL, Oracle, and more into TiDB using Debezium CDC and Kafka.","og_url":"https:\/\/www.pingcap.com\/ko\/blog\/debezium-cdc-to-tidb\/","og_site_name":"TiDB","article_publisher":"https:\/\/facebook.com\/pingcap2015","article_published_time":"2026-08-05T13:00:17+00:00","article_modified_time":"2026-08-05T15:31:25+00:00","og_image":[{"width":1600,"height":900,"url":"https:\/\/static.pingcap.com\/files\/2026\/08\/05071601\/TiDB-CDC-featured-1600x900-2.png","type":"image\/png"}],"author":"Pankaj Choudhary","twitter_card":"summary_large_image","twitter_creator":"@PingCAP","twitter_site":"@PingCAP","twitter_misc":{"\uae00\uc4f4\uc774":"Pankaj Choudhary","\uc608\uc0c1 \ub418\ub294 \ud310\ub3c5 \uc2dc\uac04":"10\ubd84"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/www.pingcap.com\/blog\/debezium-cdc-to-tidb\/#article","isPartOf":{"@id":"https:\/\/www.pingcap.com\/blog\/debezium-cdc-to-tidb\/"},"author":{"name":"Pankaj Choudhary","@id":"https:\/\/www.pingcap.com\/#\/schema\/person\/a6a7f055b5c40cf3ad18c742a747c49c"},"headline":"Real-Time Data Migration into TiDB with Debezium CDC","datePublished":"2026-08-05T13:00:17+00:00","dateModified":"2026-08-05T15:31:25+00:00","mainEntityOfPage":{"@id":"https:\/\/www.pingcap.com\/blog\/debezium-cdc-to-tidb\/"},"wordCount":1523,"publisher":{"@id":"https:\/\/www.pingcap.com\/#organization"},"image":{"@id":"https:\/\/www.pingcap.com\/blog\/debezium-cdc-to-tidb\/#primaryimage"},"thumbnailUrl":"https:\/\/static.pingcap.com\/files\/2026\/08\/05071543\/TiDB-CDC-landing-1800x600-1.png","keywords":["CDC","Data Migration","Debezium","Kafka","MySQL","PostgreSQL","TiDB"],"articleSection":["Engineering"],"inLanguage":"ko-KR"},{"@type":"WebPage","@id":"https:\/\/www.pingcap.com\/blog\/debezium-cdc-to-tidb\/","url":"https:\/\/www.pingcap.com\/blog\/debezium-cdc-to-tidb\/","name":"Debezium CDC to TiDB: Real-Time Migration Guide","isPartOf":{"@id":"https:\/\/www.pingcap.com\/#website"},"primaryImageOfPage":{"@id":"https:\/\/www.pingcap.com\/blog\/debezium-cdc-to-tidb\/#primaryimage"},"image":{"@id":"https:\/\/www.pingcap.com\/blog\/debezium-cdc-to-tidb\/#primaryimage"},"thumbnailUrl":"https:\/\/static.pingcap.com\/files\/2026\/08\/05071543\/TiDB-CDC-landing-1800x600-1.png","datePublished":"2026-08-05T13:00:17+00:00","dateModified":"2026-08-05T15:31:25+00:00","description":"Stream real-time changes from PostgreSQL, MySQL, Oracle, and more into TiDB using Debezium CDC and Kafka.","breadcrumb":{"@id":"https:\/\/www.pingcap.com\/blog\/debezium-cdc-to-tidb\/#breadcrumb"},"inLanguage":"ko-KR","potentialAction":[{"@type":"ReadAction","target":["https:\/\/www.pingcap.com\/blog\/debezium-cdc-to-tidb\/"]}]},{"@type":"ImageObject","inLanguage":"ko-KR","@id":"https:\/\/www.pingcap.com\/blog\/debezium-cdc-to-tidb\/#primaryimage","url":"https:\/\/static.pingcap.com\/files\/2026\/08\/05071543\/TiDB-CDC-landing-1800x600-1.png","contentUrl":"https:\/\/static.pingcap.com\/files\/2026\/08\/05071543\/TiDB-CDC-landing-1800x600-1.png","width":1800,"height":600},{"@type":"BreadcrumbList","@id":"https:\/\/www.pingcap.com\/blog\/debezium-cdc-to-tidb\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/www.pingcap.com\/"},{"@type":"ListItem","position":2,"name":"Real-Time Data Migration into TiDB with Debezium CDC"}]},{"@type":"WebSite","@id":"https:\/\/www.pingcap.com\/#website","url":"https:\/\/www.pingcap.com\/","name":"\ud2f0DB","description":"TiDB | SQL at Scale","publisher":{"@id":"https:\/\/www.pingcap.com\/#organization"},"potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/www.pingcap.com\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"ko-KR"},{"@type":"Organization","@id":"https:\/\/www.pingcap.com\/#organization","name":"PingCAP","url":"https:\/\/www.pingcap.com\/","logo":{"@type":"ImageObject","inLanguage":"ko-KR","@id":"https:\/\/www.pingcap.com\/#\/schema\/logo\/image\/","url":"https:\/\/static.pingcap.com\/files\/2021\/11\/pingcap-logo.png","contentUrl":"https:\/\/static.pingcap.com\/files\/2021\/11\/pingcap-logo.png","width":811,"height":232,"caption":"PingCAP"},"image":{"@id":"https:\/\/www.pingcap.com\/#\/schema\/logo\/image\/"},"sameAs":["https:\/\/facebook.com\/pingcap2015","https:\/\/x.com\/PingCAP","https:\/\/linkedin.com\/company\/pingcap","https:\/\/youtube.com\/channel\/UCuq4puT32DzHKT5rU1IZpIA"]},{"@type":"Person","@id":"https:\/\/www.pingcap.com\/#\/schema\/person\/a6a7f055b5c40cf3ad18c742a747c49c","name":"Pankaj Choudhary","image":{"@type":"ImageObject","inLanguage":"ko-KR","@id":"https:\/\/static.pingcap.com\/files\/2022\/10\/17234942\/avatar.jpg","url":"https:\/\/static.pingcap.com\/files\/2022\/10\/17234942\/avatar.jpg","contentUrl":"https:\/\/static.pingcap.com\/files\/2022\/10\/17234942\/avatar.jpg","caption":"Pankaj Choudhary"},"description":"Distributed Database Specialist","url":"https:\/\/www.pingcap.com\/ko\/blog\/author\/pankaj-choudhary\/"}]}},"grav_blocks":false,"card_markup":"<a class=\"card-resource bg-white\" href=\"https:\/\/www.pingcap.com\/ko\/blog\/debezium-cdc-to-tidb\/\"><div class=\"card-resource__image-container\"><img class=\"card-resource__image\" alt=\"TiDB-CDC-landing-1800x600\" src=\"https:\/\/static.pingcap.com\/files\/2026\/08\/05071543\/TiDB-CDC-landing-1800x600-1.png\" loading=\"lazy\" width=1800 height=600 \/><\/div><div class=\"card-resource__content-container\"><div class=\"card-resource__content-head\"><div class=\"card-resource__category\">Engineering<\/div><\/div><h5 class=\"card-resource__title\">Real-Time Data Migration into TiDB with Debezium CDC<\/h5><\/div><\/a>","_links":{"self":[{"href":"https:\/\/www.pingcap.com\/ko\/wp-json\/wp\/v2\/posts\/34481","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.pingcap.com\/ko\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.pingcap.com\/ko\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.pingcap.com\/ko\/wp-json\/wp\/v2\/users\/354"}],"replies":[{"embeddable":true,"href":"https:\/\/www.pingcap.com\/ko\/wp-json\/wp\/v2\/comments?post=34481"}],"version-history":[{"count":2,"href":"https:\/\/www.pingcap.com\/ko\/wp-json\/wp\/v2\/posts\/34481\/revisions"}],"predecessor-version":[{"id":34491,"href":"https:\/\/www.pingcap.com\/ko\/wp-json\/wp\/v2\/posts\/34481\/revisions\/34491"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.pingcap.com\/ko\/wp-json\/wp\/v2\/media\/34489"}],"wp:attachment":[{"href":"https:\/\/www.pingcap.com\/ko\/wp-json\/wp\/v2\/media?parent=34481"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.pingcap.com\/ko\/wp-json\/wp\/v2\/categories?post=34481"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.pingcap.com\/ko\/wp-json\/wp\/v2\/tags?post=34481"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}