{"id":34481,"date":"2026-08-04T06:00:17","date_gmt":"2026-08-04T13:00:17","guid":{"rendered":"https:\/\/www.pingcap.com\/?p=34481"},"modified":"2026-08-05T14:37:27","modified_gmt":"2026-08-05T21:37:27","slug":"debezium-cdc-to-tidb","status":"publish","type":"post","link":"https:\/\/www.pingcap.com\/ko\/blog\/debezium-cdc-to-tidb\/","title":{"rendered":"Migrating Real-Time Data into TiDB with Debezium CDC"},"content":{"rendered":"<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<h2 class=\"wp-block-heading\"><span class=\"ez-toc-section\" id=\"Key_Takeaways\"><\/span>\ud575\uc2ec \uc694\uc57d<span class=\"ez-toc-section-end\"><\/span><\/h2>\n\n\n\n<ul class=\"wp-block-list\">\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\n\n\n<li><strong>One pipeline, any source.<\/strong>&nbsp;Debezium 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>&nbsp;TiDB speaks the MySQL protocol, so the Debezium JDBC sink writes over a standard&nbsp;<code>jdbc:mysql:\/\/<\/code>&nbsp;connection.<\/li>\n\n\n\n<li><strong>Migrate and stream in one setup.<\/strong>&nbsp;The initial snapshot seeds existing rows, then switches to streaming live changes.<\/li>\n\n\n\n<li><strong>Near-zero downtime.<\/strong>&nbsp;Existing data and live changes arrive as one gap-free stream.<\/li>\n<\/ul>\n<\/blockquote>\n\n\n\n<p class=\"wp-block-paragraph\">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 <a href=\"https:\/\/www.pingcap.com\/ko\/what-is-tidb\/\">\ud2f0DB<\/a>. 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<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<p class=\"wp-block-paragraph\"><em>Spin up a MySQL-compatible\u00a0<a href=\"https:\/\/tidbcloud.com\/\">TiDB Cloud<\/a>\u00a0cluster in minutes and point this Debezium pipeline at it today. <\/em><\/p>","protected":false},"excerpt":{"rendered":"<p>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 [&hellip;]<\/p>\n","protected":false},"author":354,"featured_media":34489,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[436],"tags":[158,54,513,14,514,111],"class_list":["post-34481","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-tutorial","tag-cdc","tag-data-migration","tag-debezium","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 v28.2 - 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-04T13:00:17+00:00\" \/>\n<meta property=\"article:modified_time\" content=\"2026-08-05T21:37:27+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\":\"Migrating Real-Time Data into TiDB with Debezium CDC\",\"datePublished\":\"2026-08-04T13:00:17+00:00\",\"dateModified\":\"2026-08-05T21:37:27+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\\\/\\\/www.pingcap.com\\\/blog\\\/debezium-cdc-to-tidb\\\/\"},\"wordCount\":1514,\"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\",\"MySQL\",\"PostgreSQL\",\"TiDB\"],\"articleSection\":[\"Tutorial\"],\"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-04T13:00:17+00:00\",\"dateModified\":\"2026-08-05T21:37:27+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\":\"Migrating Real-Time Data 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-04T13:00:17+00:00","article_modified_time":"2026-08-05T21:37:27+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":"Migrating Real-Time Data into TiDB with Debezium CDC","datePublished":"2026-08-04T13:00:17+00:00","dateModified":"2026-08-05T21:37:27+00:00","mainEntityOfPage":{"@id":"https:\/\/www.pingcap.com\/blog\/debezium-cdc-to-tidb\/"},"wordCount":1514,"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","MySQL","PostgreSQL","TiDB"],"articleSection":["Tutorial"],"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-04T13:00:17+00:00","dateModified":"2026-08-05T21:37:27+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":"Migrating Real-Time Data 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\">Tutorial<\/div><\/div><h5 class=\"card-resource__title\">Migrating Real-Time Data 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":16,"href":"https:\/\/www.pingcap.com\/ko\/wp-json\/wp\/v2\/posts\/34481\/revisions"}],"predecessor-version":[{"id":34507,"href":"https:\/\/www.pingcap.com\/ko\/wp-json\/wp\/v2\/posts\/34481\/revisions\/34507"}],"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}]}}