在現實業務中,Kafka經常會遇到的一個整合場景就是,從資料庫獲取資料,因為關聯式資料庫是一個非常豐富的事件源。資料庫中的現有資料以及對該資料的任何更改都可以流式傳輸到Kafka主題中,在這裡這些事件可用於驅動應用,也可以流式傳輸到其它資料儲存(比如搜尋引擎或者快取)用於分析等。
實現這個需求有很多種做法,但是在本文中,會聚焦其中的一個解決方案,即Kafka聯結器中的JDBC聯結器,講述如何進行配置,以及一些問題排查的技巧,至於更多的細節,請參見Kafka的文件。
介紹
Kafka聯結器中的JDBC聯結器包含在Confluent Platform中,也可以與Confluent Hub分開安裝。它可以作為源端從資料庫提取資料到Kafka,也可以作為接收端從一個Kafka主題中將資料推送到資料庫。幾乎所有關聯式資料庫都提供JDBC驅動,包括Oracle、Microsoft SQL Server、DB2、MySQL和Postgres。
下面將從最簡單的Kafka聯結器配置開始,然後進行構建。本文中的示例是從MySQL資料庫中提取資料,該資料庫有兩個模式,每個模式都有幾張表:
mysql> SELECT table_schema, table_name FROM INFORMATION_SCHEMA.tables WHERE TABLE_SCHEMA != 'information_schema';
+--------------+--------------+
| TABLE_SCHEMA | TABLE_NAME |
+--------------+--------------+
| demo | accounts |
| demo | customers |
| demo | transactions |
| security | firewall |
| security | log_events |
+--------------+--------------+
複製程式碼
JDBC驅動
在進行配置之前,要確保Kafka聯結器可以實際連線到資料庫,即確保JDBC驅動可用。如果使用的是SQLite或Postgres,那麼驅動已經包含在內,就可以跳過此步驟。對於所有其它資料庫,需要將相關的JDBC驅動JAR檔案放在和kafka-connect-jdbcJAR相同的資料夾中。此資料夾的標準位置為:
- Confluent CLI:下載的Confluent Platform資料夾中的share/java/kafka-connect-jdbc/;
- Docker,DEB / RPM安裝:/usr/share/java/kafka-connect-jdbc/,關於如何將JDBC驅動新增到Kafka聯結器的Docker容器
- 如果kafka-connect-jdbcJAR位於其它位置,則可以使用plugin.path指向包含它的資料夾,並確保JDBC驅動位於同一資料夾中。
還可以在啟動Kafka聯結器時指定CLASSPATH,設定為可以找到JDBC驅動的位置。一定要將其設定為JAR本身,而不僅僅是包含它的資料夾,例如:
CLASSPATH=/u01/jdbc-drivers/mysql-connector-java-8.0.13.JAR ./bin/connect-distributed ./etc/kafka/connect-distributed.properties
複製程式碼
兩個事情要注意一下:
- 如果kafka-connect-jdbcJAR位於其它位置,則Kafka聯結器的plugin.path選項將無法直接指向JDBC驅動JAR檔案 。根據文件,每個JDBC驅動JAR必須與kafka-connect-jdbcJAR位於同一目錄;
- 如果正在執行多節點Kafka聯結器叢集,則需要在叢集中的每個聯結器工作節點上都正確安裝JDBC驅動JAR。
找不到合適的驅動
與JDBC聯結器有關的常見錯誤是No suitable driver found,比如:
{"error_code":400,"message":"Connector configuration is invalid and contains the following 2 error(s):\nInvalid value java.sql.SQLException: No suitable driver found for jdbc:mysql://X.X.X.X:3306/test_db?user=root&password=pwd for configuration Couldn't open connection to jdbc:mysql://X.X.X.X:3306/test_db?user=root&password=pwd\nInvalid value java.sql.SQLException: No suitable driver found for jdbc:mysql://X.X.X.X:3306/test_db?user=root&password=admin for configuration Couldn't open connection to jdbc:mysql://X.X.X.X:3306/test_db?user=root&password=pwd\nYou can also find the above list of errors at the endpoint `/{connectorType}/config/validate`"}
複製程式碼
這可能有2個原因:
- 未載入正確的JDBC驅動;
- JDBC URL不正確。
確認是否已載入JDBC驅動
Kafka聯結器會載入與kafka-connect-jdbcJAR檔案在同一資料夾中的所有JDBC驅動,還有在CLASSPATH上找到的任何JDBC驅動。如果要驗證一下,可以將聯結器工作節點的日誌級別調整為DEBUG,然後會看到如下資訊:
1.DEBUG Loading plugin urls:包含kafka-connect-jdbc-5.1.0.jar(或者對應當前正在執行的版本號)的一組JAR檔案:
DEBUG Loading plugin urls: [file:/Users/Robin/cp/confluent-5.1.0/share/java/kafka-connect-jdbc/audience-annotations-0.5.0.jar, file:/Users/Robin/cp/confluent-5.1.0/share/java/kafka-connect-jdbc/common-utils-5.1.0.jar, file:/Users/Robin/cp/confluent-5.1.0/share/java/kafka-connect-jdbc/jline-0.9.94.jar, file:/Users/Robin/cp/confluent-5.1.0/share/java/kafka-connect-jdbc/jtds-1.3.1.jar, file:/Users/Robin/cp/confluent-5.1.0/share/java/kafka-connect-jdbc/kafka-connect-jdbc-5.1.0.jar, file:/Users/Robin/cp/confluent-5.1.0/share/java/kafka-connect-jdbc/mysql-connector-java-8.0.13.jar, file:/Users/Robin/cp/confluent-5.1.0/share/java/kafka-connect-jdbc/netty-3.10.6.Final.jar, file:/Users/Robin/cp/confluent-5.1.0/share/java/kafka-connect-jdbc/postgresql-9.4-1206-jdbc41.jar, file:/Users/Robin/cp/confluent-5.1.0/share/java/kafka-connect-jdbc/slf4j-api-1.7.25.jar, file:/Users/Robin/cp/confluent-5.1.0/share/java/kafka-connect-jdbc/sqlite-jdbc-3.25.2.jar, file:/Users/Robin/cp/confluent-5.1.0/share/java/kafka-connect-jdbc/zkclient-0.10.jar, file:/Users/Robin/cp/confluent-5.1.0/share/java/kafka-connect-jdbc/zookeeper-3.4.13.jar] (org.apache.kafka.connect.runtime.isolation.DelegatingClassLoader)
複製程式碼
在這個JAR列表中,應該有JDBC驅動JAR。在上面的輸出中,可以看到MySQL、Postgres和SQLite的JAR。如果期望的JDBC驅動JAR不在,可以將驅動放入kafka-connect-jdbcJAR所在的資料夾中。
2.INFO Added plugin 'io.confluent.connect.jdbc.JdbcSourceConnector':在此之後,在記錄任何其它外掛之前,可以看到JDBC驅動已註冊:
INFO Added plugin 'io.confluent.connect.jdbc.JdbcSourceConnector' (org.apache.kafka.connect.runtime.isolation.DelegatingClassLoader)
DEBUG Registered java.sql.Driver: jTDS 1.3.1 to java.sql.DriverManager (org.apache.kafka.connect.runtime.isolation.DelegatingClassLoader)
DEBUG Registered java.sql.Driver: com.mysql.cj.jdbc.Driver@7bbbb6a8 to java.sql.DriverManager (org.apache.kafka.connect.runtime.isolation.DelegatingClassLoader)
DEBUG Registered java.sql.Driver: org.postgresql.Driver@ea9e141 to java.sql.DriverManager (org.apache.kafka.connect.runtime.isolation.DelegatingClassLoader)
DEBUG Registered java.sql.Driver: org.sqlite.JDBC@236134a1 to java.sql.DriverManager (org.apache.kafka.connect.runtime.isolation.DelegatingClassLoader)
複製程式碼
確認JDBC驅動包含在已註冊的列表中。如果沒有,那麼就是安裝不正確。
注意,雖然可能會在日誌的其它地方看到驅動的Registered java.sql.Driver資訊,但如果要確認其對於JDBC聯結器可用,那麼它必須直接出現在INFO Added plugin 'io.confluent.connect.jdbc訊息的後面。
JDBC URL
- 對於源資料庫來說JDBC URL必須是正確的,如果搞錯了,那麼Kafka聯結器即使驅動正確,也是不行。以下是一些常見的JDBC URL格式:
注意,雖然JDBC URL通常允許嵌入身份驗證資訊,但這些內容將以明文形式記錄在Kafka聯結器日誌中。因此應該使用單獨的connection.user和connection.password配置項,這樣在記錄時會被合理地處理。
指定要提取的表
JDBC驅動安裝完成之後,就可以配置Kafka聯結器從資料庫中提取資料了。下面是最小的配置,不過它不一定是最有用的,因為它是資料的批量匯入,在本文後面會討論如何進行增量載入。
curl -X POST http://localhost:8083/connectors -H "Content-Type: application/json" -d '{
"name": "jdbc_source_mysql_01",
"config": {
"connector.class": "io.confluent.connect.jdbc.JdbcSourceConnector",
"connection.url": "jdbc:mysql://mysql:3306/demo",
"connection.user": "connect_user",
"connection.password": "asgard",
"topic.prefix": "mysql-01-",
"mode":"bulk"
}
}'
複製程式碼
使用此配置,每個表(使用者有權訪問)將完全複製到Kafka,通過使用KSQL列出Kafka叢集上的主題,我們可以看到:
ksql> LIST TOPICS;
Kafka Topic | Registered | Partitions | Partition Replicas | Consumers | ConsumerGroups
----------------------------------------------------------------------------------------------------
mysql-01-accounts | false | 1 | 1 | 0 | 0
mysql-01-customers | false | 1 | 1 | 0 | 0
mysql-01-firewall | false | 1 | 1 | 0 | 0
mysql-01-log_events | false | 1 | 1 | 0 | 0
mysql-01-transactions | false | 1 | 1 | 0 | 0
複製程式碼
注意mysql-01字首,表格內容的完整副本將每五秒重新整理一次,可以通過修改poll.interval.ms進行調整,例如每小時重新整理一次:
curl -X POST http://localhost:8083/connectors -H "Content-Type: application/json" -d '{
"name": "jdbc_source_mysql_02",
"config": {
"connector.class": "io.confluent.connect.jdbc.JdbcSourceConnector",
"connection.url": "jdbc:mysql://mysql:3306/demo",
"connection.user": "connect_user",
"connection.password": "asgard",
"topic.prefix": "mysql-02-",
"mode":"bulk",
"poll.interval.ms" : 3600000
}
}'
複製程式碼
找個主題確認一下,顯示完整的資料,看看是不是自己想要的:
ksql> PRINT 'mysql-02-accounts' FROM BEGINNING;
Format:AVRO
12/20/18 3:18:44 PM UTC, null, {"id": 1, "first_name": "Hamel", "last_name": "Bly", "username": "Hamel Bly", "company": "Erdman-Halvorson", "created_date": 17759}
12/20/18 3:18:44 PM UTC, null, {"id": 2, "first_name": "Scottie", "last_name": "Geerdts", "username": "Scottie Geerdts", "company": "Mante Group", "created_date": 17692}
12/20/18 3:18:44 PM UTC, null, {"id": 3, "first_name": "Giana", "last_name": "Bryce", "username": "Giana Bryce", "company": "Wiza Inc", "created_date": 17627}
12/20/18 3:18:44 PM UTC, null, {"id": 4, "first_name": "Allen", "last_name": "Rengger", "username": "Allen Rengger", "company": "Terry, Jacobson and Daugherty", "created_date": 17746}
12/20/18 3:18:44 PM UTC, null, {"id": 5, "first_name": "Reagen", "last_name": "Volkes", "username": "Reagen Volkes", "company": "Feeney and Sons", "created_date": 17798}
…
複製程式碼
目前會展示所有可用的表,這可能不是實際的需求,可能只希望包含特定模式的表,這個可以使用catalog.pattern/schema.pattern(具體哪一個取決於資料庫)配置項進行控制:
curl -X POST http://localhost:8083/connectors -H "Content-Type: application/json" -d '{
"name": "jdbc_source_mysql_03",
"config": {
"connector.class": "io.confluent.connect.jdbc.JdbcSourceConnector",
"connection.url": "jdbc:mysql://mysql:3306/demo",
"connection.user": "connect_user",
"connection.password": "asgard",
"topic.prefix": "mysql-03-",
"mode":"bulk",
"poll.interval.ms" : 3600000,
"catalog.pattern" : "demo"
}
}'
複製程式碼
這樣就只會從demo模式中取得3張表:
ksql> LIST TOPICS;
Kafka Topic | Registered | Partitions | Partition Replicas | Consumers | ConsumerGroups
----------------------------------------------------------------------------------------------------
[…]
mysql-03-accounts | false | 1 | 1 | 0 | 0
mysql-03-customers | false | 1 | 1 | 0 | 0
mysql-03-transactions | false | 1 | 1 | 0 | 0
[…]
複製程式碼
也可以使用table.whitelist(白名單)或table.blacklist(黑名單)來控制聯結器提取的表,下面的示例顯式地列出了希望拉取到Kafka中的表清單:
curl -X POST http://localhost:8083/connectors -H "Content-Type: application/json" -d '{
"name": "jdbc_source_mysql_04",
"config": {
"connector.class": "io.confluent.connect.jdbc.JdbcSourceConnector",
"connection.url": "jdbc:mysql://mysql:3306/demo",
"connection.user": "connect_user",
"connection.password": "asgard",
"topic.prefix": "mysql-04-",
"mode":"bulk",
"poll.interval.ms" : 3600000,
"catalog.pattern" : "demo",
"table.whitelist" : "accounts"
}
}'
複製程式碼
這時就只有一個表從資料庫流式傳輸到Kafka:
ksql> LIST TOPICS;
Kafka Topic | Registered | Partitions | Partition Replicas | Consumers | ConsumerGroups
----------------------------------------------------------------------------------------------------
mysql-04-accounts | false | 1 | 1 | 0 | 0
複製程式碼
因為只有一個表,下面的配置:
"catalog.pattern" : "demo",
"table.whitelist" : "accounts",
複製程式碼
等同於:
"table.whitelist" : "demo.accounts",
複製程式碼
也可以在一個模式中指定多個表,比如:
"catalog.pattern" : "demo",
"table.whitelist" : "accounts, customers",
複製程式碼
或者也可以跨越多個模式:
"table.whitelist" : "demo.accounts, security.firewall",
複製程式碼
還可以使用其它的表過濾選項,比如table.types可以選擇表之外的物件,例如檢視。
過濾表時要注意,因為如果最終沒有物件匹配該模式(或者連線到資料庫的已認證使用者沒有許可權訪問),那麼聯結器將報錯:
INFO After filtering the tables are: (io.confluent.connect.jdbc.source.TableMonitorThread)
…
ERROR Failed to reconfigure connector's tasks, retrying after backoff: (org.apache.kafka.connect.runtime.distributed.DistributedHerder)
java.lang.IllegalArgumentException: Number of groups must be positive
複製程式碼
在通過table.whitelist/table.blacklist進行過濾之前,可以將日誌級別調整為DEBUG,檢視使用者可以訪問的表清單:
DEBUG Got the following tables: ["demo"."accounts", "demo"."customers"] (io.confluent.connect.jdbc.source.TableMonitorThread)
複製程式碼
然後,聯結器會根據提供的白名單/黑名單過濾此列表,因此要確認指定的列表位於聯結器可用的列表中,還要注意連線使用者要有許可權訪問這些表,因此還要檢查資料庫端的GRANT語句。
增量提取
到目前為止,已經按計劃將整張表都拉取到Kafka,這雖然對於轉存資料非常有用,不過都是批量並且並不總是適合將源資料庫整合到Kafka流系統中。
JDBC聯結器還有一個流式傳輸到Kafka的選項,它只會傳輸上次拉取後的資料變更,具體可以基於自增列(例如自增主鍵)和/或時間戳(例如最後更新時間戳)來執行此操作。在模式設計中的常見做法是使用這些中的一個或兩個,例如,事務表ORDERS可能有:
- ORDER_ID:一個唯一鍵(可能是主鍵),每個新訂單遞增;
- UPDATE_TS:每次資料變更時更新的時間戳列。
可以使用mode引數配置該選項,比如使用timestamp:
curl -X POST http://localhost:8083/connectors -H "Content-Type: application/json" -d '{
"name": "jdbc_source_mysql_08",
"config": {
"connector.class": "io.confluent.connect.jdbc.JdbcSourceConnector",
"connection.url": "jdbc:mysql://mysql:3306/demo",
"connection.user": "connect_user",
"connection.password": "asgard",
"topic.prefix": "mysql-08-",
"mode":"timestamp",
"table.whitelist" : "demo.accounts",
"timestamp.column.name": "UPDATE_TS",
"validate.non.null": false
}
}'
複製程式碼
下面會獲取表的全部資料,外加源資料後續的更新和插入:
注意:
- 可以結合使用這些方法中的(時間戳/自增)或兩者(時間戳+自增);
- 要使用的時間戳和/或自增列必須在聯結器處理的所有表上。如果不同的表具有不同名稱的時間戳/自增列,則需要建立單獨的聯結器配置;
- 如果只使用自增列,則不會捕獲對資料的更新,除非每次更新時自增列也會增加(在主鍵的情況下幾乎不可能);
- 某些表可能沒有唯一的標識,或者有多個組合的列表示行的唯一標識(聯合主鍵),不過JDBC聯結器只支援單個標識列;
- 時間戳+自增列選項為識別新行和更新行提供了最大的覆蓋範圍;
- 許多RDBMS支援宣告更新時間戳列的DDL,該列會自動更新。例如:
- MySQL:
CREATE TABLE foo (
…
UPDATE_TS TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
);
複製程式碼
- Postgres:
CREATE TABLE foo (
…
UPDATE_TS TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
-- Courtesy of https://techblog.covermymeds.com/databases/on-update-timestamps-mysql-vs-postgres/
CREATE FUNCTION update_updated_at_column() RETURNS trigger
LANGUAGE plpgsql
AS $$
BEGIN
NEW.update_ts = NOW();
RETURN NEW;
END;
$$;
CREATE TRIGGER t1_updated_at_modtime BEFORE UPDATE ON foo FOR EACH ROW EXECUTE PROCEDURE update_updated_at_column();
複製程式碼
- Oracle:
CREATE TABLE foo (
…
CREATE_TS TIMESTAMP DEFAULT CURRENT_TIMESTAMP ,
);
CREATE OR REPLACE TRIGGER TRG_foo_UPD
BEFORE INSERT OR UPDATE ON foo
REFERENCING NEW AS NEW_ROW
FOR EACH ROW
BEGIN
SELECT SYSDATE
INTO :NEW_ROW.UPDATE_TS
FROM DUAL;
END;
/
複製程式碼
基於查詢的提取
有時可能想從RDBMS中提取資料,但希望有比整個表更靈活的方式,原因可能包括:
- 一個有許多列的寬表,但是隻希望有部分列被傳輸到Kafka主題上;
- 表中包含敏感資訊,不希望這些資訊傳輸到Kafka主題上(儘管也可以提取時在Kafka聯結器中使用單訊息轉換進行處理);
- 多個表之間存在依賴關係,因此在傳輸到Kafka之前,可能希望將其解析為一個單一的一致性檢視。
這可以使用JDBC聯結器的query模式。在瞭解如何實現之前,需要注意以下幾點:
- 謹防管道的“過早優化”,僅僅因為不需要源表中的某些列或行,而不是說在流式傳輸到Kafka時不應包含它們;
- 正如將在下面看到的,當涉及增量攝取時,query模式可能不那麼靈活,因此從源中簡單地刪除列的另一種方法(無論是簡單地減少數量,還是因為敏感資訊)都是在聯結器本身中使用ReplaceField單訊息轉換;
- 隨著查詢越來越複雜(例如解析關聯),潛在的壓力和對源資料庫的影響會增加;
- 在RDBMS(作為源頭)中關聯資料是解決關聯的一種方法,另一種方法是將源表流式傳輸到單獨的Kafka主題,然後使用KSQL或Kafka Streams根據需求進行關聯(過濾和標記資料也是如此),KSQL是在Kafka中對資料進行後處理的絕佳方式,使管道盡可能簡單。
下面將展示如何將transactions表,再加上customers表中的資料流式傳輸到Kafka:
curl -X POST http://localhost:8083/connectors -H "Content-Type: application/json" -d '{
"name": "jdbc_source_mysql_09",
"config": {
"connector.class": "io.confluent.connect.jdbc.JdbcSourceConnector",
"connection.url": "jdbc:mysql://mysql:3306/demo",
"connection.user": "connect_user",
"connection.password": "asgard",
"topic.prefix": "mysql-09",
"mode":"bulk",
"query":"SELECT t.txn_id, t.customer_id, t.amount, t.currency, t.txn_timestamp, c.first_name, c.last_name, c.email, c.gender, c.comments FROM demo.transactions t LEFT OUTER JOIN demo.customers c on t.customer_id = c.id;",
"poll.interval.ms" : 3600000
}
}'
複製程式碼
可能注意到已切換回bulk模式,可以使用主鍵或者時間戳其中一個增量選項,但要確保在SELECT子句中包含相應的主鍵/時間戳列(例如txn_id):
curl -X POST http://localhost:8083/connectors -H "Content-Type: application/json" -d '{
"name": "jdbc_source_mysql_10",
"config": {
"connector.class": "io.confluent.connect.jdbc.JdbcSourceConnector",
"connection.url": "jdbc:mysql://mysql:3306/demo",
"connection.user": "connect_user",
"connection.password": "asgard",
"topic.prefix": "mysql-10",
"mode":"incrementing",
"query":"SELECT txn_id, t.customer_id, t.amount, t.currency, t.txn_timestamp, c.first_name, c.last_name, c.email, c.gender, c.comments FROM demo.transactions t LEFT OUTER JOIN demo.customers c on t.customer_id = c.id",
"incrementing.column.name": "txn_id",
"validate.non.null": false
}
}'
複製程式碼
如果不包括該列(即使它存在於源表中),那麼聯結器會報錯並顯示org.apache.kafka.connect.errors.DataException異常(#561)或java.lang.NullPointerException異常(#560),這是因為聯結器需要在返回的資料中獲取值,以便可以儲存相應偏移量的最新值。
如果使用query選項,除非使用mode: bulk(#566),否則無法指定自己的WHERE子句,也就是說,在查詢中使用自己的謂詞和使用Kafka進行增量提取之間是互斥的。
一個還是多個聯結器?
如果需要不同的引數設定,可以建立新的聯結器,例如,可能希望有不同的引數:
- 包含自增主鍵和/或時間戳的列的名稱;
- 輪詢表的頻率;
- 連線資料庫的使用者不同。
簡單來說,如果所有表引數都一樣,則可以使用單個聯結器。
為什麼沒有資料?
建立聯結器之後,可能在目標Kafka主題中看不到任何資料。下面會一步步進行診斷:
1.查詢/connectors端點,可確認聯結器是否建立成功:
$ curl -s“http:// localhost:8083 / connectors”
[ “jdbc_source_mysql_10”]
複製程式碼
應該看到聯結器列表,如果沒有,則需要按照之前的步驟進行建立,然後關注Kafka聯結器返回的任何錯誤。
2.檢查聯結器及其任務的狀態:
$ curl -s "http://localhost:8083/connectors/jdbc_source_mysql_10/status"|jq '.'
{
"name": "jdbc_source_mysql_10",
"connector": {
"state": "RUNNING",
"worker_id": "kafka-connect:8083"
},
"tasks": [
{
"state": "RUNNING",
"id": 0,
"worker_id": "kafka-connect:8083"
}
],
"type": "source"
}
複製程式碼
正常應該看到所有的聯結器和任務的state都是RUNNING,不過RUNNING不總是意味著正常。
3.如果聯結器或任務的狀態是FAILED,或者即使狀態是RUNNING但是沒有按照預期行為執行,那麼可以轉到Kafka聯結器工作節點的輸出(這裡有相關的說明),這裡會顯示是否存在任何實際的問題。以上面的聯結器為例,其狀態為RUNNING,但是聯結器工作節點日誌中實際上全是重複的錯誤:
ERROR Failed to run query for table TimestampIncrementingTableQuerier{table=null, query='SELECT t.id, t.customer_id, t.amount, t.currency, t.txn_timestamp, c.first_name, c.last_name, c.email, c.gender, c.comments FROM demo.transactions t LEFT OUTER JOIN demo.customers c on t.customer_id = c.id;', topicPrefix='mysql-10', incrementingColumn='t.id', timestampColumns=[]}: {} (io.confluent.connect.jdbc.source.JdbcSourceTask)
java.sql.SQLSyntaxErrorException: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'WHERE `t.id` > -1 ORDER BY `t.id` ASC' at line 1
at com.mysql.cj.jdbc.exceptions.SQLError.createSQLException(SQLError.java:120)
複製程式碼
4.在這裡,問題是什麼並不明確,需要調出聯結器的配置來檢查指定的查詢是否正確:
$ curl -s "http://localhost:8083/connectors/jdbc_source_mysql_10/config"|jq '.'
{
"connector.class": "io.confluent.connect.jdbc.JdbcSourceConnector",
"mode": "incrementing",
"incrementing.column.name": "t.id",
"topic.prefix": "mysql-10",
"connection.password": "asgard",
"validate.non.null": "false",
"connection.user": "connect_user",
"query": "SELECT t.id, t.customer_id, t.amount, t.currency, t.txn_timestamp, c.first_name, c.last_name, c.email, c.gender, c.comments FROM demo.transactions t LEFT OUTER JOIN demo.customers c on t.customer_id = c.id;",
"name": "jdbc_source_mysql_10",
"connection.url": "jdbc:mysql://mysql:3306/demo"
}
複製程式碼
5.在MySQL中執行此查詢發現能正常執行:
mysql> SELECT t.id, t.customer_id, t.amount, t.currency, t.txn_timestamp, c.first_name, c.last_name, c.email, c.gender, c.comments FROM demo.transactions t LEFT OUTER JOIN demo.customers c on t.customer_id = c.id;
+------+-------------+--------+----------+----------------------+------------+-----------+----------------------------+--------+------------------------------------------------------+
| id | customer_id | amount | currency | txn_timestamp | first_name | last_name | email | gender | comments |
+------+-------------+--------+----------+----------------------+------------+-----------+----------------------------+--------+------------------------------------------------------+
| 1 | 5 | -72.97 | RUB | 2018-12-12T13:58:37Z | Modestia | Coltart | mcoltart4@scribd.com | Female | Reverse-engineered non-volatile success
複製程式碼
6.所以肯定是Kafka聯結器在執行時做了什麼。鑑於錯誤訊息引用t.id,這是在incrementing.column.name引數中指定的,可能問題與此有關。通過將Kafka聯結器的日誌級別調整為DEBUG,可以看到執行的完整SQL語句:
DEBUG TimestampIncrementingTableQuerier{table=null, query='SELECT t.id, t.customer_id, t.amount, t.currency, t.txn_timestamp, c.first_name, c.last_name, c.email, c.gender, c.comments FROM demo.transactions t LEFT OUTER JOIN demo.customers c on t.customer_id = c.id;', topicPrefix='mysql-10', incrementingColumn='t.id', timestampColumns=[]} prepared SQL query: SELECT t.id, t.customer_id, t.amount, t.currency, t.txn_timestamp, c.first_name, c.last_name, c.email, c.gender, c.comments FROM demo.transactions t LEFT OUTER JOIN demo.customers c on t.customer_id = c.id; WHERE `t.id` > ? ORDER BY `t.id` ASC (io.confluent.connect.jdbc.source.TimestampIncrementingTableQuerier)
複製程式碼
7.看一下該prepared SQL query部分,可能會發現:
[…] FROM demo.transactions t LEFT OUTER JOIN demo.customers c on t.customer_id = c.id; WHERE `t.id` > ? ORDER BY `t.id` ASC
複製程式碼
8.注意在JOIN子句的c.id後面有語句終止符(;),後面有WHERE子句。該WHERE子句由Kafka聯結器附加,用於實現所要求的incrementing模式,但建立了一個無效的SQL語句; 9.然後在GitHub中查詢與看到的錯誤相關的問題,因為有時它實際上是一個已知的問題,例如這個問題; 10.如果聯結器存在並且是RUNNING,並且Kafka聯結器工作節點日誌中也沒有錯誤,還應該檢查:
- 聯結器的提取間隔是多少?也許它完全按照配置執行,並且源表中的資料已經更改,但就是沒有拉取到新資料。要檢查這一點,可以在Kafka聯結器工作節點的輸出中查詢`JdbcSourceTaskConfig`的值和`poll.interval.ms`的值;
- 如果正在使用的是增量攝取,Kafka聯結器關於偏移量是如何儲存的?如果刪除並重建相同名稱的聯結器,則將保留前一個例項的偏移量。考慮這樣的場景,建立完聯結器之後,成功地將所有資料提取到源表中的給定主鍵或時間戳值,然後刪除並重新建立了它,新版本的聯結器將獲得之前版本的偏移量,因此僅提取比先前處理的資料更新的資料,具體可以通過檢視儲存在其中的`offset.storage.topic`值和相關表來驗證這一點。
複製程式碼
重置JDBC源聯結器讀取資料的點
當Kafka聯結器以分散式模式執行時,它會在Kafka主題(通過offset.storage.topic配置)中儲存有關它在源系統中讀取的位置(稱為偏移量)的資訊,當聯結器任務重啟時,它可以從之前的位置繼續進行處理,具體可以在聯結器工作節點日誌中看到:
INFO Found offset {{protocol=1, table=demo.accounts}={timestamp_nanos=0, timestamp=1547030056000}, {table=accounts}=null} for partition {protocol=1, table=demo.accounts} (io.confluent.connect.jdbc.source.JdbcSourceTask)
複製程式碼
每次聯結器輪詢時,都會使用這個偏移量,它會使用預編譯的SQL語句,並且使用Kafka聯結器任務傳遞的值替換?佔位符:
DEBUG TimestampIncrementingTableQuerier{table="demo"."accounts", query='null', topicPrefix='mysql-08-', incrementingColumn='', timestampColumns=[UPDATE_TS]} prepared SQL query: SELECT * FROM `demo`.`accounts` WHERE `demo`.`accounts`.`UPDATE_TS` > ? AND `demo`.`accounts`.`UPDATE_TS` < ? ORDER BY `demo`.`accounts`.`UPDATE_TS` ASC (io.confluent.connect.jdbc.source.TimestampIncrementingTableQuerier)
DEBUG Executing prepared statement with timestamp value = 2019-01-09 10:34:16.000 end time = 2019-01-09 13:23:40.000 (io.confluent.connect.jdbc.source.TimestampIncrementingCriteria)
複製程式碼
這裡,第一個時間戳值就是儲存的偏移量,第二個時間戳值是當前時間戳。
雖然沒有文件記載,但可以手動更改聯結器使用的偏移量,因為是在JDBC源聯結器的上下文中,所以可以跨多個源聯結器型別,這意味著更改時間戳或主鍵,聯結器會將後續記錄視為未處理的狀態。
首先要做的是確保Kafka聯結器已經重新整理了週期性的偏移量,可以在工作節點日誌中看到何時執行此操作:
INFO WorkerSourceTask{id=jdbc_source_mysql_08-0} Committing offsets (org.apache.kafka.connect.runtime.WorkerSourceTask)
複製程式碼
看下Kafka的主題,可以看到Kafka聯結器建立的內部主題,並且負責偏移量的主題也是其中之一,名字可能有所不同:
ksql> LIST TOPICS;
Kafka Topic | Registered | Partitions | Partition Replicas | Consumers | ConsumerGroups
----------------------------------------------------------------------------------------------------
docker-connect-configs | false | 1 | 1 | 0 | 0
docker-connect-offsets | false | 1 | 1 | 0 | 0
docker-connect-status | false | 5 | 1 | 0 | 0
ksql> PRINT 'docker-connect-offsets' FROM BEGINNING;
Format:JSON
{"ROWTIME":1547038346644,"ROWKEY":"[\"jdbc_source_mysql_08\",{\"protocol\":\"1\",\"table\":\"demo.customers\"}]","timestamp_nanos":0,"timestamp":1547030057000}
複製程式碼
當Kafka聯結器任務啟動時,它會讀取此主題並使用適當主鍵的最新值。要更改偏移量,只需插入一個新值即可。最簡單的方法是轉存當前主題內容,修改內容並重新執行,因為一致性和簡單,可以考慮使用kafkacat:
- 轉存當前的內容:
$ kafkacat -b kafka:29092 -t docker-connect-offsets -C -K# -o-1
% Reached end of topic docker-connect-offsets [0] at offset 0
["jdbc_source_mysql_08",{"protocol":"1","table":"demo.accounts"}]#{"timestamp_nanos":0,"timestamp":1547030056000}
);
複製程式碼
如果是多個聯結器,可能複雜些,但是這裡只有一個,所以使用了-o-1標誌,它定義了返回的偏移量。
- 根據需要修改偏移量。在這裡使用了mode=timestamp來監測表中的變化。時間戳值是1547030056000,使用相關的時間戳轉換之類的工具,可以很容易地轉換和操作,比如將其提前一小時(1547026456000)。接下來,使用更新後的timestamp值準備新訊息:
["jdbc_source_mysql_08",{"protocol":"1","table":"demo.accounts"}]#{"timestamp_nanos":0,"timestamp":1547026456000}
複製程式碼
- 將新訊息發給主題:
echo '["jdbc_source_mysql_08",{"protocol":"1","table":"demo.accounts"}]#{"timestamp_nanos":0,"timestamp":1547026456000}' | \
kafkacat -b kafka:29092 -t docker-connect-offsets -P -Z -K#
複製程式碼
- 如果要從頭開始重啟聯結器,可以傳送NULL訊息值:
echo'[“jdbc_source_mysql_08”,{“protocol”:“1”,“table”:“demo.accounts”}]#'| \
kafkacat -b kafka:29092 -t docker-connect-offsets -P -Z -K#
複製程式碼
- 重啟聯結器任務:
curl -i -X POST -H "Accept:application/json" \
-H "Content-Type:application/json" http://localhost:8083/connectors/jdbc_source_mysql_08/tasks/0/restart
複製程式碼
- 也可以只重啟Kafka聯結器工作節點,重啟之後,資料來源中所有比新設定的偏移量更新的記錄,都會被重新提取到Kafka主題中。
從指定的時間戳或者主鍵處開啟表的捕獲
當使用時間戳或自增主鍵模式建立JDBC源聯結器時,它會從主鍵為-1和/或時間戳為1970-01-01 00:00:00.00開始,這意味著會獲得表的全部內容,然後在後續的輪詢中獲取任何插入/更新的資料。
但是如果不想要表的完整副本,只是希望聯結器從現在開始,該怎麼辦呢?這在目前的Kafka聯結器中還不支援,但可以使用前述的方法。不需要獲取現有的偏移量訊息並對其進行定製,而是自己建立。訊息的格式依賴於正在使用的聯結器和表的名稱,一種做法是先建立聯結器,確定格式,然後刪除聯結器,另一種做法是使用具有相同源表名和結構的環境,除非在該環境中沒有可供聯結器提取的資料,否則同樣也能得到所需的訊息格式。
在建立聯結器之前,使用適當的值配置偏移量主題。在這裡,希望從demo.transactions表中提取自增主鍵大於42的所有行:
echo '["jdbc_source_mysql_20",{"protocol":"1","table":"demo.transactions"}]#{"incrementing":42}' | \
kafkacat -b kafka:29092 -t docker-connect-offsets -P -Z -K#
複製程式碼
下面建立聯結器:
curl -X POST http://localhost:8083/connectors -H "Content-Type: application/json" -d '{
"name": "jdbc_source_mysql_20",
"config": {
"connector.class": "io.confluent.connect.jdbc.JdbcSourceConnector",
"connection.url": "jdbc:mysql://mysql:3306/demo",
"connection.user": "connect_user",
"connection.password": "asgard",
"topic.prefix": "mysql-20-",
"mode":"incrementing",
"table.whitelist" : "demo.transactions",
"incrementing.column.name": "txn_id",
"validate.non.null": false
}
}'
複製程式碼
在生成的Kafka聯結器工作日誌中,可以看到:
INFO Found offset {{protocol=1, table=demo.transactions}={incrementing=42}, {table=transactions}=null} for partition {protocol=1, table=demo.transactions} (io.confluent.connect.jdbc.source.JdbcSourceTask)
…
DEBUG Executing prepared statement with incrementing value = 42 (io.confluent.connect.jdbc.source.TimestampIncrementingCriteria)
複製程式碼
和預期一樣,Kafka主題中只注入了txn_id大於42的行:
ksql> PRINT 'mysql-20x-transactions' FROM BEGINNING;
Format:AVRO
1/9/19 1:44:07 PM UTC, null, {"txn_id": 43, "customer_id": 3, "amount": {"bytes": "ús"}, "currency": "CNY", "txn_timestamp": "2018-12-15T08:23:24Z"}
1/9/19 1:44:07 PM UTC, null, {"txn_id": 44, "customer_id": 5, "amount": {"bytes": "\f!"}, "currency": "CZK", "txn_timestamp": "2018-10-04T13:10:17Z"}
1/9/19 1:44:07 PM UTC, null, {"txn_id": 45, "customer_id": 3, "amount": {"bytes": "çò"}, "currency": "USD", "txn_timestamp": "2018-04-03T03:40:49Z"}
複製程式碼
配置Kafka訊息鍵
Kafka訊息是鍵/值對,其中值是有效內容。在JDBC聯結器的上下文中,值是要被提取的錶行的內容。Kafka訊息中的鍵對於分割槽和下游處理非常重要,其中任何關聯(比如KSQL)都將在資料中完成。
JDBC聯結器預設不設定訊息鍵,但是使用Kafka聯結器的單訊息轉換(SMT)機制可以輕鬆實現。假設想要提取accounts表並將其ID列用作訊息鍵。只需簡單地將其新增到下面的配置中即可:
curl -X POST http://localhost:8083/connectors -H "Content-Type: application/json" -d '{
"name": "jdbc_source_mysql_06",
"config": {
"connector.class": "io.confluent.connect.jdbc.JdbcSourceConnector",
"connection.url": "jdbc:mysql://mysql:3306/demo",
"connection.user": "connect_user",
"connection.password": "asgard",
"topic.prefix": "mysql-06-",
"poll.interval.ms" : 3600000,
"table.whitelist" : "demo.accounts",
"mode":"bulk",
"transforms":"createKey,extractInt",
"transforms.createKey.type":"org.apache.kafka.connect.transforms.ValueToKey",
"transforms.createKey.fields":"id",
"transforms.extractInt.type":"org.apache.kafka.connect.transforms.ExtractField$Key",
"transforms.extractInt.field":"id"
}
}'
複製程式碼
這時如果使用諸如kafka-avro-console-consumer之類的工具檢查資料,就會看到鍵(JSON內容之前的最左列)與id值匹配:
kafka-avro-console-consumer \
--bootstrap-server kafka:29092 \
--property schema.registry.url=http://schema-registry:8081 \
--topic mysql-06-accounts --from-beginning --property print.key=true
1 {"id":{"int":1},"first_name":{"string":"Hamel"},"last_name":{"string":"Bly"},"username":{"string":"Hamel Bly"},"company":{"string":"Erdman-Halvorson"},"created_date":{"int":17759}}
2 {"id":{"int":2},"first_name":{"string":"Scottie"},"last_name":{"string":"Geerdts"},"username":{"string":"Scottie Geerdts"},"company":{"string":"Mante Group"},"created_date":{"int":17692}}
複製程式碼
如果要在資料中設定鍵以便與KSQL一起使用,則需要將其建立為字串型別,因為KSQL目前不支援其它鍵型別,具體可以在聯結器配置中新增如下內容:
"key.converter": "org.apache.kafka.connect.storage.StringConverter"
複製程式碼
然後就可以在KSQL中使用了:
ksql> CREATE STREAM ACCOUNTS WITH (KAFKA_TOPIC='mysql-06X-accounts', VALUE_FORMAT='AVRO');
ksql> SELECT ROWKEY, ID, FIRST_NAME + ' ' + LAST_NAME FROM ACCOUNTS;
1 | 1 | Hamel Bly
2 | 2 | Scottie Geerdts
3 | 3 | Giana Bryce
複製程式碼
更改主題名稱
JDBC聯結器要求指定topic.prefix,但如果不想要,或者想將主題名更改為其它模式,SMT可以實現。
假設要刪除mysql-07-字首,那麼需要一點正規表示式的技巧:
curl -X POST http://localhost:8083/connectors -H "Content-Type: application/json" -d '{
"name": "jdbc_source_mysql_07",
"config": {
"connector.class": "io.confluent.connect.jdbc.JdbcSourceConnector",
"connection.url": "jdbc:mysql://mysql:3306/demo",
"connection.user": "connect_user",
"connection.password": "asgard",
"topic.prefix": "mysql-07-",
"poll.interval.ms" : 3600000,
"catalog.pattern" : "demo",
"table.whitelist" : "accounts",
"mode":"bulk",
"transforms":"dropTopicPrefix",
"transforms.dropTopicPrefix.type":"org.apache.kafka.connect.transforms.RegexRouter",
"transforms.dropTopicPrefix.regex":"mysql-07-(.*)",
"transforms.dropTopicPrefix.replacement":"$1"
}
}'
複製程式碼
這樣主題名就和表名一致了:
ksql> LIST TOPICS;
Kafka Topic | Registered | Partitions | Partition Replicas | Consumers | ConsumerGroups
----------------------------------------------------------------------------------------------------
accounts | false | 1 | 1 | 0 | 0
複製程式碼
Bytes, Decimals, Numerics和自定義型別
這是話題比較深入。
- numeric.mapping: best_fit如果源中包含NUMERIC/NUMBER型別的資料,則可能需要這個配置項;
- 如果需要,可以在JDBC聯結器中使用query選項,用於對源表中的資料進行轉換;
- 如果欄位以JDBCDECIMAL型別暴露,則numeric.mapping無法處理:
- MySQL將所有數值儲存為DECIMAL;
- SQL Server將DECIMAL和NUMERIC原生儲存,因此必須將DECIMAL欄位轉換為NUMERIC;
- 在Oracle中,要在NUMBER欄位中指定長度和標度,例如NUMBER(5,0),不能是NUMBER;
- NUMERIC和DECIMAL都被視為NUMBER,INT也是;
完成之後,下面會做一個解釋:
Kafka聯結器是一個可以將資料注入Kafka、與特定源技術無關的框架。無論是來自SQL Server、DB2、MQTT、文字檔案、REST還是Kafka聯結器支援的任何其它數十種來源,它傳送給Kafka的資料格式都為Avro或JSON,這通常是一個透明的過程,只是在處理數值資料型別時有些特別,比如DECIMAL,NUMBER等等,以下面的MySQL查詢為例:
mysql> SELECT * FROM transactions LIMIT 1;
+--------+-------------+--------+----------+----------------------+
| txn_id | customer_id | amount | currency | txn_timestamp |
+--------+-------------+--------+----------+----------------------+
| 1 | 5 | -72.97 | RUB | 2018-12-12T13:58:37Z |
+--------+-------------+--------+----------+----------------------+
複製程式碼
挺正常是吧?其實,amount列是DECIMAL(5,2):
mysql> describe transactions;
+---------------+--------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+---------------+--------------+------+-----+---------+-------+
| txn_id | int(11) | YES | | NULL | |
| customer_id | int(11) | YES | | NULL | |
| amount | decimal(5,2) | YES | | NULL | |
| currency | varchar(50) | YES | | NULL | |
| txn_timestamp | varchar(50) | YES | | NULL | |
+---------------+--------------+------+-----+---------+-------+
5 rows in set (0.00 sec)
複製程式碼
但是當使用JDBC聯結器的預設設定提取到Kafka中時,最終會是這樣:
ksql> PRINT 'mysql-02-transactions' FROM BEGINNING;
Format:AVRO
1/4/19 5:38:45 PM UTC, null, {"txn_id": 1, "customer_id": 5, "amount": {"bytes": "ã\u007F"}, "currency": "RUB", "txn_timestamp": "2018-12-12T13:58:37Z"}
複製程式碼
DECIMAL變成了一個看似亂碼的bytes值,聯結器預設會使用自己的DECIMAL邏輯型別,該型別在Avro中被序列化為位元組,可以通過檢視Confluent Schema Registry中的相關條目來看到這一點:
$ curl -s "http://localhost:8081/subjects/mysql-02-transactions-value/versions/1"|jq '.schema|fromjson.fields[] | select (.name == "amount")'
{
"name": "amount",
"type": [
"null",
{
"type": "bytes",
"scale": 2,
"precision": 64,
"connect.version": 1,
"connect.parameters": {
"scale": "2"
},
"connect.name": "org.apache.kafka.connect.data.Decimal",
"logicalType": "decimal"
}
],
"default": null
}
複製程式碼
當聯結器使用AvroConverter消費時,這會正常處理並儲存為DECIMAL(並且在Java中也可以反序列化為BigDecimal),但對於反序列化Avro的其它消費者,它們只會得到位元組。在使用啟用了模式的JSON時,也會看到這一點,amount值會是Base64編碼的位元組字串:
{
"schema": {
"type": "struct",
"fields": [
{
"type": "bytes",
"optional": true,
"name": "org.apache.kafka.connect.data.Decimal",
"version": 1,
"parameters": {
"scale": "2"
},
"field": "amount"
},
},
"payload": {
"txn_id": 1000,
"customer_id": 5,
"amount": "Cv8="
}
}
複製程式碼
因此,不管使用的是JSON還是Avro,這都是numeric.mapping配置項的來源。它預設設定為none(即使用聯結器的DECIMAL型別),但通常希望聯結器將型別實際轉換為更相容的型別,以適合數字的精度,更具體的說明,可以參見相關的文件。
此選專案前不支援DECIMAL型別,因此這裡是在Postgres中具有NUMERIC型別的相同原理的示例:
curl -X POST http://localhost:8083/connectors -H "Content-Type: application/json" -d '{
"name": "jdbc_source_postgres_12",
"config": {
"connector.class": "io.confluent.connect.jdbc.JdbcSourceConnector",
"connection.url": "jdbc:postgresql://postgres:5432/postgres",
"connection.user": "connect_user",
"connection.password": "asgard",
"topic.prefix": "postgres-12-",
"numeric.mapping": "best_fit",
"table.whitelist" : "demo.transactions",
"mode":"bulk",
"poll.interval.ms" : 3600000
}
}'
複製程式碼
結果如下所示:
ksql> PRINT 'postgres-12-transactions' FROM BEGINNING;
Format:AVRO
1/7/19 6:27:16 PM UTC, null, {"txn_id": 1, "customer_id": 5, "amount": -72.97, "currency": "RUB", "txn_timestamp": "2018-12-12T13:58:37Z"}
複製程式碼
可以在這裡看到有關此內容的更多詳細資訊,以及Postgres、Oracle和MS SQL Server中的示例。
處理多個表
如果需要從多個表中提取資料,則可以通過並行處理來減少總提取時間,這在Kafka的JDBC聯結器有兩種方法:
- 定義多個聯結器,每個聯結器都處理單獨的表;
- 定義單個聯結器,但增加任務數。每個Kafka聯結器的工作由一個或多個任務來執行,每個聯結器預設只有一個任務,這意味著從資料庫中提取資料是單程式處理的。
前者具有更高的管理開銷,但確實提供了每個表自定義設定的靈活性。如果可以使用相同的聯結器配置提取所有表,則增加單個聯結器中的任務數是一種好方法。
當增加從資料庫中提取資料的併發性時,要從整體上考慮。因為執行一百個併發任務雖然可能會更快,但數百個與資料庫的連線可能會對資料庫產生負面影響。
以下是同一聯結器的兩個示例。兩者都將從資料庫中提取所有表,總共6個。在第一個聯結器中,未指定最大任務數,因此為預設值1。在第二個中,指定了最多執行三個任務("tasks.max":3):
curl -X POST http://localhost:8083/connectors -H "Content-Type: application/json" -d '{
"name": "jdbc_source_mysql_01",
"config": {
"connector.class": "io.confluent.connect.jdbc.JdbcSourceConnector",
"connection.url": "jdbc:mysql://mysql:3306/demo",
"connection.user": "connect_user",
"connection.password": "asgard",
"topic.prefix": "mysql-01-",
"mode":"bulk"
}
}'
curl -X POST http://localhost:8083/connectors -H "Content-Type: application/json" -d '{
"name": "jdbc_source_mysql_11",
"config": {
"connector.class": "io.confluent.connect.jdbc.JdbcSourceConnector",
"connection.url": "jdbc:mysql://mysql:3306/demo",
"connection.user": "connect_user",
"connection.password": "asgard",
"topic.prefix": "mysql-11-",
"mode":"bulk",
"tasks.max":3
}
}'
複製程式碼
當查詢聯結器的Kafka聯結器RESTAPI時,可以看到每個聯結器正在執行的任務數以及它們已分配的表。第一個聯結器有一個任務負責所有6張表:
$ curl -s "http://localhost:8083/connectors/jdbc_source_mysql_01/tasks"|jq '.'
[
{
"id": {
"connector": "jdbc_source_mysql_01",
"task": 0
},
"config": {
"tables": "`demo`.`NUM_TEST`,`demo`.`accounts`,`demo`.`customers`,`demo`.`transactions`,`security`.`firewall`,`security`.`log_events`",
…
}
}
]
複製程式碼
第二個聯結器有3個任務,每個任務分配2張表:
$ curl -s“http:// localhost:8083 / connectors / jdbc_source_mysql_11 / tasks”| jq'。'
[
{
“ID”: {
“connector”:“jdbc_source_mysql_11”,“任務”:0
},
“config”:{
“tables”:“`demo` .NUM_TEST`,`demo` .accounts`”,
...
}
},
{
“ID”: {
“connector”:“jdbc_source_mysql_11”,“任務”:1
},
“config”:{
“tables”:“`demo``customers`,`demo` .transactions`”,
...
}
},
{
“ID”: {
“connector”:“jdbc_source_mysql_11”,“任務”:2
},
“config”:{
“tables”:“`security``firewall`,`security``log_events`”,
...
}
}
]
複製程式碼
歡迎工作一到五年的Java工程師朋友們加入Java程式設計師開發: 721575865
群內提供免費的Java架構學習資料(裡面有高可用、高併發、高效能及分散式、Jvm效能調優、Spring原始碼,MyBatis,Netty,Redis,Kafka,Mysql,Zookeeper,Tomcat,Docker,Dubbo,Nginx等多個知識點的架構資料)合理利用自己每一分每一秒的時間來學習提升自己,不要再用"沒有時間“來掩飾自己思想上的懶惰!趁年輕,使勁拼,給未來的自己一個交代!