百萬級資料匯入(hyperf+xlswriter+task+websocket)

晏南風發表於2023-04-12

需要實現的功能:
1、匯入excel檔案,10w條資料或者更多
2、進行入庫操作
可能涉及多張表
需要進行多表資料校驗(updateOrCreate)
需要保證多張表資料一致(transaction)
3、前端實時顯示入庫進度

實現思路:

將資料進行分塊然後分配到不同程式進行數資料庫匯入操作,每個task worker完成後會觸發onfinsh方法,監聽該事件透過websocket進行進度通知

可能遇到的問題:

檔案太大了,一下撈到記憶體,記憶體會爆炸
資料分塊投遞程式和程式消費的問題
程式消費完通知的問題
資料庫寫入阻塞導致匯入非常慢

解決方案

1、xlsx的讀取分幾種模式,全量讀取和遊標讀取,選擇遊標讀取耗費的記憶體是非常小的,然後可以根據讀取數量進行一次處理

while ($res = $this->xlsObj->nextRow($_dataType)) {
    $data[] = $res;
    $count++;
    if ($count % 10000 == 0) {
        //回撥資料插入的方法
        $closure($data);
        unset($data);
    }
}

2、關於進行投遞和消費問題,如果在傳統fpm專案中,一般會選擇訊息中介軟體,先把訊息推送到中介軟體,然後再多程式消費,但是一般投遞訊息是越小越好,都需要經過序列化處理、然後程式消費在進行反序列化,swoole為我們提供了一套更簡單的方案,來看官方說明:

百萬級資料匯入(hyperf+xlsx+task+websocket)
swoole預設程式間通訊都是基於unix socket的,他的效能如下:

百萬級資料匯入(hyperf+xlsx+task+websocket)
這樣一來和中介軟體的連結耗時和傳輸耗時全部可以省掉,投遞和消費都是基於記憶體的無io操作

3、程式消費的時候需要入庫,雖然swoole的mysql io 已經全部協程化、因為我這裡是多表檢驗難免需要查詢校驗後再進行入庫,所以這裡開了協程併發入庫,使用 hyperf utils 裡面的 parallel 設定併發數為 10,這樣也快很多

4、程式消費完通知的問題我們可以透過監聽OnFinish事件,程式匯入結束後返回已完成條數和總條數,就可得知進度,讓webscoket server 主動向 client 推送進度

實現效果

百萬級資料匯入(hyperf+xlsx+task+websocket)
這裡沒分塊是因為我預設是按100分塊的,我的表裡沒那麼資料,就沒分塊:laughing:

技術棧

task_ipc_mode的問題

  • 訊息佇列模式使用作業系統提供的記憶體佇列儲存資料,未指定 mssage_queue_key 訊息佇列 Key,將使用私有佇列,在 Server 程式終止後會刪除訊息佇列。
  • 指定訊息佇列 KeyServer 程式終止後,訊息佇列中的資料不會刪除,因此程式重啟後仍然能取到資料
本作品採用《CC 協議》,轉載必須註明作者和本文連結

相關文章