๋ณธ๋ฌธ ๋ฐ”๋กœ๊ฐ€๊ธฐ
  • ๐Ÿ‘ฉ๐Ÿปโ€๐Ÿ’ป ๐ŸŒฎ ๐Ÿ’ฌ
๐Ÿ‘ฉ๐Ÿป‍๐Ÿ’ป/DB

mysql ๋ฐ์ดํ„ฐ ๋ณ€๊ฒฝ ์‹ค์‹œ๊ฐ„ ํ™•์ธ ์ง„ํ–‰

by ๋ฐ”์ฟ„๋ฆฌ 2024. 10. 20.

mysql ๋ฐ์ดํ„ฐ ๋ณ€๊ฒฝ์„ ์‹ค์‹œ๊ฐ„์œผ๋กœ ํ™•์ธํ•˜๊ณ  ์‹ถ์–ด์„œ searchํ•˜๊ณ  ์ •๋ฆฌํ–ˆ๋‹ค.

 

mysql ๋ฐ์ดํ„ฐ ๋ณ€๊ฒฝ ์‹ค์‹œ๊ฐ„ ํ™•์ธ

๊ฐœ์š”์ด๋ฒˆ์— ์ƒˆ๋กœ ์ง„ํ–‰๋˜๋Š” ํ”„๋กœ์ ํŠธ์—์„œ ์ƒˆ๋กœ์šด ๊ธฐ๋Šฅ์„ ์ถ”๊ฐ€ํ•ด์•ผํ•œ๋‹ค.๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์— ๋ณ€ํ™”๊ฐ€ ์žˆ์„ ๋•Œ, ์‹ค์‹œ๊ฐ„์œผ๋กœ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋Š” ๊ธฐ๋Šฅ์— ๋Œ€ํ•ด์„œ search ํ–ˆ๋‹ค.์‹ค์‹œ๊ฐ„ ๋ฐ์ดํ„ฐ๋ฅผ ๋‹ค๋ฃจ๊ณ  ์‹ถ์—ˆ๋Š”๋ฐ

bonory.tistory.com

์ด๋ฅผ ๊ธฐ๋ฐ˜์œผ๋กœ ์‹ค์ œ๋กœ ์ง„ํ–‰ํ•ด๋ณด๋ ค๊ณ  ํ•œ๋‹ค.

์šฐ์„  ๋‚˜๋Š” docker๋กœ zookeeper, kafka, kafka-connect, mysql์„ ์˜ฌ๋ฆด ๊ฒƒ์ด๋‹ค

๊ทธ๋ž˜์„œ docker-compose.yml์„ ์•„๋ž˜์™€ ๊ฐ™์ด ์ž‘์„ฑํ–ˆ๋‹ค

docker-compose.yml

version: '3'
services:
  zookeeper:
    image: confluentinc/cp-zookeeper:latest
    environment:
      ZOOKEEPER_CLIENT_PORT: 2181
      ZOOKEEPER_TICK_TIME: 2000
    ports:
      - "2181:2181"
  kafka:
    image: confluentinc/cp-kafka:latest
    depends_on:
      - zookeeper
    environment:
      KAFKA_BROKER_ID: 1
      KAFKA_ZOOKEEPER_CONNECT: zookeeper:2181
      KAFKA_ADVERTISED_LISTENERS: PLAINTEXT://kafka:9092
      KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR: 1
    ports:
      - "9092:9092"
  kafka-connect:
    image: debezium/connect:latest
    ports:
      - "8083:8083"
    environment:
      BOOTSTRAP_SERVERS: kafka:9092
      GROUP_ID: "1"
      CONFIG_STORAGE_TOPIC: my_connect_configs
      OFFSET_STORAGE_TOPIC: my_connect_offsets
      STATUS_STORAGE_TOPIC: my_connect_statuses
      KEY_CONVERTER_SCHEMAS_ENABLE: "false"
      VALUE_CONVERTER_SCHEMAS_ENABLE: "false"
    depends_on:
      - kafka
  mysql:
    image: mysql:8.0
    environment:
      MYSQL_ROOT_PASSWORD: root
      MYSQL_DATABASE: robotworld
    ports:
      - "3306:3306"
    volumes:
      - mysql_data:/var/lib/mysql
    command:
      --server-id=1
      --log-bin=mysql-bin
      --binlog-format=ROW
      --binlog-row-image=FULL
volumes:
  mysql_data:
    driver: local

 

mysql

  • volumes : ๋ฐ์ดํ„ฐ๋ฅผ ์œ ์ง€ํ•˜๊ธฐ ์œ„ํ•ด MySQL ๋ฐ์ดํ„ฐ ๋””๋ ‰ํ„ฐ๋ฆฌ๋ฅผ ํ˜ธ์ŠคํŠธ ๋””๋ ‰ํ„ฐ๋ฆฌ ๋˜๋Š” Docker ๋ณผ๋ฅจ์— ๋งˆ์šดํŠธ
  • command : MySQL ์„œ๋ฒ„ ์‹คํ–‰ ์‹œ ํ•„์š”ํ•œ binlog ์„ค์ •์„ ์ถ”๊ฐ€
    • --server-id=1 : MySQL ์„œ๋ฒ„์˜ ๊ณ ์œ ํ•œ ID๋ฅผ ์ง€์ •
    • --log-bin=mysql-bin : ๋ฐ”์ด๋„ˆ๋ฆฌ ๋กœ๊ทธ ํŒŒ์ผ์˜ ๊ธฐ๋ณธ ์ด๋ฆ„์„ ์ง€์ •ํ•˜์—ฌ binlog๋ฅผ ํ™œ์„ฑํ™”
    • --binlog-format=ROW : ํ–‰ ๊ธฐ๋ฐ˜์˜ binlog ํ˜•์‹์„ ์„ค์ •
    • --binlog-row-image=FULL : ๋ณ€๊ฒฝ๋œ ๋ฐ์ดํ„ฐ์˜ ์ „์ฒด ํ–‰์„ ๊ธฐ๋ก

volumes

  • mysql_data: MySQL ๋ฐ์ดํ„ฐ ํŒŒ์ผ์„ ์ €์žฅํ•  Docker ๋ณผ๋ฅจ ์ด๋ฆ„
  • driver: local
    • ๋กœ์ปฌ ๋“œ๋ผ์ด๋ฒ„๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ Docker ๋ณผ๋ฅจ ์ƒ์„ฑ
    • ๊ธฐ๋ณธ์ ์œผ๋กœ Docker๊ฐ€ ์ œ๊ณตํ•˜๋Š” ๋กœ์ปฌ ์Šคํ† ๋ฆฌ์ง€ ์‚ฌ์šฉ

mysql์— binlog ํ™œ์„ฑํ•˜๊ธฐ

์œ„ ์„ค์ •์œผ๋กœ ์ƒ์„ฑ๋œ mysql container์— ์ ‘์†ํ•ด์„œ ์ •์ƒ์ ์œผ๋กœ binlog๊ฐ€ ํ™œ์„ฑํ™” ๋˜์—ˆ๋Š”์ง€ ํ™•์ธํ•œ๋‹ค.

  • docker exec -it [comtainer id] bash
  • mysql -u root -p
  • SHOW VARIABLES LIKE 'log_bin';
    • ON์ด๋ฉด ์„ค์ • ์™„๋ฃŒ

  • SHOW VARIABLES LIKE 'binlog_format';
    • ROW์ด๋ฉด ์„ค์ • ์™„๋ฃŒ

 

Kafka Connect API๋ฅผ ํ†ตํ•ด Debezium MySQL ์ปค๋„ฅํ„ฐ๋ฅผ ๋“ฑ๋ก

๋“ฑ๋กํ•˜๊ธฐ ์ „, Kafka Connect๊ฐ€ ์ œ๋Œ€๋กœ ์‹คํ–‰์ค‘์ธ์ง€ ํ™•์ธ

curl http://localhost:8083/connectors

 

>> ๋นˆ ๋ฆฌ์ŠคํŠธ ํ˜น์€ ์ด๋ฏธ ๋“ฑ๋ก๋œ ๋ชฉ๋ก์„ ๋ฐ˜ํ™˜ํ•˜๋ฉด ์ •์ƒ์ ์œผ๋กœ ๋™์ž‘ํ•˜๋Š” ๊ฒƒ์ž„

 

Kafka Connect API๋Š” RESTful API์ด๋ฏ€๋กœ curl ๋˜๋Š” Postman๊ณผ ๊ฐ™์€ ๋„๊ตฌ๋ฅผ ์‚ฌ์šฉํ•˜๋ ค HTTP ์š”์ฒญ์œผ๋กœ ํ™•์ธํ•œ๋‹ค.

curl -X POST http://localhost:8083/connectors -H "Content-Type: application/json" -d '{
  "name": "mysql-connector",
  "config": {
    "connector.class": "io.debezium.connector.mysql.MySqlConnector",
    "database.hostname": "mysql",
    "database.port": "3306",
    "database.user": "root",
    "database.password": "root",
    "database.server.id": "1",
    "database.server.name": "mysql_server",
    "database.whitelist": "robotworld",
    "database.history.kafka.bootstrap.servers": "kafka:9092",
    "database.history.kafka.topic": "schema-changes.robotworld",
    "include.schema.changes": "true",
    "database.serverTimezone": "UTC",
    "topic.prefix": "mysql_server"
  }
}'

 

์–˜ ๋ณด๋‚ด๊ณ , ์ปค๋„ฅํ„ฐ ์„ฑ๊ณตํ•˜๋ฉด ์ด๋Ÿฐ ์‘๋‹ต ๋ฐ›์Œ

{
  "name": "mysql-connector",
  "config": {
    "connector.class": "io.debezium.connector.mysql.MySqlConnector",
    "database.hostname": "mysql",
    "database.port": "3306",
    "database.user": "root",
    "database.password": "root",
    "database.server.id": "1",
    "database.server.name": "mysql_server",
    "database.whitelist": "robotworld",
    "database.history.kafka.bootstrap.servers": "kafka:9092",
    "database.history.kafka.topic": "schema-changes.robotworld",
    "include.schema.changes": "true",
    "database.serverTimezone": "UTC",
    "topic.prefix": "mysql_server",
    "name": "mysql-connector"
  },
  "tasks": [],
  "type": "source"
}

 

์ปค๋„ฅํ„ฐ ์—ฐ๊ฒฐ ํ™•์ธ

curl -X GET http://localhost:8083/connectors/mysql-connector/status

 

์ปค๋„ฅํ„ฐ ์ œ๊ฑฐ

curl -X DELETE http://localhost:8083/connectors/mysql-connector

 

์ด์ œ MySQL ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์—์„œ ๋ณ€๊ฒฝ ์‚ฌํ•ญ์ด ๋ฐœ์ƒํ•˜๋ฉด ํ•ด๋‹น ๋‚ด์šฉ์ด Kafka๋กœ ์ŠคํŠธ๋ฆฌ๋ฐ ๋œ๋‹ค.

  • Kafka ํ† ํ”ฝ์—์„œ ๋ฐ์ดํ„ฐ ํ™•์ธ: MySQL์—์„œ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค(robotworld)์— ์‚ฝ์ž…, ์—…๋ฐ์ดํŠธ ๋˜๋Š” ์‚ญ์ œ ์ž‘์—…์„ ์ˆ˜ํ–‰ํ•˜๋ฉด ํ•ด๋‹น ๋ณ€๊ฒฝ ์‚ฌํ•ญ์ด Kafka ํ† ํ”ฝ์— ๋ฐ˜์˜. Kafka CLI ๋˜๋Š” Kafka Console Consumer๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์ด ๋ฐ์ดํ„ฐ ํ™•์ธ/์‚ฌ์šฉ ๊ฐ€๋Šฅ
docker exec -it <kafka-container-id> kafka-console-consumer --bootstrap-server kafka:9092 --topic mysql_server.robotworld.<your_table_name> --from-beginning
  • Kafka ํ† ํ”ฝ ํ™•์ธ: Kafka Console Consumer๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๋ฐ์ดํ„ฐ๊ฐ€ ํ† ํ”ฝ์— ์ •์ƒ์ ์œผ๋กœ ๋“ค์–ด์˜ค๋Š”์ง€ ํ™•์ธ ๊ฐ€๋Šฅ !