Dữ liệu thời gian thựcReal-time data

Dữ liệu tươi đến đâu là do khâu chậm nhất quyết định.Data is only as fresh as the slowest link.

Một stream job trễ 158 ms trở nên vô nghĩa nếu trình duyệt 30 giây mới hỏi lại một lần. Trang này đặt hai quyết định cạnh nhau: dữ liệu được xử lý thế nào, và được đưa tới người dùng thế nào.A 158 ms stream job is pointless if the browser only asks again every 30 seconds. This page puts two decisions side by side: how data is processed, and how it is delivered to users.

Stream
ảnh: 158 msimage: 158 ms
Batch
ảnh: 24 himage: 24 h
WebSocket
SSE
Long-polling
Polling

Độ trễ điển hình trên thang log, từ 10 mili giây đến 2 ngày. Ghép một hàng tầng xử lý với một hàng tầng phân phối: người dùng chịu độ trễ của thanh nằm xa bên phải hơn. Bấm vào tên để xem chi tiết.Typical latency on a log scale, from 10 milliseconds to 2 days. Pair one processing row with one delivery row: users get the delay of whichever bar sits further right. Tap a name for details.

Trước hết: có khi bạn không cần gì cảFirst: you may need none of this

Độ trễ của chuỗi cộng dồn qua từng khâu. Nếu không có khâu nào cần nhanh, phương án rẻ nhất thường thắng.Latency adds up across the chain. If no link needs to be fast, the cheapest option usually wins.

Sự kiệnEvent Xử lýProcessing Phân phốiDelivery Màn hìnhScreen

độ trễ đầu-cuối = thu thập + xử lý + phân phốiend-to-end latency = ingestion + processing + delivery

Không cần pipeline hay realtime khiYou need no pipeline or real-time when

  • Dữ liệu đủ nhỏ để tính lúc người dùng mở trang. SQL tổng hợp trên vài triệu dòng có index thường dưới 1 giây.Data is small enough to compute on page load. Aggregate SQL over a few million indexed rows usually runs under 1 second.
  • Người dùng chấp nhận bấm Làm mới.Users are fine pressing Refresh.
  • Dữ liệu đổi chậm hơn tần suất người ta nhìn vào nó.Data changes more slowly than people look at it.
  • Không hành động nào thay đổi nếu số đến sớm hơn vài phút.No action changes if the number arrives minutes earlier.

Tính lúc đọc, cache HTTP 60 giây, thêm nút Làm mới.Compute on read, cache for 60 seconds, add a Refresh button.

Dấu hiệu đã vượt quá mức nàySigns you've outgrown it

  • Truy vấn lúc đọc vượt quá 1 giây, hoặc nhiều người xem làm database quá tải.Read queries exceed 1 second, or viewers overload the database.
  • Có yêu cầu ghi độ tươi bằng con số, như "cảnh báo trong 10 giây".A requirement states freshness as a number, like "alert within 10 seconds".

Chọn cái nào trong 30 giâyChoose in 30 seconds

Hai câu hỏi riêng cho hai tầng. Màu của đáp án cho biết ai quyết định khi nào dữ liệu di chuyển.Two separate questions for two layers. The answer's colour shows who decides when data moves.

Tầng xử lýProcessing

Kết quả có mất giá trị nếu đến trễ hơn vài phút?Does the result lose value if it's more than a few minutes late?

  • KhôngNo

    Dữ liệu nhỏ, truy vấn dưới 1 giây?Small data, query under 1 second?

  • CóYes

    Chấp nhận trễ vài giây đến 1 phút?Is a few seconds to a minute acceptable?

    • CóYesMicro-batch
    • KhôngNoStream

Tầng phân phốiDelivery

Người dùng cần thấy thay đổi mà không phải bấm gì?Must users see changes without doing anything?

  • KhôngNoBaseline
  • CóYes

    Client cũng gửi dữ liệu liên tục, cần nhanh cả hai chiều?Does the client also send often, fast both ways?

    • CóYesWebSocket
    • KhôngNo

      Dữ liệu đổi thưa và ít người xem?Rare changes and few viewers?

      • CóYesPolling
      • KhôngNo

        Hạ tầng cho phép giữ kết nối streaming?Does your infrastructure allow streaming connections?

Kiểm tra cuối: hai đáp án phải cùng cấp độ trễ. Đừng ghép stream 150 ms với polling 30 giây.Final check: both answers must sit at the same latency scale. Don't pair a 150 ms stream with 30-second polling.

Tầng xử lýThe processing layer

Dữ liệu thô được biến thành kết quả theo lịch, hay theo từng sự kiện.Raw data becomes results on a schedule, or event by event.

Batch processing

Đồng hồ quyết địnhThe clock decides
nguồnsourceclient
Ảnh: 24 giờ một lần, 1 file mỗi ngàyImage: every 24 h, 1 file a day

Gom dữ liệu thành một tập có giới hạn. Đến giờ, một job đọc hết, tính, ghi kết quả rồi tắt.Collect data into a bounded set. On schedule, one job reads it all, computes, writes the result and exits.

Chọn batch là đổi độ tươi của dữ liệu lấy chi phí thấp và sự chính xác dễ đạt.Choosing batch trades data freshness for low cost and correctness that's easy to achieve.

Dùng choUse for

  • Báo cáo doanh thu, KPI hằng ngàyDaily revenue and KPI reports
  • Đối soát, tính lương cuối kỳReconciliation and payroll
  • Huấn luyện lại model mỗi đêmNightly model retraining

ĐượcGain

  • Chỉ tốn tiền lúc chạy: job 20 phút chiếm 1,4% một ngàyPay only while running: a 20-minute job is 1.4% of a day
  • Thấy đủ dữ liệu nên dễ đúngSees all the data, so it's easy to get right
  • Có lỗi thì sửa code rồi chạy lạiFix bugs by rerunning

MấtLose

  • Trễ tối thiểu bằng chu kỳ, có khi gần 26 giờMinimum delay is the cycle, up to ~26 hours
  • Job hỏng lúc 2 giờ sáng là sáng ra không có sốA 2 a.m. failure means no numbers by morning
  • Không phản ứng được với từng sự kiệnCan't react to individual events

Dấu hiệu nhận biếtHow to recognise it Không ai hành động khác đi nếu con số đến sớm hơn vài giờ.Nobody would act differently if the number arrived a few hours earlier.

Stream processing

Sự kiện quyết địnhThe event decides
nguồnsourceclient
Ảnh: trễ 158 ms, job luôn chạyImage: 158 ms latency, always on

Xử lý dòng dữ liệu không có điểm kết thúc, từng sự kiện ngay khi tới. Job chạy liên tục và giữ trạng thái.Process a never-ending flow, each event as it arrives. The job runs continuously and keeps state.

Chọn stream là đổi chi phí vận hành và độ phức tạp lấy kết quả tươi tính bằng giây.Choosing stream trades operating cost and complexity for results that are seconds fresh.

Dùng choUse for

  • Chặn gian lận thẻ trước khi duyệtBlocking card fraud before approval
  • Dashboard vận hành, cảnh báo IoTOperational dashboards, IoT alerts
  • Giá động, tồn kho thời gian thựcDynamic pricing, live inventory

ĐượcGain

  • Trễ từ mili giây đến vài giâyMilliseconds to seconds of latency
  • Tải trải đều, không có đỉnh nửa đêmEven load, no midnight spike
  • Event log cho phép replay và nhiều consumerEvent log allows replay and many consumers

MấtLose

  • Trả tiền và trực sự cố 24/7Paying and on-call 24/7
  • Dữ liệu muộn, trùng, sai thứ tựLate, duplicate and out-of-order data
  • State lớn, đổi logic phải giữ tương thíchLarge state; logic changes must stay compatible

Dấu hiệu nhận biếtHow to recognise it Giá trị của kết quả giảm mạnh sau vài giây đến vài phút.The result loses most of its value after seconds to minutes.

Tầng phân phốiThe delivery layer

Kết quả đến trình duyệt bằng cách nào. Trong sơ đồ: chấm là sự kiện ở nguồn, mũi tên là lúc client nhận được, nét đứt là thời gian dữ liệu nằm chờ.How results reach the browser. In the diagrams: dots are events at the source, arrows are when the client receives them, dashed lines are time the data sits waiting.

Polling

Đồng hồ quyết địnhThe clock decides
nguồnsourceclient
Ảnh: 4 request, 1 cập nhậtImage: 4 requests, 1 update

Client hỏi "có gì mới không?" theo chu kỳ cố định. Server trả lời ngay, có hay không có thay đổi.The client asks "anything new?" on a fixed cycle. The server answers immediately, change or not.

Chọn polling là đổi request lãng phí và độ trễ tối đa bằng một chu kỳ lấy sự đơn giản tuyệt đối.Choosing polling trades wasted requests and up to one cycle of delay for complete simplicity.

Dùng choUse for

  • Trạng thái job xuất file, đơn hàngExport job or order status
  • Kết quả build CI trên trang nội bộCI build results on an internal page
  • Tỷ giá, thời tiết vài phút đổi một lầnRates or weather that change every few minutes

ĐượcGain

  • HTTP thường, qua mọi proxy và CDNPlain HTTP, passes every proxy and CDN
  • Không trạng thái, mở rộng ngang dễStateless, scales horizontally
  • ETag và 304 gần như không tốn băng thôngETag and 304 cost almost no bandwidth

MấtLose

  • Phần lớn request vô íchMost requests are wasted
  • 10.000 client hỏi mỗi 2 giây là 5.000 req/s10,000 clients every 2 s is 5,000 req/s
  • Tốn pin trên di độngDrains mobile batteries

Dấu hiệu nhận biếtHow to recognise it Trễ vài giây đến vài phút vẫn ổn và số người xem cùng lúc không lớn.A delay of seconds to minutes is fine and concurrent viewers are few.

Long-polling

LaiHybrid
nguồnsourceclient
Ảnh: 2 request, 2 cập nhậtImage: 2 requests, 2 updates

Client gửi request, server giữ lại tới khi có dữ liệu hoặc hết giờ, trả lời, rồi client gửi lại ngay.The client sends a request; the server holds it until there's data or a timeout, answers, and the client resends at once.

Chọn long-polling là đổi một kết nối treo trên mỗi client lấy độ trễ gần tức thời mà vẫn ở trong HTTP thuần.Choosing long-polling trades one parked connection per client for near-instant latency in plain HTTP.

Dùng choUse for

  • Chat khi môi trường chặn WebSocketChat where WebSocket is blocked
  • Hàng đợi: AWS SQS chờ tối đa 20 giâyQueues: AWS SQS waits up to 20 s
  • Telegram Bot API getUpdatesTelegram Bot API getUpdates

ĐượcGain

  • Trễ gần như pushNear-push latency
  • Ít request vô ích hơn polling nhiềuFar fewer wasted requests than polling
  • Vẫn là HTTP thuầnStill plain HTTP

MấtLose

  • Mỗi client chiếm một kết nối treoOne parked connection per client
  • Không có cursor là mất sự kiện trong khoảng hởWithout a cursor, events in the gap are lost
  • Bị proxy cắt nếu giữ quá 60 giâyProxies cut holds longer than 60 s

Dấu hiệu nhận biếtHow to recognise it Cần độ trễ thấp nhưng chỉ được dùng HTTP request/response thuần.You need low latency but may only use plain HTTP request/response.

Server-Sent Events

Sự kiện quyết địnhThe event decides
nguồnsourceclient
Ảnh: 1 request, 5 sự kiệnImage: 1 request, 5 events

Một response HTTP không bao giờ đóng. Server đẩy sự kiện dạng văn bản xuống, một chiều.One HTTP response that never closes. The server pushes text events down, one way.

Chọn SSE là bỏ kênh ngược từ client lên server để lấy sự đơn giản của HTTP và cơ chế nối lại có sẵn.Choosing SSE gives up the client-to-server channel for HTTP's simplicity and built-in reconnection.

Dùng choUse for

  • Feed thông báo, tỷ số, bảng giáNotification feeds, scores, tickers
  • Stream token câu trả lời của LLMStreaming LLM response tokens
  • Thanh tiến trình, dashboard liveProgress bars, live dashboards

ĐượcGain

  • Trình duyệt tự nối lại với Last-Event-IDBrowser auto-reconnects with Last-Event-ID
  • Dùng được cookie, auth, HTTP/2Cookies, auth and HTTP/2 just work
  • Gỡ lỗi bằng curl -NDebug with curl -N

MấtLose

  • Một chiều, chỉ văn bản UTF-8One way, UTF-8 text only
  • HTTP/1.1 giới hạn ~6 kết nối mỗi domainHTTP/1.1 caps ~6 connections per domain
  • EventSource không đặt được header AuthorizationEventSource can't set an Authorization header

Dấu hiệu nhận biếtHow to recognise it Dữ liệu chỉ chảy từ server xuống; client cần gửi gì thì một request thường là đủ.Data only flows down; when the client must send, a normal request is enough.

WebSocket

Sự kiện quyết địnhThe event decides
nguồnsourceclient
Ảnh: 1 lần bắt tay, hai chiềuImage: 1 handshake, both ways

Bắt tay bằng HTTP Upgrade, server trả 101, sau đó là kênh hai chiều trên một kết nối TCP (RFC 6455).An HTTP Upgrade handshake, a 101 reply, then a two-way channel over one TCP connection (RFC 6455).

Chọn WebSocket là gánh hạ tầng có trạng thái và một khối việc tự xây để lấy kênh hai chiều nhanh nhất.Choosing WebSocket means carrying stateful infrastructure and self-built plumbing for the fastest two-way channel.

Dùng choUse for

  • Chat, game nhiều người chơiChat, multiplayer games
  • Soạn thảo cộng tácCollaborative editing
  • Màn hình giao dịch vừa nhận giá vừa đặt lệnhTrading screens that stream prices and place orders

ĐượcGain

  • Hai chiều, trễ thấp nhất trong bốn cáchTwo-way, lowest latency of the four
  • Mỗi frame chỉ 2–14 byte headerOnly 2–14 bytes of header per frame
  • Gửi nhị phân trực tiếpNative binary data

MấtLose

  • Tự xây reconnect, heartbeat, resumeYou build reconnect, heartbeat, resume
  • Có trạng thái: triển khai làm rớt kết nối, cần pub/subStateful: deploys drop connections, needs pub/sub
  • Một số firewall doanh nghiệp chặnSome corporate firewalls block it

Dấu hiệu nhận biếtHow to recognise it Client cũng gửi dữ liệu liên tục và độ trễ của cả hai chiều đều quan trọng.The client also sends data constantly and latency matters both ways.

Bảng đánh đổiTrade-off table

Cột in đậm là trục hay bị bỏ qua nhất: hệ thống tốn gì khi không có gì xảy ra.The bold column is the most overlooked axis: what the system costs when nothing is happening.

Khái niệmConceptAi quyết định khi nàoWho decides whenĐộ tươi điển hìnhTypical freshnessTốn gì khi không có gì xảy raCost when nothing happensĐộ khó vận hànhOperational difficultyPhục hồi khi lỗiRecovery
BatchĐồng hồ quyết địnhThe clock decidesVài giờ đến 1 ngàyHours to a dayGần như không, job đã tắtAlmost nothing, job has exitedThấpLowChạy lại jobRerun the job
StreamSự kiện quyết địnhThe event decidesMili giây đến vài giâyMilliseconds to secondsCluster chạy 24/7A cluster running 24/7CaoHighKhôi phục checkpoint, replay logRestore checkpoint, replay log
PollingĐồng hồ quyết địnhThe clock decidesTrung bình nửa chu kỳHalf a cycle on averageRequest đều đặn theo chu kỳSteady requests every cycleRất thấpVery lowRequest kế tiếp tự sửaNext request fixes it
Long-pollingLaiHybridGần tức thờiNear-instantMột kết nối treo mỗi client, vòng lại mỗi ~30 giâyOne parked connection per client, resent every ~30 sTrung bìnhMediumGửi lại kèm cursorResend with cursor
SSESự kiện quyết địnhThe event decidesTức thời, một chiềuInstant, one-wayMột kết nối mở mỗi client, ping định kỳOne open connection per client, periodic pingThấp đến trung bìnhLow to mediumTrình duyệt tự nối lạiBrowser reconnects itself
WebSocketSự kiện quyết địnhThe event decidesTức thời, hai chiềuInstant, two-wayMột kết nối mở mỗi client, heartbeatOne open connection per client, heartbeatCaoHighTự xâyBuild it yourself

Cặp ghép hợp lý: batch với baseline hoặc polling chậm; stream với SSE cho dashboard; stream với WebSocket khi người dùng cũng tạo ra sự kiện.Sensible pairings: batch with the baseline or slow polling; stream with SSE for dashboards; stream with WebSocket when users also create events.

Sai lầm thường gặpCommon mistakes

Phần lớn đến từ việc chọn một tầng mà quên tầng còn lại, hoặc quên rằng proxy sẽ cắt kết nối im lặng.Most come from choosing one layer and forgetting the other, or forgetting that proxies cut silent connections.

Sai lầmMistakeVì sao saiWhy it is wrongSửa thế nàoFix
Dựng Kafka và Flink cho dashboard chỉ xem mỗi sángKafka and Flink for a dashboard viewed once each morningTrả tiền 24/7 cho độ tươi không ai dùngPaying 24/7 for freshness nobody usesBatch, hoặc tính lúc đọcBatch, or compute on read
Stream 150 ms nhưng trình duyệt poll mỗi 30 giâyA 150 ms stream, but the browser polls every 30 sĐộ trễ đầu-cuối bằng khâu chậm nhấtEnd-to-end latency equals the slowest linkDùng SSE, hoặc hạ stream xuống micro-batch cho khớpUse SSE, or relax the stream to micro-batch
WebSocket cho luồng một chiều như thông báo, bảng giáWebSocket for one-way flows like notifications or tickersTự xây reconnect và mở rộng hạ tầng có trạng thái, trong khi không cần kênh ngượcBuilding reconnects and stateful scaling with no need for a return channelSSESSE
Hàng chục nghìn client poll mỗi giâyTens of thousands of clients polling every secondTải tăng tuyến tính, phần lớn request vô íchLoad grows linearly, mostly wastedSSE hoặc long-polling; nếu giữ polling: tăng chu kỳ, thêm ETag, dừng khi tab ẩnSSE or long-polling; if polling stays: longer interval, ETags, pause hidden tabs
Long-polling không có cursorLong-polling without a cursorSự kiện rơi vào khoảng hở giữa hai request bị mấtEvents in the gap between requests are lostGửi ID sự kiện cuối, server trả mọi thứ kể từ đóSend the last event ID; the server returns everything since
Kết nối dài không có heartbeatLong-lived connections without a heartbeatProxy cắt im lặng sau 60 giây, client tưởng vẫn kết nốiProxies cut them silently after 60 s while the client thinks it's connectedPing 15–30 giây một lầnPing every 15–30 s
Tin rằng exactly-once của stream là tuyệt đốiBelieving stream exactly-once is absoluteSink không idempotent vẫn ghi trùng khi job khởi động lạiA non-idempotent sink still duplicates on restartUpsert theo khoá, cộng batch đối soátUpsert by key, plus batch reconciliation
Batch chạy đúng 00:00Running batch at exactly 00:00Bỏ sót sự kiện đến muộn của hôm trướcMisses the previous day's late eventsChạy trễ 1–2 giờ, hoặc chạy lại ngày D-1Run 1–2 hours later, or rerun day D-1

Chỗ ảnh gốc nói chưa chuẩnWhere the source images are off

  • Ảnh 1 ghi stream là "Latency over completeness". Chỉ đúng một phần: watermark và allowed lateness cho phép chờ dữ liệu muộn rồi cập nhật lại. Thứ phải chọn là chờ bao lâu. Hai con số 148.316 và 148.331 trong ảnh chính là kết quả tạm thời và kết quả cuối.Image 1 labels stream "Latency over completeness". Only partly true: watermarks and allowed lateness let a job wait for late data and revise. The real choice is how long to wait. The image's 148,316 and 148,331 are the provisional and final results.
  • Độ trễ batch "24h" là chu kỳ. Độ trễ thật bằng chu kỳ cộng thời gian chạy cộng thời gian chờ dữ liệu muộn, có thể gần 26 giờ.The "24h" batch latency is the cycle. Real latency is cycle plus runtime plus waiting for late data, up to about 26 hours.
  • Ảnh 2 gọi cả bốn cách là push. Polling và long-polling thực chất là pull, vì client luôn khởi tạo request.Image 2 calls all four methods push. Polling and long-polling are really pull, since the client always initiates.
  • "spork" trong ảnh 1 là lỗi chữ của ảnh do AI tạo; ý đúng là Apache Spark."spork" in image 1 is a text artifact of the AI-generated image; it means Apache Spark.

Ba câu cần nhớThree things to remember

  1. Người dùng thấy dữ liệu tươi đến đâu là do khâu chậm nhất quyết định. Chọn tầng xử lý và tầng phân phối cùng nhau.How fresh data looks is set by the slowest link. Choose processing and delivery together.
  2. Câu hỏi đầu tiên: đồng hồ hay sự kiện quyết định khi nào? Mặc định là đồng hồ, cho tới khi có con số chứng minh ngược lại.First question: does the clock or the event decide when? Default to the clock until numbers prove otherwise.
  3. Cần hai chiều thì WebSocket, một chiều thì SSE, dữ liệu ít đổi thì polling vẫn đúng.Two-way needs WebSocket, one-way needs SSE, and rarely changing data is still a job for polling.