前言
數據導出,這可以說是一個隨處可見的需求,大部分管理平台,報表系統都會有這個需求。
對於這個需求,不少系統會做限制,只能從系統導出幾千或幾萬的數據,再多的話就要提申請,經過層層審批,到 DB 那邊的團隊處理。
其實走不走申請,很大程度上是取決於公司的規章制度,大部分應該還是沒有特別完善的,都是做在系統里,有權限的可以導出所有數據。
說實話,老黃也一直沒搞懂,為什么有些人老是想着導出幾十萬,幾百萬的數據在那里看,過濾,篩選。。。
不過有需求,終究還是要滿足的,像下面這種幾百 MB 的 CSV 文件,是很經常看的見的。。。
常見問題
導出大文件時,一般都會遇到 帶寬 和 內存 的問題。
帶寬
如果說,供下載的文件,是放在我們的服務器上面,那么下載的時候是會占用我們的流出帶寬。
這個在帶寬比較小的情況下是很容易占滿。
針對帶寬問題,最好的辦法就是不占用業務系統的帶寬,這個需要引入第三方雲存儲,比如阿里雲的 OSS,騰訊雲的COS。這樣提供的下載鏈接是雲存儲的鏈接,這樣就和業務系統隔離了。
內存
在生成文件時,如果沒有考慮到內存的情況,一次性把數據放到內存里面,就很容易占用大量的服務器內存。
不限制站點占用的內存,很容易影響服務器上面的其他站點。
限制了站點占用的內存,容易達到限制引發站點重啟,從而影響正常的訪問。
針對內存問題,就是避免一次性把數據全部都放在內存里面,可以分批處理。
下面再來看看一個具體的數據導出方案。
具體方案
這個方案會有有 5 個角色參與,用戶,后台系統,中間件,導出系統和雲存儲。
大體如下圖所示:
這里老黃把它粗略的分解成10個步驟。
1. 提交導出申請
用戶想導出某些內容的時候,需要在后台系統里面提交申請。
2. 生成導出批次
后台系統接收到用戶提交的申請后,給這個申請生成一個批次號,同時把導出什么內容,什么查詢條件記錄下來。
內容這一個可以存儲查詢的方法名,查詢條件可以存儲方法參數的 JSON 字符串。
這樣在導出數據那一步時可以通過反射處理。
當然還少不了時間,人,狀態這些基本信息了。
把這些信息入庫,這一步就算 OK 了。
3. 發送導出批次到中間件
這一步涉及到中間件的選取問題,一般會建議選擇 MQ 或 Redis。
發送的內容最簡單的就是一個批次號就可以了,當然想把批次的其他信息組裝一起發過去也是 OK 的。
4. 提交申請成功
當批次信息成功發送到中間件后,就可以認為系統已經接收了這個申請,這個時候就可以提示用戶申請成功了。
5. 讀取導出批次信息
導出系統這一塊其實還是有很多設計的點的。導出系統這一塊最好是能獨立服務器部署,避免對應用服務器產生級聯影響。
導出系統要監聽中間件里面的批次信息,當收到批次信息后,它就要開始干活了。這個活分兩類:
一類是,如果這個導出系統是 中心樞紐,只負責 調度 的話,它的活就是分配給具體的 worker 節點去執行后續的操作,好比創建一個 k8s 的任務。
另一類是,這個導出系統就是一個 worker 節點,就是負責執行后續內容的。
如果導出任務不是很頻繁的話,導出系統 === worker 節點就可以了,也不要過度設計。
6. 查詢/生成/加密文件
這一步才是真正的執行導出的操作。
有了批次信息就可以知道用戶要導出什么東西,根據這個就去執行查詢操作,然后把查詢的結果生成對應格式的文件。
因為所有的文件,后面都是要上傳到雲存儲,安全起見的話,需要加一個密碼,避免所有人下載后都能打開。
文件有可能不是只生成一個,可能會按天按月切分,所以最好把文件放進壓縮包里面。
這樣在雲存儲上面都是帶密碼的壓縮包。
7. 上傳到雲存儲
生成好文件后,就需要把文件上傳到雲存儲里面。這里建議有條件的要走內網去上傳,不然 NAT 網關或服務器的帶寬容易打滿。
8. 組裝下載地址
上傳成功后要根據參數拼接好這個文件的下載地址。
拿到下載地址后,還需要進行有效期處理,即這個下載地址會包含着它的過期時間,什么時間之后就不能再訪問了。相關雲存儲都提供了對應的方法,所以這一步會比較簡單。
一般來說,一個文件會保留 3 ~ 7 天左右的有效期,更久的話就是一個月了。不排除有土豪公司永久保存。
設置有效期主要有幾個考慮
業務上,不可避免在短時間內會有人導出相同內容,在有效期范圍內可以復用這一個,避免重復生成。
資源上,雲存儲的價格,雖然不會特別貴,但還是要省着點,會定期清理一些不需要的歷史文件
9. 回填信息
這一步其實就是把處理之后的下載地址、完成時間、批次狀態等信息更新回批次信息里面
10. 查詢導出申請並下載
到這一步之后,用戶一般會收到站內信,告訴用戶已經可以進行下載操作了,當用戶點下載的時候,跳到雲存儲的地址,等待下載完成就可以了。
寫在最后
數據導出,雖然說是一個相對不那么起眼的功能,但想做到比較好的體驗,也是要花點心思弄一弄的。
這里介紹的這個方案是實際在用的了,不過只列出了比較粗的內容,還有一些細節內容就不再展開了,好比同一個用戶連續導出相同的內容等等。