# 資料埋點導論

::: tip 本章要解決的問題
**我們怎麼知道使用者在應用裡做了什麼？**

想像你開了一家線上手搖飲店。你可以站在櫃台後面，親眼觀察每位顧客：他們走進來先看了菜單多久？點了哪款飲品？有沒有猶豫後放棄離開？

但如果你的「店鋪」是一個手機 App 或網站，你無法親眼看到使用者的操作。這時候就需要一種技術手段，在應用的關鍵位置「埋」下記錄點，自動幫你記錄使用者的每一步操作。這就是**資料埋點（Event Tracking）**。

「埋點」這個詞聽起來很專業，但它的核心思路很簡單：**在使用者可能操作的地方，放一個「記錄器」，把使用者做了什麼記下來。**

本章將分四步講解這個過程：

1. **選擇採集方案** — 決定在哪裡放記錄器、怎麼放
2. **設計資料格式** — 決定每條記錄應該包含哪些資訊
3. **傳輸與快取** — 把記錄從使用者手機安全送到伺服器
4. **清洗與入庫** — 整理資料，去掉重複和錯誤，存入資料庫
:::

---

## 第一步：部署點位選擇與採集方案

**目標**：決定用什麼方式來記錄使用者的操作。

舉個例子：產品經理想知道「有多少使用者點擊了購買按鈕」。要回答這個問題，開發者需要在「購買按鈕」的程式碼裡加上一段記錄邏輯 — 每當使用者點擊這個按鈕，就自動記一筆。

但這裡有一個選擇題：我們是**只在重要的地方放記錄器**（比如只記錄「購買」和「註冊」），還是**在所有地方都放記錄器**（記錄使用者的每一次點擊、滑動、停留）？

不同的選擇，對應不同的埋點方案。

<DataTrackingDemo tab="methods" />

**三種主流的埋點方式**

行業中常用的埋點方案有三種，各有優劣：

**方式一：程式碼埋點（Code Tracking）— 手動精確記錄**

開發者在程式碼中手動指定：當使用者做了某個操作時，記錄一條資料。

打個比方：這就像在手搖飲店的收銀台專門安排一個人，只記錄「誰買了什麼、花了多少錢」。記錄的資訊非常詳細和準確。

- *優勢*：可以記錄非常詳細的業務資訊，比如使用者用了哪張優惠券、帳戶餘額是多少
- *代價*：每增加一個新的記錄點，都需要開發者寫程式碼、測試、發布新版本，流程較長

**方式二：視覺化埋點（Visual Tracking）— 點擊圈選記錄**

不需要寫程式碼。系統提供一個視覺化工具，營運人員可以直接在應用介面上「圈選」想要監測的按鈕或區域，系統自動開始記錄。

打個比方：這就像在手搖飲店的監控畫面上，用滑鼠框選「收銀台區域」，系統就自動開始統計這個區域的人流量。

- *優勢*：不需要開發者參與，營運人員自己就能設定，效率很高
- *代價*：只能記錄「使用者點了什麼」這類介面操作，無法記錄「訂單金額」等深層業務資料

**方式三：全埋點（Auto Tracking）— 自動記錄一切**

在應用中整合一個 SDK（可以理解為一個「工具包」），它會自動記錄使用者的所有操作：每一次點擊、每一次滑動、在每個頁面停留了多久。

打個比方：這就像在手搖飲店的每個角落都裝上攝影機，記錄顧客的一舉一動。

- *優勢*：不會遺漏任何操作，覆蓋最全面
- *代價*：資料量非常大，其中很多是無用資訊（比如使用者無意識的滑動），後續需要花大量精力篩選和清理

**本步小結**：選好了埋點方式後，我們的應用就具備了「記錄使用者操作」的能力。

**但這裡有一個新問題**：記錄器雖然能捕獲到使用者的操作，但如果每個記錄器記下來的格式都不一樣（比如有的寫「使用者ID」，有的寫「userID」，有的根本沒記），後續就沒辦法統一分析。所以下一步，我們需要規定一個統一的記錄格式。

---

## 第二步：格式規範設計

**前置條件**：我們已經選好了埋點方式（比如程式碼埋點），應用已經能夠捕獲使用者的操作了。

**本步目標**：規定一個統一的「記錄模板」，讓所有埋點記錄的格式保持一致。

**為什麼需要統一格式？** 想像一下：如果手搖飲店有三個店員同時記錄銷售情況，一個寫「小明買了珍珠奶茶 15 元」，另一個寫「15，奶茶，珍珠」，第三個寫「珍珠奶茶一杯」。到了月底彙總的時候，這些記錄格式完全不同，整理起來會非常痛苦。所以我們需要一張統一的「記錄表」，規定每條記錄必須填寫哪些欄位。

<DataTrackingDemo tab="model" />

**核心原理：4W1H 記錄模板**

無論記錄什麼操作，每條資料都需要回答以下五個問題（簡稱 4W1H）：

**Who — 誰做的？**

我們需要知道這條記錄是哪個使用者產生的。

- 如果使用者已經登入，就用他的帳號 ID（比如 `user_id: "zhangsan123"`）
- 如果使用者沒有登入，就用裝置的唯一識別碼（比如手機的裝置編號），這樣至少能區分「這是同一台手機上的操作」

**When — 什麼時候做的？**

記錄操作發生的精確時間，精確到毫秒。

這裡有一個細節：如果你的應用有海外使用者，台北時間下午 3 點和紐約時間下午 3 點其實差了 13 個小時。為了避免混亂，所有時間統一轉換為 UTC 標準時間（可以理解為「世界統一時間」）。

**Where & How — 在什麼環境下做的？**

這部分記錄使用者操作時的裝置和網路環境，稱為**公共屬性**。之所以叫「公共」，是因為無論使用者做了什麼操作，這些資訊都會自動附帶上去。例如：

- 裝置型號：iPhone 15 / 小米 14
- 網路類型：WiFi / 5G / 4G
- App 版本號：v1.2.3
- 作業系統：iOS 18 / Android 15

這些資訊的價值在於：如果發現某個 Bug 只在特定機型上出現，公共屬性可以幫助快速定位問題。

**What — 具體做了什麼？**

這部分記錄操作的具體業務細節，稱為**自訂屬性**。不同的操作需要記錄不同的資訊。例如：

- 使用者點擊「加入購物車」：需要記錄商品名稱、商品價格、商品數量
- 使用者完成付款：需要記錄訂單金額、付款方式、優惠券編號

**本步小結**：透過 4W1H 模板，我們把使用者的每一個操作都轉化成了一條格式統一的資料記錄。在技術實作中，這條記錄通常以 JSON 格式儲存（JSON 是一種通用的資料格式，上方的互動元件展示了它的樣子）。

**但這裡又有一個新問題**：資料格式統一了，但如果應用的使用者量很大（比如促銷活動期間，每秒鐘可能產生上萬條記錄），使用者手機不可能每產生一條記錄就立刻發送一次 — 這樣既費電又費流量，伺服器也撐不住。所以下一步，我們需要設計一個更聰明的傳輸方式。

---

## 第三步：傳輸機制與快取策略

**前置條件**：使用者的每個操作已經被記錄成了格式統一的 JSON 資料。

**本步目標**：把這些資料從使用者的手機（或瀏覽器）可靠地傳輸到我們的伺服器，即使在網路不好的情況下也不丟資料。

**為什麼不能直接發送？** 如果每產生一條記錄就立刻發一次網路請求，就像每寫一封信就跑一趟郵局一樣 — 效率太低了。更合理的做法是：攢一批信，一次性送過去。

<DataTrackingDemo tab="pipeline" />

**核心原理：資料傳輸的三道保障**

資料從使用者手機到伺服器，需要經過三道保障機制，確保既高效又不丟資料：

**第一道：攢一批再發（批量聚合）**

SDK（埋點工具包）不會每產生一條記錄就發送一次，而是先把記錄暫存在手機記憶體裡。當攢夠一定數量（比如 30 條），或者等待超過一定時間（比如 5 秒），再把這一批資料打包，一次性發送出去。

這就像寄快遞：你不會買一件東西就跑一趟快遞站，而是攢幾件一起寄，省時省力。對手機來說，這樣做能減少網路請求次數，省電省流量。

**第二道：斷網也不丟（本地儲存）**

使用者在電梯裡、捷運隧道中，手機經常沒有網路訊號。如果資料只存在記憶體裡，使用者一關閉 App，資料就沒了。

所以 SDK 會把還沒發送的資料存到手機的本地儲存中（類似於把信先放進抽屜）。等網路恢復後，再自動把這些資料補發出去。這樣即使使用者短暫斷網，資料也不會遺失。

**第三道：伺服器不被壓垮（訊息佇列）**

資料到達伺服器後，並不會直接寫入資料庫。為什麼？因為在促銷活動等高峰期，可能每秒有幾萬條資料同時湧入，資料庫如果直接處理這麼大的量，可能會崩潰。

解決方案是在中間加一個「緩衝區」，技術上叫**訊息佇列**（常用的工具叫 Kafka）。它的作用就像餐廳的取號排隊系統：高峰期顧客（資料）先排隊等候，廚房（資料庫）按自己的節奏一個一個處理，不會被同時湧入的訂單壓垮。

**本步小結**：透過「攢一批再發 → 斷網本地儲存 → 訊息佇列緩衝」這三道保障，資料已經安全抵達了伺服器。

**但還有一個問題**：因為斷網重連後會自動補發資料，同一條記錄有可能被發送了兩次。如果不處理就直接存入資料庫，資料就會重複（比如一筆 100 元的訂單被記成了兩筆，營收就虛高了）。所以下一步，我們需要對資料進行「清洗」。

---

## 第四步：資料清洗與入庫流程

**前置條件**：資料已經透過傳輸管道安全抵達伺服器。

**本步目標**：在資料正式存入資料庫之前，先做一次「健檢」— 去掉重複的、修復格式有問題的，確保最終儲存的資料乾淨、準確。

**為什麼需要清洗？** 就像收到一箱快遞後，你需要檢查一下：有沒有重複發貨的？有沒有發錯的？有沒有包裝破損的？資料也是一樣，直接存入資料庫之前，需要先檢查和整理。

這個過程在技術上叫做 **ETL**，是三個英文單字的縮寫：
- **E**xtract（提取）：從訊息佇列中取出資料
- **T**ransform（轉換）：檢查和修復資料格式
- **L**oad（載入）：把清洗好的資料寫入資料庫

<DataTrackingDemo tab="overview" />

**核心原理：清洗資料的兩個關鍵動作**

**動作一：去重 — 去掉重複的記錄**

前面提到，斷網重連後 SDK 會自動補發資料，這可能導致同一條記錄被發送了多次。怎麼識別哪些是重複的？

方法很簡單：在客戶端打包資料時，給每條記錄分配一個全球唯一的編號（叫做 `dedup_id`，類似於快遞單號）。伺服器在儲存資料前，先檢查這個編號是否已經存在 — 如果已經存在，說明是重複資料，直接丟棄。

**動作二：校驗與格式統一 — 修復不規範的記錄**

應用會不斷更新版本，不同版本的埋點程式碼可能存在細微差異。比如：

- 舊版本把使用者 ID 欄位命名為 `userId`，新版本改成了 `user_id`
- 某些記錄的時間戳記明顯不合理（比如顯示為 1970 年）
- 某些欄位的值無法識別

在這一步，系統會編寫轉換規則來統一處理這些問題：欄位名不一致的統一對齊，時間戳記異常的記錄予以丟棄，無法識別的值標記為 `unknown`。

**本步小結**：經過去重和格式校驗後，資料以乾淨、統一的形式寫入**資料倉儲**（一種專門用於儲存和分析大量資料的資料庫，常見的有 ClickHouse、Hive 等）。資料分析師可以直接用 SQL 語句查詢這些資料，獲得可靠的分析結果。

---

## 完整流程回顧

以下是資料埋點從採集到入庫的四步流程總結：

| 步驟 | 做了什麼 | 得到了什麼 | 還剩什麼問題 |
|------|----------|-----------|-------------|
| **1. 選擇採集方案** | 決定用哪種方式記錄使用者操作 | 應用具備了記錄能力 | 各記錄器的資料格式不統一 |
| **2. 設計資料格式** | 用 4W1H 模板統一記錄格式 | 每條記錄都是標準的 JSON | 使用者量大時逐條發送撐不住 |
| **3. 傳輸與快取** | 攢批發送、斷網儲存、佇列緩衝 | 資料安全抵達伺服器 | 重試可能導致資料重複 |
| **4. 清洗與入庫** | 去重、校驗、格式統一 | 乾淨的資料存入資料倉儲 | — |

---

## 結語

當使用者在應用中點擊一個按鈕時，表面上看只是一個瞬間的動作。但在這背後，一條完整的資料鏈路已經開始運轉：

1. 埋點程式碼捕獲到這次點擊，按照 4W1H 模板產生一條標準記錄
2. 記錄被暫存在手機本地，攢夠一批後統一發送到伺服器
3. 伺服器透過訊息佇列平穩接收，再經過去重和格式校驗
4. 最終，一條乾淨、準確的資料被寫入資料倉儲

這就是資料埋點的完整過程。它把使用者分散的、看不見的操作行為，轉化成了可以查詢、可以分析的結構化資料。產品經理可以據此了解使用者喜歡什麼功能、在哪裡流失；營運人員可以評估活動效果；開發者可以定位問題出現在哪個版本。

這套「採集 → 建模 → 傳輸 → 清洗」的體系，是資料驅動決策的基礎設施。
