2025年9月29日 星期一

蝦皮快速搜尋與商品推薦的技術實現:從資料收集到個性化推薦

蝦皮快速搜尋與商品推薦的技術實現:從資料收集到個性化推薦

蝦皮(Shopee)作為東南亞領先的電商平台,每天處理數億次搜尋與推薦請求,能在毫秒內回應用戶輸入(如「無線耳機 1000-2000 元」)並提供個性化商品推薦(如「你可能也喜歡」)。這背後是資料索引、快取、分散式搜尋引擎與推薦演算法的協作。本文深入解析蝦皮的快速搜尋與商品推薦機制,聚焦於如何收集用戶行為資料並實現快速推薦,結合 2025 年的技術趨勢(如 Elasticsearch、Redis 與協同過濾),並提供 PHP/Laravel 程式碼範例,方便你應用於電商專案。內容參考蝦皮等電商平台的公開案例與業界最佳實踐。


1. 核心挑戰與技術架構

挑戰分析

  • 資料規模:蝦皮擁有數億商品,每筆包含名稱、價格、類別、屬性(如顏色、尺寸)與庫存狀態,傳統 SQL 查詢效率低下(O(n) 時間複雜度)。
  • 查詢複雜性:支援多語言關鍵字搜尋(繁中、英文、泰文等)、範圍篩選(價格、評價)、排序(銷量、相關性)與即時庫存檢查。
  • 高併發:雙 11 等促銷活動每秒數十萬請求,需毫秒級響應。
  • 即時推薦:根據用戶瀏覽、搜尋、購買與收藏行為,動態生成個性化商品推薦。
  • 用戶資料收集:需即時追蹤行為(如點擊、加入購物車),並整合歷史數據進行分析。

解決策略

  • 搜尋引擎:Elasticsearch (ES) 提供倒排索引與多維查詢,實現快速搜尋。
  • 快取層:Redis 快取熱門搜尋與推薦結果,降低伺服器負載。
  • 推薦系統:協同過濾(Collaborative Filtering)與基於內容的推薦,結合用戶行為數據。
  • 資料收集:Kafka 異步收集用戶行為,儲存至 MongoDB 或 MySQL。
  • 技術棧:後端 PHP/Laravel,前端 Vue.js/React,搜尋 Elasticsearch,快取 Redis,資料庫 MySQL/PostgreSQL,消息隊列 Kafka。

2. 快速搜尋的實現:Elasticsearch 核心

Elasticsearch 是蝦皮搜尋的核心,透過倒排索引將關鍵字搜尋從 O(n) 降至 O(log n),並支援多語言與範圍查詢。以下是實現細節:

搜尋流程

  1. 資料索引:商品資料(名稱、價格、類別、庫存)從 MySQL 同步到 ES,使用 Logstash 或 PHP 腳本。
  2. 查詢處理:用戶輸入(如「無線耳機」)轉為 ES 查詢,結合 bool query(match、filter)與 multi_match 支援多語言。
  3. 排序與分頁:按銷量、價格或相關性排序,使用 from/size 實現分頁。

PHP 整合範例

以下展示如何用 Laravel 與 Elasticsearch 實現商品搜尋:

php
// Composer: composer require elasticsearch/elasticsearch
use Elasticsearch\ClientBuilder;

class ProductSearchController extends Controller
{
    public function search(Request $request)
    {
        $client = ClientBuilder::create()->setHosts(['elasticsearch:9200'])->build();

        $params = [
            'index' => 'products',
            'body' => [
                'query' => [
                    'bool' => [
                        'must' => [
                            ['multi_match' => [
                                'query' => $request->input('keyword', '無線耳機'),
                                'fields' => ['name^2', 'description', 'category'] // 名稱權重更高
                            ]]
                        ],
                        'filter' => [
                            ['range' => ['price' => ['gte' => 1000, 'lte' => 2000]]],
                            ['term' => ['in_stock' => true]]
                        ]
                    ]
                ],
                'sort' => [['sales' => 'desc'], ['_score' => 'desc']],
                'from' => ($request->input('page', 1) - 1) * 20,
                'size' => 20
            ]
        ];

        $response = $client->search($params);
        return response()->json([
            'results' => $response['hits']['hits'],
            'total' => $response['hits']['total']['value']
        ]);
    }
}
  • 時間複雜度:O(log N),N 為商品數量,基於 B+ 樹索引。
  • 空間複雜度:O(N * M),M 為平均屬性數。
  • 實務應用:蝦皮使用 ES 處理多語言搜尋(如「headphones」「耳機」),並結合庫存檢查確保結果即時性。

3. 收集用戶資料:行為追蹤與儲存

快速推薦依賴於即時收集與分析用戶行為數據(如瀏覽、點擊、加入購物車、購買)。以下是實現方式:

收集流程

  1. 前端追蹤:用 JavaScript SDK(如自訂事件追蹤或 Firebase Analytics)記錄行為(點擊商品、搜尋關鍵字、停留時間)。
  2. 後端接收:Laravel API 接收事件,透過 Kafka 異步儲存,確保高併發處理。
  3. 資料儲存:MongoDB(非結構化行為數據)或 MySQL(結構化訂單數據)。
  4. 即時處理:Redis 儲存短期行為數據(如最近 24 小時瀏覽),供推薦系統快速訪問。

PHP 範例:記錄用戶行為

php
use Predis\Client;
use Illuminate\Support\Facades\DB;

class UserBehaviorController extends Controller
{
    public function track(Request $request)
    {
        $redis = new Client();
        $userId = $request->input('user_id');
        $productId = $request->input('product_id');
        $action = $request->input('action'); // view/click/add_to_cart

        // 儲存到 Redis(短期行為)
        $redis->zIncrBy("user:$userId:actions", 1, "$action:$productId");
        $redis->expire("user:$userId:actions", 86400); // 24 小時過期

        // 異步儲存到 MySQL
        DB::table('user_actions')->insertAsync([
            'user_id' => $userId,
            'product_id' => $productId,
            'action' => $action,
            'created_at' => now()
        ]);

        return response()->json(['status' => 'tracked']);
    }
}
  • 時間複雜度:O(1),Redis zIncrBy 和 MySQL 插入為常數時間。
  • 空間複雜度:O(U * A),U 為用戶數,A 為平均行為數。
  • 實務應用:蝦皮透過類似機制追蹤用戶點擊與購物車行為,結合 Kafka 處理高峰期事件流。

4. 快速商品推薦:協同過濾與快取

推薦流程

蝦皮的商品推薦(如「你可能也喜歡」)主要基於協同過濾(Collaborative Filtering)與基於內容的推薦:

  1. 用戶-商品矩陣:用 Redis Sorted Set 儲存用戶行為(如瀏覽、購買),形成交互矩陣。
  2. 相似度計算:計算用戶間或商品間的相似度(如 Jaccard 相似度),找出相關商品。
  3. 即時推薦:用 Redis 快取 Top-N 推薦結果,結合 ES 過濾已下架商品。
  4. 離線計算:Laravel Job 定期計算相似度矩陣,儲存至 Redis,減少即時計算負擔。

PHP 範例:協同過濾推薦

php
use Predis\Client;

class ProductRecommendationController extends Controller
{
    public function recommend(Request $request)
    {
        $redis = new Client();
        $userId = $request->input('user_id');
        $k = $request->input('limit', 5);

        // 獲取用戶行為
        $userActions = $redis->zRange("user:$userId:actions", 0, -1);
        $similarProducts = [];

        // 協同過濾:找其他用戶也瀏覽/購買的商品
        foreach ($redis->keys('user:*:actions') as $otherUserKey) {
            $otherUserId = str_replace('user:', '', str_replace(':actions', '', $otherUserKey));
            if ($otherUserId == $userId) continue;

            $intersection = $redis->zInter("user:$userId:actions", [$otherUserKey]);
            $similarProducts = array_merge($similarProducts, $intersection);
        }

        // 計數並排序
        $productCounts = array_count_values($similarProducts);
        arsort($productCounts);
        $recommendedIds = array_slice(array_keys($productCounts), 0, $k);

        // 從 ES 獲取商品詳情
        $client = ClientBuilder::create()->setHosts(['elasticsearch:9200'])->build();
        $params = [
            'index' => 'products',
            'body' => [
                'query' => [
                    'terms' => ['_id' => $recommendedIds]
                ]
            ]
        ];
        $results = $client->search($params)['hits']['hits'];

        // 快取推薦結果
        $redis->zAdd("recommend:user:$userId", array_combine($recommendedIds, array_values($productCounts)));
        $redis->expire("recommend:user:$userId", 3600);

        return response()->json(['recommendations' => $results]);
    }
}
  • 時間複雜度:O(U * P),U 為用戶數,P 為平均行為數(Redis 集合交集)。離線計算可降至 O(1) 查詢。
  • 空間複雜度:O(P),儲存交集與推薦結果。
  • 實務應用:蝦皮在商品詳情頁顯示「買這個的人也買」,基於用戶行為與類別相似度。

快取優化

  • Redis Sorted Set:儲存用戶推薦清單,鍵為 recommend:user:$userId,TTL 1 小時。
  • 離線批次:用 Laravel Scheduler 每晚計算相似度矩陣,儲存至 Redis。

5. 資料庫與資料同步

  • 主要資料庫:MySQL 儲存商品與訂單資料,複合索引(name、price、category)加速查詢。
  • 非結構化資料:MongoDB 儲存用戶行為日誌,支援靈活查詢。
  • 同步機制:Kafka 異步將新商品、庫存更新或用戶行為推送到 ES/Redis,確保即時性。

SQL 範例(備援 ES)

sql
CREATE INDEX idx_product_name_price ON products (name, price);
SELECT * FROM products
WHERE price BETWEEN 1000 AND 2000
AND category = 'electronics'
AND in_stock = true
ORDER BY sales DESC
LIMIT 20;

6. 前端與用戶體驗優化

  • 即時搜尋建議:Vue.js/React 透過 AJAX 發送部分輸入(如「耳機」),ES 提供 fuzzy matching 建議(如「無線耳機」「藍牙耳機」)。
  • 動態推薦:在商品詳情頁或首頁,用 AJAX 載入「你可能也喜歡」清單。
  • 無限滾動:ES 的 scroll API 或 from/size 分頁,支援動態載入。
  • 實務案例:蝦皮的搜尋欄支援即時建議,推薦區塊根據瀏覽動態更新。

7. 效能瓶頸與最佳實踐

  • 瓶頸
    • 高併發搜尋:ES 叢集負載過高。
    • 推薦計算:即時協同過濾耗時。
    • 行為收集:高頻事件導致延遲。
  • 解決方案
    • 擴展:AWS OpenSearch 部署多節點 ES 叢集,支援 PB 級資料。
    • 快取:Redis 儲存熱門查詢與推薦,命中率 >90%。
    • 異步:Kafka 處理行為數據,確保高併發。
    • 監控:Kibana 追蹤 ES 延遲,Prometheus 監控 Redis/MySQL。
  • 成本優化:只索引核心欄位(name、price、category),圖片壓縮至 AWS S3。
  • 安全:Laravel Throttle 限制 API 請求,防 SQL 注入與 DDoS。

8. 2025 年趨勢與進階應用

  • AI 增強:用 NLP(如 BERT)解析多語言查詢,提升搜尋精準度。
  • 元搜尋:聚合蝦皮與其他平台(如 PChome)的商品,實現跨平台比較。
  • 即時通知:用 WebSocket 或 LINE API 推送促銷商品,結合 Redis Pub/Sub。

結論與實務建議

蝦皮的快速搜尋與商品推薦系統整合了 Elasticsearch(搜尋)、Redis(快取)、Kafka(行為收集)與協同過濾(推薦)。作為 PHP/Laravel 開發者,你可以:

  1. 快速上手:用 Docker 部署 Elasticsearch + Redis,測試搜尋與推薦。
  2. API 設計:用 Laravel 包裝 ES 查詢與 Redis 快取,提供 RESTful 端點。
  3. 行為追蹤:整合 Kafka 或 RabbitMQ,處理高併發數據。
  4. 學習資源:閱讀《Elasticsearch: The Definitive Guide》、Redis 官方文件與《Designing Data-Intensive Applications》。

沒有留言:

張貼留言

熱門文章