需要實現的功能:
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 為我們提供了一套更簡單的方案,來看官方說明:
swoole 默認進程間通信都是基于 unix socket 的,他的性能如下:
這樣一來和中間件的鏈接耗時和傳輸耗時全部可以省掉,投遞和消費都是基于內存的無 io 操作
3、進程消費的時候需要入庫,雖然 swoole 的 mysql io 已經全部協程化、因為我這里是多表檢驗難免需要查詢校驗后再進行入庫,所以這里開了協程并發入庫,使用 hyperf utils 里面的 parallel 設置并發數為 10,這樣也快很多
4、進程消費完通知的問題我們可以通過監聽 OnFinish 事件,進程導入結束后返回已完成條數和總條數,就可得知進度,讓 webscoket server 主動向 client 推送進度
實現效果
這里沒分塊是因為我默認是按 100 分塊的,我的表里沒那么數據,就沒分塊
來看看 9145 條數據 cpu 的調度率
可以看到,因為均分到四個不同 task worker 緣故,cpu 調用不會只在一個進程上而是在多個進程均衡調度?。?/p>
浙公網安備 33010602011771號