使用Docker部署debezium来监控 MySQL 数据库
Debezium
是一个分布式平台,它将来自现有数据库的信息转换为事件流,使应用程序能够检测并立即响应数据库中的行级更改。
Debezium
构建在Apache Kafka
之上,并提供了一组Kafka Connect
兼容的连接器。每个连接器都与特定的数据库管理系统(DBMS
)一起工作。连接器通过检测发生的变化来记录DBMS
中数据变化的历史,并将每个变化事件的记录流式传输到Kafka topic
。然后,消费应用程序可以从Kafka topic
中读取结果事件记录。
通过利用Kafka
可靠的流平台,Debezium
使应用程序能够正确和完整地消费数据库中发生的更改。即使您的应用程序意外停止或失去连接,它也不会错过停机期间发生的事件。应用程序重新启动后,它将从停止的位置继续从topic
读取。
debezium
包含了下面这些连接器:
Debezium connector for Cassandra
Debezium connector for Db2
Debezium connector for MongoDB
Debezium connector for MySQL
Debezium connector for Oracle Database
Debezium connector for PostgreSQL
Debezium connector for SQL Server
Debezium connector for Vitess
下面我们将使用docker
部署一个debezium mysql connector
,来监控 MySQL
数据库的变化。
1.准备工作
安装并启动 docker
,如果还没有安装,可以参考:官方安装指南
2.启动服务
使用Debezium
需要三个独立的服务:ZooKeeper
、Kafka
和Debezium connector
。在本教程中,我们将使用Docker
和Debezium
提供的容器镜像来启动每个服务。
为了减少服务启动所需的资源,以及简化操作步骤和配置,我们都将以单机的方式来启动 zookeeper 和 kafka。
为了在使用完容器之后能够更容易的清除数据,我们在启动容器的时候不进行卷挂载,这样容器停止的时候所保存的数据都会丢失。
并且,为了能够实时看到容器输出,我们将不使用-d
的方式在后台启动,而是将容器的所有输出都显示在启动它的终端,并且每个启动的服务都将有一个独立的终端。
2.1 启动 zookeeper
ZooKeeper
是第一个必须启动的服务。
步骤:
(1)使用1.9 版本的quay.io/debezium/zookeeper
镜像运行一个新容器
docker run -it --rm --name zookeeper -p 2181:2181 -p 2888:2888 -p 3888:3888 quay.io/debezium/zookeeper:1.9
命令解析:
-it
:容器是交互式的,这意味着终端的标准输入和输出附加到容器上。--rm
:容器停止时将被移除。--name zookeeper
:指定容器的名称为 zookeeper。-p 2181:2181 -p 2888:2888 -p 3888:3888
:将容器的三个端口映射到Docker
主机上的相同端口,这样其他容器(以及容器外的应用程序)才能够与ZooKeeper
通信。
(2)验证ZooKeeper
是否启动。
如果看到类似以下的输出,就说明 zookeeper
已经成功启动。
Starting up in standalone mode
/usr/bin/java
ZooKeeper JMX enabled by default
Using config: /zookeeper/conf/zoo.cfg
...
port 0.0.0.0/0.0.0.0:2181
2.2 启动 kafka
启动ZooKeeper
后,接下来我们在一个新的容器中启动Kafka
。
步骤:
(1)使用1.9版本的quay.io/debezium/kafka
镜像运行一个新容器:
docker run -it --rm --name kafka -p 9092:9092 --link zookeeper:zookeeper quay.io/debezium/kafka:1.9
命令解析:
-it
:容器是交互式的,这意味着终端的标准输入和输出都附加在容器上。--rm
:容器停止时将被移除。--name kafka
:指定容器名称为kafka。-p 9092:9092
:将容器中的9092端口映射到Docker主机上的相同端口,以便容器外的应用程序可以与Kafka通信。--link zookeeper:zookeeper
:告诉容器它可以在zookeeper
容器中找到zookeeper
,该容器在同一目录下运行。
(2)验证kafka
是否启动。
如果看到类似以下的输出,则说明 kafka
已经成功启动。
Starting in ZooKeeper mode using NODE_ID=1.
Using ZOOKEEPER_CONNECT=172.17.0.2:2181
Using configuration config/server.properties.
Using KAFKA_LISTENERS=PLAINTEXT://172.17.0.4:9092 and KAFKA_ADVERTISED_LISTENERS=PLAINTEXT://172.17.0.4:9092
2023-09-02 16:43:50,944 - INFO [main:Log4jControllerRegistration$@31] - Registered kafka:type=kafka.Log4jController MBean
2023-09-02 16:43:51,682 - INFO [main:X509Util@77] - Setting -D jdk.tls.rejectClientInitiatedRenegotiation=true to disable client-initiated TLS renegotiation
2023-09-02 16:43:52,032 - INFO [main:LoggingSignalHandler@72] - Registered signal handlers for TERM, INT, HUP
2023-09-02 16:43:52,047 - INFO [main:Logging@66] - starting
2023-09-02 16:43:52,050 - INFO [main:Logging@66] - Connecting to zookeeper on 172.17.0.2:2181
2023-09-02 16:43:52,109 - INFO [main:Logging@66] - [ZooKeeperClient Kafka server] Initializing a new session to 172.17.0.2:2181.
...
2023-09-02 16:43:56,541 - INFO [main:AppInfoParser$AppInfo@119] - Kafka version: 3.2.0
2023-09-02 16:43:56,542 - INFO [main:AppInfoParser$AppInfo@120] - Kafka commitId: 38103ffaa962ef50
2023-09-02 16:43:56,542 - INFO [main:AppInfoParser$AppInfo@121] - Kafka startTimeMs: 1693673036411
2023-09-02 16:43:56,578 - INFO [main:Logging@66] - [KafkaServer id=1] started
[KafkaServer id=1] started
表示 kafka 已经成功启动,并为客户端连接做好准备。
2.3 启动 MySQL
此时我们已经启动了ZooKeeper
和Kafka
,现在需要启动一个 MySQL 数据库服务。我们将使用一个示例数据库启动MySQL
服务器。
步骤:
(1)打开一个新的终端,使用下面的命令启动一个新的容器,该容器运行预先配置了inventory
数据库的MySQL
数据库服务器。
该命令使用1.9版本的quay.io/debezium/example-mysql
镜像运行一个新容器。基于mysql:8.0
镜像。它还定义并填充了一个inventory
数据库。
docker run -it --rm --name mysql -p 3306:3306 \
-e MYSQL_ROOT_PASSWORD=debezium -e MYSQL_USER=mysqluser -e MYSQL_PASSWORD=mysqlpw \
quay.io/debezium/example-mysql:1.9
命令解析:
-it
:容器是交互式的,这意味着终端的标准输入和输出都附加在容器上。--rm
:容器停止时将被移除。--name mysql
:指定容器名称为mysql。-p 3306:3306
:将容器中的3306
端口(MySQL
的默认端口)映射到Docker主机上的相同端口,以便容器外的应用程序可以与启动的MySQL
服务通信。-e MYSQL_ROOT_PASSWORD=debezium -e MYSQL_USER=mysqluser -e MYSQL_PASSWORD=mysqlpw
:创建具有Debezium MySQL连接器所需的最低权限的用户和密码。
(2)验证
如果看到类似下面的输出,说明 MySQL 服务已经正常启动。
...
2023-09-02T16:37:34.067920Z 0 [System] [MY-011323] [Server] X Plugin ready for connections. Bind-address: '::' port: 33060, socket: /var/run/mysqld/mysqlx.sock
2023-09-02T16:37:34.068725Z 0 [System] [MY-010931] [Server] /usr/sbin/mysqld: ready for connections. Version: '8.0.31' socket: '/var/run/mysqld/mysqld.sock' port: 3306 MySQL Community Server - GPL.
然后就可以通过 mysql 连接工具来连接本地启动的 mysql 服务了,也可以执行类似use inventory; show tables; select * from customers;
这些 SQL 语句来查看当前 mysql
的已有的表和数据了。
2.4 启动 kafka connect
现在可以启动 Kafka Connect 服务,它公开了一个REST API来管理Debezium MySQL连接器。
步骤:
(1)开启一个新的终端,使用镜像 quay.io/debezium/connect:2.3
来运行一个新的容器。
docker run -it --rm --name connect -p 8083:8083 \
-e GROUP_ID=1 \
-e CONFIG_STORAGE_TOPIC=my_connect_configs \
-e OFFSET_STORAGE_TOPIC=my_connect_offsets \
-e STATUS_STORAGE_TOPIC=my_connect_statuses \
--link kafka:kafka --link mysql:mysql \
quay.io/debezium/connect:2.3
命令解析:
--name connect
:指定启动的容器名称-p 8083:8083
:将容器中的8083
端口映射到Docker
主机上的相同端口。这使得容器外的应用程序可以使用Kafka Connect
的REST API
来设置和管理新的容器实例。-e GROUP_ID=1 -e CONFIG_STORAGE_TOPIC=my_connect_configs -e OFFSET_STORAGE_TOPIC=my_connect_offsets -e STATUS_STORAGE_TOPIC=my_connect_statuses
:设置镜像所需的环境变量--link kafka:kafka --link mysql:mysql
:将容器链接到正在运行的Kafka
和MySQL
容器。
(2)验证
如果看到类似下面的输出,则说明启动成功。
...
2023-09-04 16:48:33,939 INFO || Kafka version: 3.4.0 [org.apache.kafka.common.utils.AppInfoParser]
...
2023-09-04 16:48:34,485 INFO || [Worker clientId=connect-1, groupId=1] Starting connectors and tasks using config offset -1 [org.apache.kafka.connect.runtime.distributed.DistributedHerder]
2023-09-04 16:48:34,485 INFO || [Worker clientId=connect-1, groupId=1] Finished starting connectors and tasks [org.apache.kafka.connect.runtime.distributed.DistributedHerder]
下面我们使用 Kafka Connect REST API
来检查 Kafka Connect
服务的状态。
打开一个新的终端,执行如下命令:
curl -H "Accept:application/json" localhost:8083/
{"version":"3.4.0","commit":"cb8625948210849f"}
表明Kafka Connect版本3.4.0正在运行。
接下来我们查看以下 Kafka Connect
中注册的连接器列表:
curl -H "Accept:application/json" localhost:8083/connectors/
[]
表明 Kafka Connect
目前没有注册连接器。
3. 部署 MySQL 连接器
3.1 注册MySQL连接器来监控数据库
现在 Kafka Connect
服务中没有注册的连接器,我们现在开始注册一个 Debezium MySQL Connector
,来监视 inventory
数据库中数据表的变化。对于 MySQL
数据库来说,Debezium MySQL Connector
通过监听源数据表的 binlog
来获取数据库所有的变更事件。
对于 MySQL
数据库来说,binlog
记录了数据库的所有事务(例如对单个行的更改和对模式的更改)。当数据库中的一行发生更改时,Debezium
生成一个更改事件。
在生产环境中,通常要么使用Kafka
工具手动创建topic
,在创建的时候指定分区和副本的数量,要么使用Kafka Connect
机制自定义自动创建主题的设置。在本教程中,Kafka
被配置为只使用一个副本自动创建 topic
。
步骤:
(1)准备将要注册的Debezium MySQL
连接器的配置。
我们要注册的连接器的配置如下:
{
"name": "inventory-connector",
"config": {
"connector.class": "io.debezium.connector.mysql.MySqlConnector",
"tasks.max": "1",
"database.hostname": "mysql",
"database.port": "3306",
"database.user": "root",
"database.password": "debezium",
"database.server.id": "184054",
"database.server.name": "dbserver1",
"database.include.list": "inventory",
"database.history.kafka.bootstrap.servers": "kafka:9092",
"database.history.kafka.topic": "schema-changes.inventory"
}
}
配置说明:
"name": "inventory-connector"
:连接器的名称"config"
:连接器的配置"tasks.max": "1"
:任何时候只能操作一个任务。因为MySQL连接器读取MySQL服务器的binlog,所以使用单个连接器任务可以确保正确的顺序和事件处理。Kafka Connect服务使用连接器来启动一个或多个完成工作的任务,并自动将正在运行的任务分布到Kafka Connect服务的集群中。如果任何服务停止或崩溃,这些任务将被重新分配给正在运行的服务。"database.hostname": "mysql"
:数据库主机,即MySQL服务器所在的Docker容器的名称。Docker操作容器中的网络堆栈,这样每个链接的容器都可以用/etc/hosts来解析,使用容器名作为主机名。如果MySQL运行在一个正常的网络上,可以为这个值指定IP地址或可解析的主机名。"database.server.id": "184054", "database.server.name": "dbserver1"
:唯一的服务器ID和名称。服务器名称是MySQL服务器或服务器集群的逻辑标识符。这个名字将被用作所有Kafka topic的前缀。"database.include.list": "inventory"
:指定要监听的数据库的范围,只会检测到inventory
数据库中的更改。“database.history.kafka.bootstrap.servers“
:指定存储数据库历史schema
信息所使用的kafka
服务。”database.history.kafka.topic”
:指定 kafka 存储数据库历史 schema 信息所使用的 kafka topic 名称。
出于安全考虑,不应该将纯文本形式的密码或其他秘密放入连接器配置中。
(2)打开一个新的终端,并使用curl
命令注册Debezium MySQL
连接器。
该命令使用Kafka Connect
服务的API
向/connectors
资源提交POST
请求,并使用描述新连接器(称为inventory-connector
)的JSON
文档。该命令使用localhost
连接到Docker
主机。如果您使用的是非本地Docker
平台,请将localhost
替换为您的Docker
主机的IP
地址。
curl -i -X POST -H "Accept:application/json" -H "Content-Type:application/json" localhost:8083/connectors/ \
-d '{ "name": "inventory-connector", "config": { "connector.class": "io.debezium.connector.mysql.MySqlConnector", "tasks.max": "1", "database.hostname": "mysql", "database.port": "3306", "database.user": "root", "database.password": "debezium", "database.server.id": "184054", "database.server.name": "dbserver1", "database.include.list": "inventory", "database.history.kafka.bootstrap.servers": "kafka:9092", "database.history.kafka.topic": "dbhistory.inventory" } }'
(3)验证inventory-connector
是否包含在连接器列表中:
curl -H "Accept:application/json" localhost:8083/connectors/
["inventory-connector"]
(4)查看连接器的任务:
curl -i -X GET -H "Accept:application/json" localhost:8083/connectors/inventory-connector
应该看到类似于以下的响应(为便于阅读而格式化):
HTTP/1.1 200 OK
Date: Thu, 06 Feb 2020 22:12:03 GMT
Content-Type: application/json
Content-Length: 531
Server: Jetty(9.4.20.v20190813)
{
"name": "inventory-connector",
...
"tasks": [
{
"connector": "inventory-connector",
"task": 0
}
]
}
连接器运行单个任务(任务0)来完成其工作。连接器只支持单个任务,因为MySQL
将其所有活动记录在一个顺序的binlog
中。这意味着连接器只需要一个读取器就可以获得所有事件的一致姓有序视图。
3.2 观察连接器启动
当我们注册一个新的连接器时,kafka connect
容器中会生成大量日志,通过查看这些日志我们可以理解连接器从创建到开始读取 MySQL
服务器的 binlog
所经历的过程。
默认情况下,会首先创建和启动连接器 inventory-connector
...
2021-11-30 01:38:44,223 INFO || [Worker clientId=connect-1, groupId=1] Tasks [inventory-connector-0] configs updated [org.apache.kafka.connect.runtime.distributed.DistributedHerder]
2021-11-30 01:38:44,224 INFO || [Worker clientId=connect-1, groupId=1] Handling task config update by restarting tasks [] [org.apache.kafka.connect.runtime.distributed.DistributedHerder]
2021-11-30 01:38:44,224 INFO || [Worker clientId=connect-1, groupId=1] Rebalance started [org.apache.kafka.connect.runtime.distributed.WorkerCoordinator]
2021-11-30 01:38:44,224 INFO || [Worker clientId=connect-1, groupId=1] (Re-)joining group [org.apache.kafka.connect.runtime.distributed.WorkerCoordinator]
2021-11-30 01:38:44,227 INFO || [Worker clientId=connect-1, groupId=1] Successfully joined group with generation Generation{generationId=3, memberId='connect-1-7b087c69-8ac5-4c56-9e6b-ec5adabf27e8', protocol='sessioned'} [org.apache.kafka.connect.runtime.distributed.WorkerCoordinator]
2021-11-30 01:38:44,230 INFO || [Worker clientId=connect-1, groupId=1] Successfully synced group in generation Generation{generationId=3, memberId='connect-1-7b087c69-8ac5-4c56-9e6b-ec5adabf27e8', protocol='sessioned'} [org.apache.kafka.connect.runtime.distributed.WorkerCoordinator]
2021-11-30 01:38:44,231 INFO || [Worker clientId=connect-1, groupId=1] Joined group at generation 3 with protocol version 2 and got assignment: Assignment{error=0, leader='connect-1-7b087c69-8ac5-4c56-9e6b-ec5adabf27e8', leaderUrl='http://172.17.0.7:8083/', offset=4, connectorIds=[inventory-connector], taskIds=[inventory-connector-0], revokedConnectorIds=[], revokedTaskIds=[], delay=0} with rebalance delay: 0 [org.apache.kafka.connect.runtime.distributed.DistributedHerder]
2021-11-30 01:38:44,232 INFO || [Worker clientId=connect-1, groupId=1] Starting connectors and tasks using config offset 4 [org.apache.kafka.connect.runtime.distributed.DistributedHerder]
2021-11-30 01:38:44,232 INFO || [Worker clientId=connect-1, groupId=1] Starting task inventory-connector-0 [org.apache.kafka.connect.runtime.distributed.DistributedHerder]
...
接下来,连接器开始执行快照,即对读取存量并写入 kafka topic:
...
2021-11-30 01:38:44,534 INFO MySQL|dbserver1|snapshot Snapshot step 1 - Preparing [io.debezium.relational.RelationalSnapshotChangeEventSource]
2021-11-30 01:38:44,535 INFO MySQL|dbserver1|snapshot Snapshot step 2 - Determining captured tables [io.debezium.relational.RelationalSnapshotChangeEventSource]
2021-11-30 01:38:44,535 INFO MySQL|dbserver1|snapshot Read list of available databases [io.debezium.connector.mysql.MySqlSnapshotChangeEventSource]
2021-11-30 01:38:44,537 INFO MySQL|dbserver1|snapshot list of available databases is: [information_schema, inventory, mysql, performance_schema, sys] [io.debezium.connector.mysql.MySqlSnapshotChangeEventSource]
2021-11-30 01:38:44,537 INFO MySQL|dbserver1|snapshot Read list of available tables in each database [io.debezium.connector.mysql.MySqlSnapshotChangeEventSource]
2021-11-30 01:38:44,548 INFO MySQL|dbserver1|snapshot snapshot continuing with database(s): [inventory] [io.debezium.connector.mysql.MySqlSnapshotChangeEventSource]
2021-11-30 01:38:44,551 INFO MySQL|dbserver1|snapshot Snapshot step 3 - Locking captured tables [inventory.addresses, inventory.customers, inventory.geom, inventory.orders, inventory.products, inventory.products_on_hand] [io.debezium.relational.RelationalSnapshotChangeEventSource]
2021-11-30 01:38:44,552 INFO MySQL|dbserver1|snapshot Flush and obtain global read lock to prevent writes to database [io.debezium.connector.mysql.MySqlSnapshotChangeEventSource]
2021-11-30 01:38:44,557 INFO MySQL|dbserver1|snapshot Snapshot step 4 - Determining snapshot offset [io.debezium.relational.RelationalSnapshotChangeEventSource]
2021-11-30 01:38:44,560 INFO MySQL|dbserver1|snapshot Read binlog position of MySQL primary server [io.debezium.connector.mysql.MySqlSnapshotChangeEventSource]
2021-11-30 01:38:44,562 INFO MySQL|dbserver1|snapshot using binlog 'mysql-bin.000003' at position '156' and gtid '' [io.debezium.connector.mysql.MySqlSnapshotChangeEventSource]
2021-11-30 01:38:44,562 INFO MySQL|dbserver1|snapshot Snapshot step 5 - Reading structure of captured tables [io.debezium.relational.RelationalSnapshotChangeEventSource]
2021-11-30 01:38:44,562 INFO MySQL|dbserver1|snapshot All eligible tables schema should be captured, capturing: [inventory.addresses, inventory.customers, inventory.geom, inventory.orders, inventory.products, inventory.products_on_hand] [io.debezium.connector.mysql.MySqlSnapshotChangeEventSource]
2021-11-30 01:38:45,058 INFO MySQL|dbserver1|snapshot Reading structure of database 'inventory' [io.debezium.connector.mysql.MySqlSnapshotChangeEventSource]
2021-11-30 01:38:45,187 INFO MySQL|dbserver1|snapshot Snapshot step 6 - Persisting schema history [io.debezium.relational.RelationalSnapshotChangeEventSource]
2021-11-30 01:38:45,273 INFO MySQL|dbserver1|snapshot Releasing global read lock to enable MySQL writes [io.debezium.connector.mysql.MySqlSnapshotChangeEventSource]
2021-11-30 01:38:45,274 INFO MySQL|dbserver1|snapshot Writes to MySQL tables prevented for a total of 00:00:00.717 [io.debezium.connector.mysql.MySqlSnapshotChangeEventSource]
2021-11-30 01:38:45,274 INFO MySQL|dbserver1|snapshot Snapshot step 7 - Snapshotting data [io.debezium.relational.RelationalSnapshotChangeEventSource]
2021-11-30 01:38:45,275 INFO MySQL|dbserver1|snapshot Snapshotting contents of 6 tables while still in transaction [io.debezium.relational.RelationalSnapshotChangeEventSource]
2021-11-30 01:38:45,275 INFO MySQL|dbserver1|snapshot Exporting data from table 'inventory.addresses' (1 of 6 tables) [io.debezium.relational.RelationalSnapshotChangeEventSource]
2021-11-30 01:38:45,276 INFO MySQL|dbserver1|snapshot For table 'inventory.addresses' using select statement: 'SELECT `id`, `customer_id`, `street`, `city`, `state`, `zip`, `type` FROM `inventory`.`addresses`' [io.debezium.relational.RelationalSnapshotChangeEventSource]
2021-11-30 01:38:45,295 INFO MySQL|dbserver1|snapshot Finished exporting 7 records for table 'inventory.addresses'; total duration '00:00:00.02' [io.debezium.relational.RelationalSnapshotChangeEventSource]
2021-11-30 01:38:45,296 INFO MySQL|dbserver1|snapshot Exporting data from table 'inventory.customers' (2 of 6 tables) [io.debezium.relational.RelationalSnapshotChangeEventSource]
2021-11-30 01:38:45,296 INFO MySQL|dbserver1|snapshot For table 'inventory.customers' using select statement: 'SELECT `id`, `first_name`, `last_name`, `email` FROM `inventory`.`customers`' [io.debezium.relational.RelationalSnapshotChangeEventSource]
2021-11-30 01:38:45,304 INFO MySQL|dbserver1|snapshot Finished exporting 4 records for table 'inventory.customers'; total duration '00:00:00.008' [io.debezium.relational.RelationalSnapshotChangeEventSource]
2021-11-30 01:38:45,304 INFO MySQL|dbserver1|snapshot Exporting data from table 'inventory.geom' (3 of 6 tables) [io.debezium.relational.RelationalSnapshotChangeEventSource]
2021-11-30 01:38:45,305 INFO MySQL|dbserver1|snapshot For table 'inventory.geom' using select statement: 'SELECT `id`, `g`, `h` FROM `inventory`.`geom`' [io.debezium.relational.RelationalSnapshotChangeEventSource]
2021-11-30 01:38:45,316 INFO MySQL|dbserver1|snapshot Finished exporting 3 records for table 'inventory.geom'; total duration '00:00:00.011' [io.debezium.relational.RelationalSnapshotChangeEventSource]
2021-11-30 01:38:45,316 INFO MySQL|dbserver1|snapshot Exporting data from table 'inventory.orders' (4 of 6 tables) [io.debezium.relational.RelationalSnapshotChangeEventSource]
2021-11-30 01:38:45,316 INFO MySQL|dbserver1|snapshot For table 'inventory.orders' using select statement: 'SELECT `order_number`, `order_date`, `purchaser`, `quantity`, `product_id` FROM `inventory`.`orders`' [io.debezium.relational.RelationalSnapshotChangeEventSource]
2021-11-30 01:38:45,325 INFO MySQL|dbserver1|snapshot Finished exporting 4 records for table 'inventory.orders'; total duration '00:00:00.008' [io.debezium.relational.RelationalSnapshotChangeEventSource]
2021-11-30 01:38:45,325 INFO MySQL|dbserver1|snapshot Exporting data from table 'inventory.products' (5 of 6 tables) [io.debezium.relational.RelationalSnapshotChangeEventSource]
2021-11-30 01:38:45,325 INFO MySQL|dbserver1|snapshot For table 'inventory.products' using select statement: 'SELECT `id`, `name`, `description`, `weight` FROM `inventory`.`products`' [io.debezium.relational.RelationalSnapshotChangeEventSource]
2021-11-30 01:38:45,343 INFO MySQL|dbserver1|snapshot Finished exporting 9 records for table 'inventory.products'; total duration '00:00:00.017' [io.debezium.relational.RelationalSnapshotChangeEventSource]
2021-11-30 01:38:45,344 INFO MySQL|dbserver1|snapshot Exporting data from table 'inventory.products_on_hand' (6 of 6 tables) [io.debezium.relational.RelationalSnapshotChangeEventSource]
2021-11-30 01:38:45,344 INFO MySQL|dbserver1|snapshot For table 'inventory.products_on_hand' using select statement: 'SELECT `product_id`, `quantity` FROM `inventory`.`products_on_hand`' [io.debezium.relational.RelationalSnapshotChangeEventSource]
2021-11-30 01:38:45,353 INFO MySQL|dbserver1|snapshot Finished exporting 9 records for table 'inventory.products_on_hand'; total duration '00:00:00.009' [io.debezium.relational.RelationalSnapshotChangeEventSource]
2021-11-30 01:38:45,355 INFO MySQL|dbserver1|snapshot Snapshot - Final stage [io.debezium.pipeline.source.AbstractSnapshotChangeEventSource]
2021-11-30 01:38:45,356 INFO MySQL|dbserver1|snapshot Snapshot ended with SnapshotResult [status=COMPLETED, offset=MySqlOffsetContext [sourceInfoSchema=Schema{io.debezium.connector.mysql.Source:STRUCT}, sourceInfo=SourceInfo [currentGtid=null, currentBinlogFilename=mysql-bin.000003, currentBinlogPosition=156, currentRowNumber=0, serverId=0, sourceTime=2021-11-30T01:38:45.352Z, threadId=-1, currentQuery=null, tableIds=[inventory.products_on_hand], databaseName=inventory], snapshotCompleted=true, transactionContext=TransactionContext [currentTransactionId=null, perTableEventCount={}, totalEventCount=0], restartGtidSet=null, currentGtidSet=null, restartBinlogFilename=mysql-bin.000003, restartBinlogPosition=156, restartRowsToSkip=0, restartEventsToSkip=0, currentEventLengthInBytes=0, inTransaction=false, transactionId=null, incrementalSnapshotContext =IncrementalSnapshotContext [windowOpened=false, chunkEndPosition=null, dataCollectionsToSnapshot=[], lastEventKeySent=null, maximumKey=null]]] [io.debezium.pipeline.ChangeEventSourceCoordinator]
...
从上述日志可以看到,执行快照的步骤如下:
第一步:准备阶段;
第二步:根据配置决定要对哪些表执行快照
第三步:对要执行快照的表加锁;
第四步:决定读取快照的偏移量;
第五步:读取要执行快照的数据表的表结构信息,用于解析
第六步:释放全局读锁,持久化第五步读取到的表结构信息
第七步:对要做快照的数据表依次做快照
最后,日志输出显示连接器已经从快照模式转变为连续读取MySQL
服务器的binlog
,即开始读取新增数据:
...
2021-11-30 01:38:45,362 INFO MySQL|dbserver1|streaming Starting streaming [io.debezium.pipeline.ChangeEventSourceCoordinator]
...
Nov 30, 2021 1:38:45 AM com.github.shyiko.mysql.binlog.BinaryLogClient connect
INFO: Connected to mysql:3306 at mysql-bin.000003/156 (sid:184054, cid:13)
2021-11-30 01:38:45,392 INFO MySQL|dbserver1|binlog Connected to MySQL binlog at mysql:3306, starting at MySqlOffsetContext [sourceInfoSchema=Schema{io.debezium.connector.mysql.Source:STRUCT}, sourceInfo=SourceInfo [currentGtid=null, currentBinlogFilename=mysql-bin.000003, currentBinlogPosition=156, currentRowNumber=0, serverId=0, sourceTime=2021-11-30T01:38:45.352Z, threadId=-1, currentQuery=null, tableIds=[inventory.products_on_hand], databaseName=inventory], snapshotCompleted=true, transactionContext=TransactionContext [currentTransactionId=null, perTableEventCount={}, totalEventCount=0], restartGtidSet=null, currentGtidSet=null, restartBinlogFilename=mysql-bin.000003, restartBinlogPosition=156, restartRowsToSkip=0, restartEventsToSkip=0, currentEventLengthInBytes=0, inTransaction=false, transactionId=null, incrementalSnapshotContext =IncrementalSnapshotContext [windowOpened=false, chunkEndPosition=null, dataCollectionsToSnapshot=[], lastEventKeySent=null, maximumKey=null]] [io.debezium.connector.mysql.MySqlStreamingChangeEventSource]
2021-11-30 01:38:45,392 INFO MySQL|dbserver1|streaming Waiting for keepalive thread to start [io.debezium.connector.mysql.MySqlStreamingChangeEventSource]
2021-11-30 01:38:45,393 INFO MySQL|dbserver1|binlog Creating thread debezium-mysqlconnector-dbserver1-binlog-client [io.debezium.util.Threads]
...
4. 数据库变更事件
当我们成功部署Debezium MySQL连接器之后,它就开始监视 inventory
数据库中的数据表,并将数据更改事件写入对应的 kafka topic
。在这个例子中,我们配置了下面这些 kafka topic
:
dbserver1
: 捕获MySQL
服务器所有的DDL
语句。dbserver1.inventory.products
: 捕获inventory.products
表的更改事件。dbserver1.inventory.products_on_hand
: 捕获inventory.products_on_hand
表的更改事件。dbserver1.inventory.customers
: 捕获inventory.customers
表的更改事件。dbserver1.inventory.orders
: 捕获inventory.orders
表的更改事件。
下面我们将修改inventory.customers
表的数据,并且在观察 dbserver1.inventory.customers
topic
。
4.1 create 事件
在inventory.customers
表插入一条数据,观察可以发现dbserver1.inventory.customers
中增加了一条消息。
4.2 update 事件
在inventory.customers
表更新一条数据,观察可以发现dbserver1.inventory.customers
中增加了一条消息。
4.3 delete 事件
在inventory.customers
表删除一条数据,观察可以发现dbserver1.inventory.customers
中增加了一条消息。
5. 资源清理
使用下面的命令停止启动的 docker 容器。
docker stop connect mysql kafka zookeeper