Project nay minh hoa mot he thong data engineering gom 2 luong doc lap:
- Batch ETL: MySQL -> Spark -> MinIO theo Bronze/Silver/Gold.
- Realtime CDC: MySQL binlog -> Debezium -> Kafka -> Python consumers -> Redis -> Kafka
order_final.
Batch dung de tao data warehouse. Realtime CDC dung de bat order moi gan realtime.
- Mental Model
- Thanh Phan He Thong
- Kien Truc Tong Quan
- Cau Truc Thu Muc
- Cach Chay Tu Dau
- Batch ETL Pipeline
- Realtime CDC Pipeline
- DBeaver va UI
- Kiem Tra Va Debug
- Reset Du Lieu
- Loi Thuong Gap
Ngan gon:
MySQL = source database
Airflow = scheduler crawl books
Debezium = doc MySQL binlog
Kafka = event bus
Python consumers = xu ly realtime logic
Redis = state tam + dedup
Spark = batch transform engine
MinIO = data lake luu Parquet
Bronze = raw
Silver = cleaned
Gold = analytics/star schemaBatch flow:
MySQL -> Spark Bronze -> Spark Silver -> Spark Gold -> MinIORealtime flow:
MySQL binlog -> Debezium -> Kafka CDC topics -> Python consumers -> Redis -> Kafka order_finalBatch va realtime khong can chay cung luc.
Neu muon CDC bat dung order moi:
Start Debezium + consumers truoc -> generate/insert orders sau| Component | Container | Port | Vai tro |
|---|---|---|---|
| MySQL | mysql-db |
3306 |
Source database |
| Zookeeper | zookeeper |
2181 |
Kafka coordination |
| Kafka broker 1 | kafka-broker |
9092, 9093 |
Kafka broker |
| Kafka broker 2 | kafka-broker-2 |
9094, 9095 |
Kafka broker |
| Kafka Connect | kafka-connect |
8083 |
Chay Debezium connector |
| Redis | redis-cache |
6379 |
Cache + dedup realtime |
| MinIO | minio-storage |
9000, 9001 |
S3-compatible data lake |
| Spark master | spark-master |
7077, 8080 |
Spark cluster master |
| Spark worker | spark-worker |
8081 |
Spark executor worker |
| Airflow webserver | airflow-webserver |
8082 |
Airflow UI |
| Airflow scheduler | airflow-scheduler |
- | Schedule DAG |
| Kafka UI | kafka-ui |
8084 |
Xem Kafka topics |
+------------------+
| MySQL |
| project_db |
| books |
| customers |
| payment_method |
| orders |
| order_details |
+--------+---------+
|
+------------------+------------------+
| |
v v
+---------------------+ +---------------------+
| Batch ETL | | Realtime CDC |
| Spark JDBC | | Debezium |
+----------+----------+ +----------+----------+
| |
v v
+---------------------+ +---------------------+
| MinIO | | Kafka topics |
| bronze | | mysql.project_db.* |
| silver | +----------+----------+
| gold | |
+---------------------+ v
+------------------------+
| Python consumers |
| order.py |
| order_details.py |
+-----------+------------+
|
v
+------------------------+
| Redis |
| order_info:* |
| ordered_products:* |
| order_status:* |
+-----------+------------+
|
v
+------------------------+
| Kafka order_final |
+------------------------+.
├── docker-compose.yml
├── requirements.txt
├── .env.example
├── data/
│ ├── customers.csv
│ └── payment_method.csv
├── init-scripts/
│ ├── 01_init.sql
│ └── run.md
├── airflow/
│ └── dags/
│ └── openlibrary_dag.py
├── scripts/
│ ├── crawl-data/
│ │ ├── crawl_openlibrary.py
│ │ ├── db_helper.py
│ │ └── checkpoint.py
│ ├── database/
│ │ ├── load_file.py
│ │ └── generate_orders.py
│ ├── batch/
│ │ ├── bronze_layer.py
│ │ ├── silver_layer.py
│ │ └── gold_layer.py
│ ├── real-time/
│ │ ├── mysql-src-connector.json
│ │ ├── kafka_handler.py
│ │ ├── order.py
│ │ └── order_details.py
│ └── utils.py
├── tests/
│ └── test_regressions.py
├── logs/
└── checkpoints/Tat ca command ben duoi chay trong PowerShell tai root project:
cd D:\Data\Library_projectpython -m pip install -r requirements.txtdocker compose up -dCheck:
docker compose pscurl.exe http://localhost:8083/connectorsNeu chua co connector:
curl.exe -X POST http://localhost:8083/connectors -H "Content-Type: application/json" --data-binary "@scripts/real-time/mysql-src-connector.json"Check status:
curl.exe http://localhost:8083/connectors/mysql-source-connector/statusPhai thay:
connector RUNNINGChay lenh nay neu connector bi loi privilege:
docker exec mysql-db mysql -uroot -padmin -e "GRANT RELOAD, FLUSH_TABLES, REPLICATION CLIENT, REPLICATION SLAVE ON *.* TO 'admin'@'%'; FLUSH PRIVILEGES;"Restart connector:
curl.exe -X POST http://localhost:8083/connectors/mysql-source-connector/restart$env:MYSQL_HOST='localhost'
python scripts/database/load_file.pyAirflow DAG:
docker exec -it airflow-webserver airflow dags trigger openlibrary_crawlerQuan trong:
- Neu
openlibrary.orgbi timeout, khong co data sach that. - Project hien tai mac dinh khong force offline de tranh ghi
Offline Book ...gia. - Neu muon test offline, set
OPENLIBRARY_FORCE_OFFLINE=truetrong.envroi restart Airflow.
Test OpenLibrary tu container:
docker exec airflow-webserver python -c "import urllib.request; print(urllib.request.urlopen('https://openlibrary.org/search.json?q=fiction&limit=1&offset=0', timeout=20).status)"Neu tra 200, co the crawl data that.
Chi generate sau khi books, customers, payment_method da co data.
$env:MYSQL_HOST='localhost'
$env:MAX_ORDERS='1000'
$env:ORDER_SLEEP_SECONDS='0'
python scripts/database/generate_orders.pyCheck MySQL:
docker exec mysql-db mysql -uadmin -padmin project_db -e "SELECT COUNT(*) AS books FROM books; SELECT COUNT(*) AS customers FROM customers; SELECT COUNT(*) AS payment_method FROM payment_method; SELECT COUNT(*) AS orders FROM orders; SELECT COUNT(*) AS order_details FROM order_details;"Khong chay batch bang python scripts/batch/*.py tren Windows.
Chay trong Spark container:
docker exec spark-master /opt/spark/bin/spark-submit --master spark://spark-master:7077 --packages org.apache.hadoop:hadoop-aws:3.3.4,mysql:mysql-connector-java:8.0.33 /opt/scripts/batch/bronze_layer.pydocker exec spark-master /opt/spark/bin/spark-submit --master spark://spark-master:7077 --packages org.apache.hadoop:hadoop-aws:3.3.4,mysql:mysql-connector-java:8.0.33 /opt/scripts/batch/silver_layer.pydocker exec spark-master /opt/spark/bin/spark-submit --master spark://spark-master:7077 --packages org.apache.hadoop:hadoop-aws:3.3.4,mysql:mysql-connector-java:8.0.33 /opt/scripts/batch/gold_layer.pyMo terminal PowerShell 1:
$env:KAFKA_BOOTSTRAP='localhost:9093,localhost:9095'
$env:REDIS_HOST='localhost'
python scripts/real-time/order.pyMo terminal PowerShell 2:
$env:KAFKA_BOOTSTRAP='localhost:9093,localhost:9095'
$env:REDIS_HOST='localhost'
python scripts/real-time/order_details.pyNeu dung CMD, dung set, khong dung $env:
set KAFKA_BOOTSTRAP=localhost:9093,localhost:9095
set REDIS_HOST=localhost
python scripts/real-time/order_details.pySau khi Debezium va consumers da chay, insert order moi:
docker exec mysql-db mysql -uadmin -padmin project_db -e "SET @oid=UUID(); INSERT INTO orders (id, timestamp, customer_id, store_id, payment_method_id, num_products) VALUES (@oid, NOW(), 1, 1, 1, 2); INSERT INTO order_details (order_id, product_id, quantity, subtotal, is_suggestion) VALUES (@oid, 1, 1, 13.82, false), (@oid, 2, 2, 27.66, false); SELECT @oid AS test_order_id;"Check output:
docker exec kafka-broker kafka-run-class kafka.tools.GetOffsetShell --broker-list localhost:9092 --topic order_final --time -1Xem messages:
docker exec kafka-broker kafka-console-consumer --bootstrap-server localhost:9092 --topic order_final --from-beginning --max-messages 5Batch ETL xu ly du lieu theo lo. No doc MySQL tai mot thoi diem va ghi data lake tren MinIO.
books
customers
payment_method
orders
order_detailsFile:
scripts/batch/bronze_layer.pyInput:
MySQLOutput:
s3a://bronze/orders
s3a://bronze/order_details
s3a://bronze/customers
s3a://bronze/payment_method
s3a://bronze/booksVai tro:
- Copy raw data tu MySQL sang MinIO.
- Them metadata:
bronze_ingestion_time
bronze_source
monthFile:
scripts/batch/silver_layer.pyInput:
s3a://bronze/...Output:
s3a://silver/orders
s3a://silver/order_details
s3a://silver/customers
s3a://silver/payment_methodVai tro:
- Dedupe.
- Clean schema.
- Rename ID cho ro nghia.
- Giu
quantityvasubtotalthat.
Schema quan trong:
orders.id -> orders.order_id
customers.id -> customers.customer_id
payment_method.id -> payment_method.payment_method_id
order_details.id -> order_details.order_detail_idFile:
scripts/batch/gold_layer.pyOutput dimensions:
s3a://gold/dimensions/dim_customer
s3a://gold/dimensions/dim_payment_method
s3a://gold/dimensions/dim_product
s3a://gold/dimensions/dim_dateOutput facts:
s3a://gold/facts/fact_orders
s3a://gold/facts/fact_order_productsfact_orders gom theo order:
order_id
customer_id
payment_method_id
order_date_id
product_count
expected_products
quantity_ordered
order_status
created_timestamp
fact_created_attotal_amount tinh tu:
sum(order_details.subtotal)quantity_ordered tinh tu:
sum(order_details.quantity)Realtime CDC bat thay doi moi tu MySQL.
MySQL binlog -> Debezium -> Kafka -> Python consumers -> Redis -> Kafka order_finalDe bat CDC dung, can chay:
MySQL
Kafka
Kafka Connect / Debezium connector
Redis
order.py
order_details.pyBatch khong can chay cung realtime.
Thu tu dung:
Start Debezium + consumers truoc -> generate/insert orders sauDebezium doc MySQL binlog.
Connector:
mysql-source-connectorConfig:
scripts/real-time/mysql-src-connector.jsonNo doc 2 tables:
project_db.orders
project_db.order_detailsVa ghi ra Kafka topics:
mysql.project_db.orders
mysql.project_db.order_detailsMessage co dang:
{
"payload": {
"before": null,
"after": {
"id": "order-id",
"customer_id": 1,
"payment_method_id": 1,
"num_products": 2
},
"op": "c"
}
}op:
c = create/insert
u = update
r = snapshot/readConsumes:
mysql.project_db.ordersConsumer group:
order_info_trackerVai tro:
- Doc order metadata.
- Luu Redis hash:
order_info:{order_id}Gia tri:
customer_id
payment_method_id
num_productsorder.py khong emit order_final.
Consumes:
mysql.project_db.order_detailsConsumer group:
order_products_trackerVai tro:
- Doc product trong order.
- Add product vao Redis set:
ordered_products:{order_id}Sau moi product, check:
len(ordered_products:{order_id}) == order_info:{order_id}.num_productsNeu bang nhau, order complete.
order_info:{order_id}
ordered_products:{order_id}
order_status:{order_id}order_status:{order_id} dung de dedup:
emitting
completedFlow dedup:
SETNX order_status:{order_id} emitting
send order_final
set order_status:{order_id} completedNeu worker khac thay key da ton tai, no khong emit nua.
Topic output:
order_finalMessage mau:
{
"order_id": "69339232-7905-11f1-a1f4-7a7e11b212fe",
"customer_id": "1",
"payment_method_id": "1",
"product_ids": ["1", "2"]
}No nghia la:
Order nay da du order metadata va du product details.Config:
Database: MySQL
Host: localhost
Port: 3306
Database: project_db
Username: admin
Password: adminTables thay trong DBeaver:
books
customers
payment_method
orders
order_detailsSQL hay dung:
SHOW TABLES;
SELECT COUNT(*) FROM books;
SELECT COUNT(*) FROM customers;
SELECT COUNT(*) FROM payment_method;
SELECT COUNT(*) FROM orders;
SELECT COUNT(*) FROM order_details;Luu y:
- Bronze/Silver/Gold khong nam trong MySQL.
- Chung la Parquet files tren MinIO.
- DBeaver MySQL khong thay
bronze,silver,gold.
URL: http://localhost:9001
Username: admin
Password: admin123Buckets:
bronze
silver
lookup-data
order-infoURL: http://localhost:8082
Username: admin
Password: adminDAG:
openlibrary_crawlerURL: http://localhost:8084URL: http://localhost:8080docker compose pscurl.exe http://localhost:8083/connectors/mysql-source-connector/statusdocker exec kafka-broker kafka-topics --bootstrap-server localhost:9092 --listdocker exec kafka-broker kafka-run-class kafka.tools.GetOffsetShell --broker-list localhost:9092 --topic mysql.project_db.orders --time -1
docker exec kafka-broker kafka-run-class kafka.tools.GetOffsetShell --broker-list localhost:9092 --topic mysql.project_db.order_details --time -1
docker exec kafka-broker kafka-run-class kafka.tools.GetOffsetShell --broker-list localhost:9092 --topic order_final --time -1docker exec kafka-broker kafka-console-consumer --bootstrap-server localhost:9092 --topic order_final --from-beginning --max-messages 5docker exec redis-cache redis-cli -n 1 KEYS "order_info:*"
docker exec redis-cache redis-cli -n 1 KEYS "ordered_products:*"
docker exec redis-cache redis-cli -n 1 KEYS "order_status:*"docker exec mysql-db mysql -uadmin -padmin project_db -e "SELECT COUNT(*) AS books FROM books; SELECT COUNT(*) AS customers FROM customers; SELECT COUNT(*) AS payment_method FROM payment_method; SELECT COUNT(*) AS orders FROM orders; SELECT COUNT(*) AS order_details FROM order_details;"python -m pytest testsLenh nay xoa orders, order_details, books.
docker exec mysql-db mysql -uadmin -padmin project_db -e "SET FOREIGN_KEY_CHECKS=0; TRUNCATE TABLE order_details; TRUNCATE TABLE orders; TRUNCATE TABLE books; SET FOREIGN_KEY_CHECKS=1;"Xoa checkpoint crawler:
Remove-Item -LiteralPath "checkpoints\openlibrary_checkpoint.txt" -Force -ErrorAction SilentlyContinuedocker exec redis-cache redis-cli -n 1 FLUSHDBCan can than. Lenh nay xoa topics.
docker exec kafka-broker kafka-topics --bootstrap-server localhost:9092 --delete --topic mysql.project_db.orders
docker exec kafka-broker kafka-topics --bootstrap-server localhost:9092 --delete --topic mysql.project_db.order_details
docker exec kafka-broker kafka-topics --bootstrap-server localhost:9092 --delete --topic order_finalRecreate connector:
curl.exe -X DELETE http://localhost:8083/connectors/mysql-source-connector
curl.exe -X POST http://localhost:8083/connectors -H "Content-Type: application/json" --data-binary "@scripts/real-time/mysql-src-connector.json"Sai:
python scripts/batch/bronze_layer.pyLoi thuong gap:
UnsupportedOperationException: getSubject
winutils.exe not found
HADOOP_HOME unsetDung:
docker exec spark-master /opt/spark/bin/spark-submit --master spark://spark-master:7077 --packages org.apache.hadoop:hadoop-aws:3.3.4,mysql:mysql-connector-java:8.0.33 /opt/scripts/batch/bronze_layer.pyPowerShell khong dung \ de xuong dong.
Sai:
docker exec spark-master /opt/spark/bin/spark-submit \
--master spark://spark-master:7077 \
/opt/scripts/batch/bronze_layer.pyDung:
docker exec spark-master /opt/spark/bin/spark-submit `
--master spark://spark-master:7077 `
--packages org.apache.hadoop:hadoop-aws:3.3.4,mysql:mysql-connector-java:8.0.33 `
/opt/scripts/batch/bronze_layer.pyHoac 1 dong.
Loi:
/home/spark/.ivy2/cache/... No such file or directoryFix:
docker exec -u root spark-master sh -c "mkdir -p /home/spark/.ivy2/cache /home/spark/.ivy2/jars && chown -R spark:spark /home/spark/.ivy2"Loi:
Class org.apache.hadoop.fs.s3a.S3AFileSystem not foundNguyen nhan: thieu hadoop-aws jar.
Fix: chay Spark voi --packages:
--packages org.apache.hadoop:hadoop-aws:3.3.4,mysql:mysql-connector-java:8.0.33Check logs:
docker logs kafka-connect --tail 200Neu thay:
Access denied; you need RELOAD or FLUSH_TABLES privilegeFix:
docker exec mysql-db mysql -uroot -padmin -e "GRANT RELOAD, FLUSH_TABLES, REPLICATION CLIENT, REPLICATION SLAVE ON *.* TO 'admin'@'%'; FLUSH PRIVILEGES;"
curl.exe -X POST http://localhost:8083/connectors/mysql-source-connector/restartRealtime CDC phu hop order moi. Neu consumer start sau snapshot, Redis state co the khong reconstruct hoan hao historical orders.
Muon test sach:
Start Debezium + order.py + order_details.py truoc
Generate/insert orders sauPowerShell:
$env:KAFKA_BOOTSTRAP='localhost:9093,localhost:9095'CMD:
set KAFKA_BOOTSTRAP=localhost:9093,localhost:9095Test:
docker exec airflow-webserver python -c "import urllib.request; print(urllib.request.urlopen('https://openlibrary.org/search.json?q=fiction&limit=1&offset=0', timeout=20).status)"Neu timeout, Docker/container khong ra duoc openlibrary.org:443. Can sua proxy/VPN/DNS Docker.
Neu bat OPENLIBRARY_FORCE_OFFLINE=true, data se co dang:
Offline Book 1
Offline Author 1Neu muon data that, phai de:
OPENLIBRARY_FORCE_OFFLINE=false
OPENLIBRARY_OFFLINE_FALLBACK=falseDa verify trong qua trinh debug:
Silver Layer completed successfully
Gold Layer completed successfully
Kafka Connect connector RUNNING
Debezium privileges fixed
Kafka topics created
order_final has messages
pytest tests passVi du offsets sau khi chay realtime:
mysql.project_db.orders:0:1001
mysql.project_db.order_details:0:1827
order_final:0:874- Batch va realtime la 2 pipeline rieng.
- Batch khong bat CDC.
- Realtime khong tao Gold warehouse.
- DBeaver chi xem MySQL source tables.
- MinIO chua Bronze/Silver/Gold Parquet.
- Muon CDC bat dung order moi: start Debezium + consumers truoc khi generate orders.
- Muon data sach that: Docker/host phai connect duoc OpenLibrary.