← Kỹ sư dữ liệu← Data Engineer
Kỹ sư dữ liệuData Engineer19 Th7, 2026Jul 19, 202619 phút đọc14 min read

Nguồn dữ liệu & IngestionData Sources & Ingestion

Thuộc bộ kiến thức Data Engineer Roadmap.

Tổng quan

Mọi data pipeline đều bắt đầu từ một nơi nằm ngoài tầm kiểm soát trực tiếp của bạn: một application database do team product sở hữu, một API của SaaS bên thứ ba, một stream các event clickstream, một thư mục chứa file CSV mà ai đó thả vào object storage, hoặc một cảm biến gửi số liệu mỗi giây. Data ingestion là công việc di chuyển dữ liệu từ những nguồn này vào hệ thống mà bạn có thể lưu trữ, biến đổi và phân tích — data warehouse, data lake, hoặc một streaming platform.

Ingestion nằm ở đầu ngoài cùng của data stack, nghĩa là các lỗi ở đây tốn kém nhất để debug: một transformation lỗi thì thường lộ ra ngay qua một dbt test, nhưng một batch event bị API mất kết nối và âm thầm bỏ sót có thể không ai để ý suốt nhiều tuần, âm thầm làm sai lệch mọi báo cáo downstream. Bài viết này sẽ đi qua dữ liệu đến từ đâu, những mối quan tâm thực tế khi thu thập dữ liệu một cách có trách nhiệm và đáng tin cậy, cơ chế của hai mô hình ingestion phổ biến nhất — batch và streaming — cùng kỹ thuật nối giữa replication ở tầng storage và ingestion: Change Data Capture (CDC).

Kiến thức nền tảng

Các nhóm nguồn dữ liệu

Data engineer thường lấy dữ liệu từ một số nhóm nguồn lặp đi lặp lại, mỗi nhóm có hình dạng, tần suất và kiểu lỗi khác nhau.

Loại nguồnVí dụ điển hìnhHình dạng dữ liệuKiểu cập nhật
Application database (OLTP)PostgreSQL/MySQL phục vụ web appDòng dữ liệu chuẩn hóa caoInsert/update/delete liên tục
API / công cụ SaaS bên thứ baStripe, Salesforce, HubSpot, Google AnalyticsJSON qua REST/GraphQLKéo theo yêu cầu, bị giới hạn rate
File thả vào storageCSV/Parquet/JSON trong S3, SFTP, GCSFile phẳng, dump theo lôThả file định kỳ (hàng giờ/ngày)
Event stream / logKafka topic, application log, clickstreamEvent JSON/Avro bán cấu trúcLiên tục, khối lượng lớn
Thiết bị IoT / cảm biếnCảm biến nhiệt độ, thiết bị GPS, máy móc công nghiệpSố liệu nhỏ, tần suất caoLiên tục, thường ở edge
Bộ dữ liệu công khai / chính phủTổng cục thống kê, data.gov, WHO, cơ quan khí tượngCSV/JSON/API, đôi khi tải hàng loạtPhát hành thưa (hàng tháng/năm)
WebhookCallback từ payment provider, event GitHub, event SlackThông báo JSON được push đếnTheo sự kiện, được đẩy đến bạn

Application database (hệ thống OLTP). Đây là các database đứng sau bản thân sản phẩm — các bảng ghi lại đơn hàng, người dùng, giao dịch khi chúng xảy ra. Chúng được tối ưu cho các transaction đọc/ghi nhỏ và nhanh, không phải cho các phép quét lớn mà phân tích dữ liệu đòi hỏi, đó là lý do data engineer trích xuất (extract) từ chúng thay vì query trực tiếp phục vụ báo cáo.

API và công cụ SaaS bên thứ ba. Dữ liệu marketing, sales, support, thanh toán thường nằm ngoài hạ tầng của chính bạn, trong các công cụ như Salesforce, Stripe, Zendesk. Con đường duy nhất vào là API của nhà cung cấp, đi kèm schema riêng, cơ chế phân trang (pagination) riêng và rate limit riêng (nói kỹ hơn bên dưới).

File thả vào storage. Một pattern phổ biến, ít công nghệ nhưng rất thực tế: một đối tác hoặc team nội bộ export một file CSV/Parquet/JSON và đặt vào bucket S3, server SFTP, hoặc một ổ đĩa chia sẻ theo lịch. Đơn giản để tạo ra, nhưng dễ vỡ — định dạng file, dấu phân cách, encoding có thể trôi dạt âm thầm nếu không ai sở hữu một schema contract rõ ràng.

Event stream và log. Application log, event clickstream, và message queue (Kafka, Kinesis, Pub/Sub) tạo ra dòng chảy liên tục các bản ghi nhỏ, bán cấu trúc. Đây là nguồn tự nhiên cho streaming ingestion.

Thiết bị IoT và cảm biến. Các thiết bị vật lý — cảm biến công nghiệp, thiết bị GPS, đồng hồ đo thông minh — phát ra số liệu nhỏ với tần suất cao, thường qua mạng hạn chế (MQTT là giao thức phổ biến ở đây), và thường cần tổng hợp ở edge trước khi đến được cloud.

Bộ dữ liệu công khai và chính phủ. Tổng cục thống kê, cổng dữ liệu mở, cơ quan quản lý công bố các bộ dữ liệu có cấu trúc, thường dưới dạng tải hàng loạt hoặc API khiêm tốn, cập nhật theo nhịp chậm (hàng tháng, hàng năm). Hữu ích để làm giàu dữ liệu (ví dụ join mã bưu điện với dữ liệu nhân khẩu học) nhưng hiếm khi là nguồn nghiệp vụ chính.

Webhook. Thay vì bạn poll API của nhà cung cấp, nhà cung cấp chủ động push một event đến endpoint HTTP mà bạn expose ngay khi điều gì đó xảy ra (thanh toán thành công, PR được merge). Mô hình này đảo ngược mô hình pull thông thường và đòi hỏi service ingestion của bạn phải luôn sẵn sàng nhận push một cách đáng tin cậy — xem ./11-streaming-and-realtime-data.md để hiểu cách xử lý kiểu ingress theo sự kiện này.

Hiểu cách dữ liệu được sinh ra trước khi xây dựng pipeline

Một pipeline chỉ đáng tin cậy bằng chính hệ thống sinh ra dữ liệu mà nó đọc. Trước khi viết một dòng code extract nào, việc trả lời ba câu hỏi về hệ thống upstream sẽ rất đáng giá:

  1. Schema có ổn định không — Team sở hữu nguồn có version hóa API hoặc migration database của họ không? Một cột mới có xuất hiện âm thầm, hay bạn sẽ được báo trước? Một bảng OLTP thuộc một service đang phát triển tích cực thay đổi thường xuyên hơn nhiều so với một bộ dữ liệu chính phủ ít biến động.
  2. Volume và pattern lưu lượng — Dữ liệu được sinh ra đều đặn nhỏ giọt, hay theo đợt trong ngày (ví dụ một batch job ban đêm ngay trong hệ thống nguồn)? Một ingestion giả định tốc độ ổn định sẽ bất ngờ trước một đợt tăng đột biến 100 lần vào lúc nửa đêm.
  3. Ai sở hữu (ownership) — Bạn thực sự có thể trao đổi với ai khi có sự cố? Một database OLTP nội bộ có team engineering bạn có thể page; một bộ dữ liệu công khai thì cùng lắm chỉ có một mailing list. Biết ranh giới ownership từ đầu quyết định bạn cần code ingestion phòng thủ đến mức nào (ví dụ validate schema chặt chẽ và cảnh báo khi nguồn không có chủ sở hữu rõ ràng hoặc là nguồn bên ngoài).

Coi nguồn dữ liệu như một hộp đen “cứ cung cấp dữ liệu” là nguyên nhân gốc phổ biến nhất khiến pipeline dễ vỡ. Vài phút trao đổi với team sở hữu hệ thống upstream — hoặc đọc changelog của họ — tiết kiệm nhiều ngày chữa cháy về sau.

Những cân nhắc khi thu thập dữ liệu

Ngoài hình dạng kỹ thuật của dữ liệu, việc thu thập dữ liệu một cách có trách nhiệm còn liên quan đến vài yếu tố thực tế không thể bỏ qua:

Khái niệm chính

Batch, streaming và micro-batch ingestion

Quyết định thiết kế lớn đầu tiên cho bất kỳ ingestion pipeline nào là khi nào dữ liệu di chuyển: tất cả cùng lúc theo lịch, liên tục ngay khi được sinh ra, hay ở đâu đó giữa hai thái cực này.

Batch ingestion kéo một khối dữ liệu theo lịch — hàng giờ, hàng đêm, hàng tuần. Đây là mô hình đơn giản nhất để xây dựng và suy luận: một job chạy, extract những gì thay đổi (hoặc toàn bộ), load, rồi kết thúc. Các công cụ như Airflow điều phối kiểu pipeline này một cách tự nhiên. Đánh đổi là độ trễ (latency): dữ liệu chỉ mới bằng lần chạy gần nhất, nên một batch chạy đêm nghĩa là dữ liệu của hôm qua mới có sẵn hôm nay.

Streaming (real-time) ingestion xử lý từng bản ghi (hoặc micro-record) ngay khi nó đến, thường qua một message broker như Kafka, Kinesis, Pub/Sub, đưa vào một stream processor (Flink, Spark Structured Streaming, ksqlDB). Latency giảm xuống còn vài giây hoặc ít hơn, nhưng độ phức tạp vận hành tăng mạnh: bạn phải xử lý event đến không đúng thứ tự, exactly-once so với at-least-once delivery, backpressure, và stateful processing. Nội dung đầy đủ ở ./11-streaming-and-realtime-data.md.

Micro-batching là điểm cân bằng thực dụng ở giữa: thay vì xử lý từng event một hoặc chờ trọn một ngày, bạn ingest các batch nhỏ theo một chu kỳ ngắn — ví dụ mỗi một đến năm phút. Cách này nắm bắt phần lớn lợi ích về độ tươi mới của streaming trong khi vẫn giữ được phần lớn sự đơn giản vận hành của batch, và đây chính là điều mà trigger mặc định của Spark Structured Streaming, hay một Airflow DAG chạy theo lịch dày, thực hiện.

Loại ingestionLatencyĐộ phức tạpCơ chế trigger điển hình
BatchPhút đến giờ (hoặc ngày)ThấpCron/scheduler (ví dụ 2 giờ sáng mỗi đêm)
Micro-batchGiây đến phútTrung bìnhChu kỳ cố định ngắn (ví dụ mỗi 1–5 phút)
StreamingMili giây đến giâyCaoLiên tục, theo sự kiện

Lựa chọn giữa các mô hình này là đánh đổi giữa mức độ “tươi” mà nghiệp vụ thực sự cần và mức độ phức tạp vận hành mà team có thể gánh vác. Đa số use case phân tích (dashboard hàng ngày, báo cáo hàng tuần) được phục vụ tốt bởi batch hoặc micro-batch; phát hiện gian lận, gợi ý real-time, cảnh báo vận hành mới đáng để trả giá cho streaming thực sự.

Change Data Capture (CDC)

Extract lại toàn bộ bảng nguồn mỗi lần chạy — gọi là full extraction — thì đơn giản nhưng lãng phí và chậm một khi bảng phát triển lên vài triệu dòng. Change Data Capture (CDC) giải quyết vấn đề này bằng cách chỉ capture các thay đổi ở cấp độ dòng (insert, update, delete) đã xảy ra kể từ lần extract trước, thay vì kéo lại toàn bộ bảng.

Có hai cách phổ biến để triển khai CDC:

Log-based CDC đọc transaction log nội bộ của database — cơ chế mà database đã dùng sẵn cho replication và crash recovery của chính nó — và biến mỗi thay đổi trong log thành một event. Debezium là công cụ mã nguồn mở chuẩn cho việc này: nó đọc binlog của MySQL hoặc write-ahead log (WAL) của PostgreSQL và phát ra một event Kafka cho mỗi insert, update, delete. Vì tận dụng một log mà database vốn đã duy trì sẵn, log-based CDC:

Query-based CDC thay vào đó poll bảng nguồn định kỳ, so sánh một cột timestamp updated_at (hoặc một ID tăng dần) để tìm các dòng đã thay đổi kể từ lần poll trước. Cách này dễ thiết lập hơn — không cần truy cập vào nội bộ database — nhưng có những hạn chế thực sự: không thể phát hiện hard delete (một dòng biến mất thì không xuất hiện trong query WHERE updated_at > last_poll), tạo thêm tải đọc lặp lại lên database nguồn, và bỏ sót bất kỳ dòng nào thay đổi rồi đổi lại giữa hai lần poll.

Log-based CDCQuery-based CDC
Tải lên nguồnThấp (đọc log, không đọc bảng)Cao hơn (query polling lặp lại)
Bắt được deleteKhông (trừ khi dùng cờ soft-delete)
Độ phức tạp thiết lậpCao hơn (cần quyền truy cập log, ví dụ bật binlog/WAL)Thấp hơn (chỉ cần quyền truy cập SQL)
Đảm bảo thứ tựMạnh (log có thứ tự)Yếu (phụ thuộc thời điểm poll)
Công cụ điển hìnhDebezium, AWS DMS, FivetranJob polling SQL tự viết

Với các hệ thống production có khối lượng ghi đáng kể, log-based CDC là cách tiếp cận được ưu tiên chính vì nó bảo vệ database nguồn khỏi tải bổ sung và cho một bức tranh đầy đủ, có thứ tự về mọi thay đổi — kể cả những delete mà query-based polling sẽ âm thầm bỏ sót.

Cơ chế Extract & Load

Full extraction so với incremental extraction. Full extraction kéo toàn bộ dataset mỗi lần chạy; đơn giản và tự phục hồi (mọi lỗi trong quá khứ bị xóa sạch ở lần chạy tiếp theo) nhưng không scale tốt khi bảng lớn dần. Incremental extraction chỉ kéo các dòng mới hoặc thay đổi kể từ lần chạy trước — dùng một cột timestamp, một ID tự tăng, hoặc CDC — và là lựa chọn mặc định một khi nguồn đủ lớn khiến việc reload toàn bộ quá chậm hoặc quá tốn kém.

Idempotent load. Một nguyên tắc reliability cốt lõi: một ingestion job phải an toàn khi được chạy lại. Nếu job lỗi giữa chừng, hoặc vô tình bị trigger hai lần, chạy lại nó không được tạo ra dòng trùng lặp hay đếm trùng metric. Các kỹ thuật phổ biến gồm upsert theo một khóa tự nhiên hoặc khóa surrogate (INSERT ... ON CONFLICT DO UPDATE / MERGE) thay vì append mù quáng, ghi vào một staging table rồi hoán đổi nó vào một cách atomic, và loại bỏ trùng lặp (deduplicate) theo unique event ID trước khi ghi cuối cùng. Idempotency là điều cho phép bạn retry ingestion job đã lỗi mà không sợ hãi — một tính chất dễ bỏ qua lúc ban đầu nhưng tốn kém để bổ sung sau này.

Watermarking và checkpointing. Để biết cần extract tiếp theo cái gì, một pipeline incremental cần theo dõi nó đã đi đến đâu — một watermark (ví dụ: “updated_at được extract lần cuối = 2026-07-18 23:00:00”) hoặc một checkpoint (ví dụ: offset của một Kafka consumer). Trạng thái này phải được lưu bền vững (trong một control table, một metadata store, hoặc cơ chế track offset riêng của streaming platform) và chỉ được cập nhật sau khi một batch được xác nhận đã ghi thành công — nếu không, một crash xảy ra giữa lúc extract và lúc cập nhật checkpoint có thể âm thầm bỏ sót hoặc ghi trùng dữ liệu.

API như một nguồn dữ liệu

Ingest từ một REST API bên thứ ba kéo theo một loạt mối quan tâm thực tế không xuất hiện khi đọc trực tiếp từ database:

Xử lý tốt ba yếu tố này chính là phần lớn sự khác biệt giữa một script “curl trong cron job” mong manh và một ingestion pipeline đáng tin cậy chống lại một API bên thứ ba.

So sánh các pattern ingestion

Kết hợp cơ chế extract với mô hình thời gian cho ra bốn pattern thực tế:

PatternLatencyĐộ phức tạpPhù hợp nhất với
Full batchGiờ đến ngàyThấpBảng nhỏ/ít thay đổi, backfill lần đầu
Incremental batchPhút đến giờTrung bìnhBảng lớn có cột updated_at/ID đáng tin cậy
CDCGiây đến phútTrung bình–caoBảng OLTP lớn cần replication gần real-time, kể cả delete
StreamingMili giây đến giâyCaoUse case theo sự kiện: phát hiện gian lận, dashboard trực tiếp, cảnh báo

Trong thực tế, đa số data platform dùng kết hợp: CDC hoặc incremental batch cho replication OLTP vào warehouse, batch pull cho dữ liệu API bên thứ ba ít thay đổi nhanh, và streaming dành riêng cho nhóm use case thực sự cần latency dưới một phút. Sau khi dữ liệu đã vào hệ thống, nó chảy tiếp vào transformation — xem ./08-etl-elt-and-data-pipelines.md — và cần được kiểm tra tính đúng đắn cũng như theo dõi lineage, nội dung ở ./14-data-quality-governance-and-metadata.md.

Best Practices

Tài liệu tham khảo

Part of the Data Engineer Roadmap knowledge base.

Overview

Every data pipeline starts somewhere outside of your control: an application database that a product team owns, a third-party SaaS API, a stream of clickstream events, a folder of CSV files someone drops into object storage, or a sensor sending readings every second. Data ingestion is the discipline of moving data from these sources into a system where you can store, transform, and analyze it — a data warehouse, a data lake, or a streaming platform.

Ingestion sits at the very front of the data stack, which means its failures are the most expensive to debug: a broken transformation is visible in a dbt test, but a silently dropped batch of events from a flaky API can go unnoticed for weeks and quietly corrupt every downstream report. This note covers where data comes from, the practical concerns of collecting it responsibly and reliably, and the mechanics of the two dominant ingestion patterns — batch and streaming — plus the technique that bridges storage-level replication with ingestion: Change Data Capture (CDC).

Fundamentals

Categories of data sources

Data engineers typically pull from a handful of recurring source types, each with a different shape, cadence, and failure mode.

Source typeTypical exampleData shapeUpdate pattern
Application databases (OLTP)PostgreSQL/MySQL backing a web appHighly normalized rowsContinuous inserts/updates/deletes
Third-party APIs / SaaS toolsStripe, Salesforce, HubSpot, Google AnalyticsJSON over REST/GraphQLPull on demand, rate-limited
Files dropped in storageCSV/Parquet/JSON in S3, SFTP, GCSFlat files, batch dumpsPeriodic drops (hourly/daily)
Event streams / logsKafka topics, application logs, clickstreamSemi-structured JSON/Avro eventsContinuous, high volume
IoT devices / sensorsTemperature sensors, GPS trackers, industrial equipmentSmall, frequent readingsContinuous, often at the edge
Public / government datasetsCensus bureau, data.gov, WHO, weather agenciesCSV/JSON/APIs, sometimes bulk downloadsInfrequent (monthly/yearly) releases
WebhooksPayment provider callbacks, GitHub events, Slack eventsJSON push notificationsEvent-driven, pushed to you

Application databases (OLTP systems). These are the databases behind the product itself — the tables that record orders, users, and transactions as they happen. They are optimized for fast, small read/write transactions, not for the large scans that analytics requires, which is why data engineers extract from them rather than query them directly for reporting.

Third-party APIs and SaaS tools. Marketing, sales, support, and payments data usually lives outside your own infrastructure, in tools like Salesforce, Stripe, or Zendesk. The only way in is the vendor’s API, which comes with its own schema, pagination scheme, and rate limits (covered below).

Files dropped in storage. A common, low-tech but very real pattern: a partner or internal team exports a CSV/Parquet/JSON file and lands it in an S3 bucket, an SFTP server, or a shared drive on a schedule. Simple to produce, but fragile — file formats, delimiters, and encodings drift silently unless someone owns a schema contract.

Event streams and logs. Application logs, clickstream events, and message queues (Kafka, Kinesis, Pub/Sub) produce a continuous flow of small, semi-structured records. This is the natural source for streaming ingestion.

IoT devices and sensors. Physical devices — industrial sensors, GPS trackers, smart meters — emit small readings frequently, often over constrained networks (MQTT is common here), and often need edge aggregation before they reach the cloud.

Public and government datasets. Census bureaus, open-data portals, and regulatory agencies publish structured datasets, often as bulk downloads or modest APIs, updated on slow cadences (monthly, yearly). Useful for enrichment (e.g., joining zip codes to demographic data) but rarely a primary business source.

Webhooks. Instead of you polling a vendor’s API, the vendor pushes an event to an HTTP endpoint you expose the moment something happens (a payment succeeds, a PR is merged). This inverts the usual pull model and requires your ingestion service to be reliably available to receive pushes — see ./11-streaming-and-realtime-data.md for handling this kind of event-driven ingress.

Understanding data generation before you build

A pipeline is only as reliable as the system generating the data it reads. Before writing a single line of extraction code, it pays to answer three questions about the upstream system:

  1. Schema stability — Does the source team version their API or database migrations? Will a new column silently appear, or will you be told in advance? An OLTP table owned by an actively developed service changes far more often than a slow-moving government dataset.
  2. Volume and traffic patterns — Is data produced at a steady trickle, or in daily bursts (e.g., a nightly batch job in the source system itself)? Ingestion that assumes a steady rate will be surprised by a 100x spike at midnight.
  3. Ownership — Who can you actually talk to when something breaks? An internal OLTP database has an engineering team you can page; a public dataset has, at best, a mailing list. Knowing the ownership boundary up front determines how defensively you need to code your ingestion (e.g., strict schema validation and alerting when the source is unowned or external).

Treating the source as a black box that “just provides data” is the most common root cause of fragile pipelines. A few minutes talking to the team that owns the upstream system — or reading its changelog — saves days of firefighting later.

Data collection considerations

Beyond the technical shape of the data, collecting it responsibly involves a few non-negotiable practicalities:

Key Concepts

Batch, streaming, and micro-batch ingestion

The first major design decision for any ingestion pipeline is when data moves: all at once on a schedule, continuously as it’s produced, or somewhere in between.

Batch ingestion pulls a chunk of data on a schedule — hourly, nightly, weekly. It’s the simplest model to build and reason about: a job runs, extracts what changed (or everything), loads it, and finishes. Tools like Airflow orchestrate this style of pipeline naturally. The tradeoff is latency: data is only as fresh as the last run, so a nightly batch means yesterday’s data is available today.

Streaming (real-time) ingestion processes each record (or micro-record) as it arrives, typically through a message broker like Kafka, Kinesis, or Pub/Sub, feeding a stream processor (Flink, Spark Structured Streaming, ksqlDB). Latency drops to seconds or less, but the operational complexity rises sharply: you now need to think about out-of-order events, exactly-once vs. at-least-once delivery, backpressure, and stateful processing. Full treatment in ./11-streaming-and-realtime-data.md.

Micro-batching is the pragmatic middle ground: instead of processing one event at a time or waiting a full day, you ingest small batches on a short interval — every one to five minutes, for example. This captures most of the freshness benefit of streaming with much of the operational simplicity of batch, and is what tools like Spark Structured Streaming’s default trigger, or a frequently-scheduled Airflow DAG, effectively implement.

Ingestion typeLatencyComplexityTypical trigger
BatchMinutes to hours (or days)LowCron/scheduler (e.g., nightly at 2 AM)
Micro-batchSeconds to minutesMediumShort fixed interval (e.g., every 1–5 min)
StreamingMilliseconds to secondsHighContinuous, event-driven

Choosing between them is a tradeoff between how fresh the business actually needs the data to be and how much operational complexity the team can support. Most analytics use cases (daily dashboards, weekly reports) are well served by batch or micro-batch; fraud detection, real-time recommendations, and operational alerting justify the cost of true streaming.

Change Data Capture (CDC)

Re-extracting an entire source table on every run — a full extraction — is simple but wasteful and slow once a table grows past a few million rows. Change Data Capture (CDC) solves this by capturing only the row-level changes (inserts, updates, deletes) that happened since the last extraction, instead of pulling the whole table again.

There are two common ways to implement CDC:

Log-based CDC reads the database’s internal transaction log — the mechanism the database already uses for its own replication and crash recovery — and turns each logged change into an event. Debezium is the standard open-source tool here: it reads MySQL’s binlog or PostgreSQL’s write-ahead log (WAL) and emits a Kafka event for every insert, update, and delete. Because it taps into a log the database already maintains, log-based CDC:

Query-based CDC instead polls the source table periodically, comparing a updated_at timestamp (or a monotonically increasing ID) to find rows that changed since the last poll. It’s easier to set up — no access to database internals is required — but it has real limitations: it cannot detect hard deletes (a row that vanishes doesn’t show up in a WHERE updated_at > last_poll query), it adds repeated read load to the source database, and it misses any row that changed and changed back between polls.

Log-based CDCQuery-based CDC
Source loadLow (reads log, not tables)Higher (repeated polling queries)
Captures deletesYesNo (without soft-delete flags)
Setup complexityHigher (needs log access, e.g., binlog/WAL enabled)Lower (just SQL access)
Ordering guaranteesStrong (log is ordered)Weak (depends on poll timing)
Typical toolingDebezium, AWS DMS, FivetranCustom SQL polling jobs

For production systems with meaningful write volume, log-based CDC is the preferred approach precisely because it protects the source database from extra load and gives a complete, ordered picture of every change — including deletes that query-based polling would silently miss.

Extract & load mechanics

Full vs. incremental extraction. A full extraction pulls the entire dataset every run; it’s simple and self-healing (any past error is wiped out on the next run) but doesn’t scale well as tables grow. Incremental extraction pulls only new or changed rows since the last run — using a timestamp column, an auto-incrementing ID, or CDC — and is the default choice once a source is large enough that a full reload would be too slow or costly.

Idempotent loads. A core reliability principle: an ingestion job should be safe to re-run. If a job fails halfway through, or is accidentally triggered twice, re-running it must not create duplicate rows or double-count metrics. Common techniques include upserting on a natural or surrogate key (INSERT ... ON CONFLICT DO UPDATE / MERGE) instead of blind appends, writing to a staging table and swapping it in atomically, and deduplicating on a unique event ID before the final write. Idempotency is what lets you retry failed ingestion jobs without fear — a property that’s easy to skip early on and expensive to retrofit later.

Watermarking and checkpointing. To know what to extract next, an incremental pipeline needs to track how far it has already gotten — a watermark (e.g., “last extracted updated_at = 2026-07-18 23:00:00”) or a checkpoint (e.g., a Kafka consumer offset). This state must be stored durably (in a control table, a metadata store, or the streaming platform’s own offset tracking) and updated only after a batch is confirmed to have landed successfully — otherwise a crash between extraction and checkpoint update can silently skip or duplicate data.

APIs as a data source

Ingesting from a third-party REST API involves a set of practical concerns that don’t come up when reading directly from a database:

Handling these three well is most of what separates a fragile “curl in a cron job” script from a reliable ingestion pipeline against a third-party API.

Comparing ingestion patterns

Putting the extraction mechanism and the timing model together gives four practical patterns:

PatternLatencyComplexityBest fit
Full batchHours to daysLowSmall/slow-changing tables, first-time backfills
Incremental batchMinutes to hoursMediumLarge tables with a reliable updated_at/ID column
CDCSeconds to minutesMedium–highLarge OLTP tables needing near-real-time replication, including deletes
StreamingMilliseconds to secondsHighEvent-driven use cases: fraud detection, live dashboards, alerting

In practice, most data platforms use a mix: CDC or incremental batch for OLTP replication into the warehouse, batch pulls for third-party API data that doesn’t change quickly, and streaming reserved for the subset of use cases that genuinely need sub-minute latency. Once data lands, it flows into transformation — see ./08-etl-elt-and-data-pipelines.md — and needs to be checked for correctness and tracked for lineage, covered in ./14-data-quality-governance-and-metadata.md.

Best Practices

References