← 返回部落格
·6 min 閱讀

USJ 等待時間即時看板:資料管線設計與實作

資料工程ETL即時資料專案紀錄

為什麼要做這個專案

去過大阪環球影城的人都知道,排隊是無可避免的一環。雖然官方 App 有提供等待時間,但使用體驗並不理想——你必須點進每個設施才能看到時間,無法一目瞭然。

我們想做的是一個簡潔的看板頁面,讓使用者可以在同一個畫面上快速掌握所有設施的等待狀況,方便規劃遊園路線。

系統架構概覽

整個系統分為四層:

  1. 資料蒐集層:定期從資料源取得最新的等待時間
  2. 資料處理層:清洗、標準化並儲存時間序列資料
  3. API 層:提供 RESTful API 給前端查詢
  4. 前端展示層:即時顯示各設施的等待時間

資料蒐集

我們每 5 分鐘執行一次資料蒐集任務,將最新的等待時間寫入資料庫。蒐集程式用 Python 撰寫,部署在 Docker 容器中,由 cron job 觸發。

每次蒐集的資料包含:

  • 設施名稱(日文 / 英文)
  • 目前等待時間(分鐘)
  • 設施運作狀態(營運中 / 暫停 / 維護中)
  • 資料取得時間戳記

資料處理與儲存

原始資料進來後,需要經過幾道處理:

設施名稱標準化:同一個設施可能有不同的名稱變體(例如「ハリー・ポッター」和「Harry Potter」)。我們建立了一個對照表,將所有變體映射到統一的識別碼。

異常值偵測:偶爾會出現不合理的等待時間(例如負數或超過 500 分鐘),這些需要被標記並過濾。

時間序列儲存:我們使用 PostgreSQL 儲存歷史資料,每筆紀錄包含設施 ID、等待時間和時間戳記。這樣不僅能提供即時資料,還能做歷史趨勢分析。

CREATE TABLE wait_times (
    id SERIAL PRIMARY KEY,
    ride_id VARCHAR(50) NOT NULL,
    wait_minutes INTEGER NOT NULL,
    status VARCHAR(20) NOT NULL,
    recorded_at TIMESTAMP WITH TIME ZONE NOT NULL,
    created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
);

CREATE INDEX idx_wait_times_ride_recorded
    ON wait_times (ride_id, recorded_at DESC);

API 設計

API 提供兩個主要端點:

  • GET /api/rides/current:取得所有設施的最新等待時間
  • GET /api/rides/:id/history?date=YYYY-MM-DD:取得特定設施的歷史資料

回傳格式設計得盡量精簡,減少傳輸量:

{
  "rides": [
    {
      "id": "harry-potter",
      "name": "哈利波特禁忌之旅",
      "wait": 75,
      "status": "operating",
      "updatedAt": "2026-03-01T10:05:00+09:00"
    }
  ],
  "parkStatus": "open",
  "lastSync": "2026-03-01T10:05:00+09:00"
}

前端即時更新

前端使用 Next.js 開發,頁面載入時先透過 Server Components 取得初始資料(避免白畫面),之後在客戶端透過輪詢機制每 60 秒更新一次。

為什麼用輪詢而不是 WebSocket?因為等待時間的更新頻率不高(每 5 分鐘),使用 WebSocket 反而會增加伺服器的連線維護成本。對這個場景來說,簡單的輪詢就夠了。

資料洞察

累積了一段時間的歷史資料後,我們發現了一些有趣的模式:

  • 開園後第一個小時是等待時間最短的黃金時段,大部分設施的等待時間不超過 20 分鐘
  • 下午 1 點到 3 點是等待高峰,熱門設施平均要等 90 分鐘以上
  • 平日與假日的差異巨大:假日的平均等待時間是平日的 2-3 倍
  • 季節性活動期間(如萬聖節、聖誕節)整體等待時間會上升 30-50%

這些洞察也被整合到前端,以圖表的方式呈現給使用者參考。

維運經驗

監控

我們設定了幾個關鍵指標的告警:

  • 資料蒐集失敗超過 3 次連續
  • API 回應時間超過 2 秒
  • 資料新鮮度超過 15 分鐘(表示蒐集可能中斷)

資料庫管理

時間序列資料會持續增長,我們設定了自動清理機制,只保留最近 90 天的詳細資料。更早的資料會被聚合為每小時的平均值,大幅節省儲存空間。

結語

這個專案雖然功能看似簡單——就是顯示等待時間——但背後涉及的資料工程環節一點都不少。從穩定的資料蒐集、即時的處理管線,到高效的前端呈現,每一個環節都需要精心設計。

對我們來說,這也是一次很好的實踐:用資料工程的思維,解決日常生活中的小痛點。