1. 程式人生 > >創業公司做資料分析(二)運營資料系統

創業公司做資料分析(二)運營資料系統

  作為系列文章的第二篇,本文將首先來探討應用層中的運營資料系統,因為運營資料幾乎是所有網際網路創業公司開始做資料的起點,也是早期資料服務的主要物件。本文將著重回顧下我們做了哪些工作、遇到過哪些問題、如何解決並實現了相應的功能。

早期資料服務

  產品上線開始推廣後不久,後臺研發人員便會經常收到運營同事的私信:“能不能查一下有多少使用者註冊了,來自哪裡?……..”。幾次之後,大家便覺得這樣的效率太低了:研發人員需要在繁忙的開發任務中抽時間來做資料查詢、統計,而運營同事則需要等很久才能拿到資料。於是,大家開始協商更好的方法,最終達成一致:由運營同事提供所需的資料模板,後臺研發人員根據模板將資料匯入Excel檔案,運營同事可根據自身需求自己分析統計。這便是早期的資料服務了,其組成結構如下圖所示。


  這樣的做法簡單明瞭,後臺研發人員根據資料模板寫一個Python指令碼,從業務資料庫中將資料撈出來,做些分析、整合後,將結果輸出到一個Excel檔案,然後傳送郵件通知運營同事接收檔案。然而,隨著需求的增加和細化、資料量的增加,暴露的問題越來越多,這裡先羅列出來,這些問題有的會在本文提出解決方案,有的則會在後面的文章中陸續提出解決方案。

  • Worker越來越多,分佈在多個地方,存在很多重複的勞動和程式碼,某個邏輯的修改需要改很多檔案。
  • 由於使用ORM來訪問資料庫,很多程式碼只考慮邏輯,沒考慮到查詢資料的效率問題,導致有些報告需要跑十幾個小時才能出結果(在迴圈查詢資料的效能問題及優化
    一文有講解)。
  • 中間計算結果流失,資料沒有共享,每個Worker都要跑自己的邏輯去算一遍。
  • Woker依靠crontab來控制觸發,沒有監管,經常由於髒資料導致中斷,需要等到運營同事發現後報過來才知道。

運營資料Dashboard

  隨著業務的發展,以資料報表的形式來提供資料服務逐漸不能滿足需求了。一方面,高層期望每天一早便能看到清晰的資料,搞清楚最近的運營效果和趨勢;另一方面,雖然資料報表提供了詳細的資料,但是還是需要手動去過濾、統計一下才有結果,所有想看資料的人都需要做一遍才行,而業務人員處理Excel的水平層次不齊。
  於是,我們開始籌劃Dashboard系統,以Web的形式提供資料視覺化服務。可是,Dashboard要做成什麼樣子?由於產品經理和設計人員都忙於產品業務,所以只能自己考慮要做什麼、怎麼做。好在筆者之前用過百度統計,對那裡面的一些統計服務比較清楚,結合公司的業務,形成了一些思路:

  • 資料內容上,包含:核心指標資料和圖表分析兩部分。前者以曲線圖為主,要能快速顯示數量和趨勢,比如註冊日增量趨勢圖;後者使用各種圖表來展現某個時間段內的分析結果,比如10月份的TOP10使用者感興趣品牌。
  • 資料型別上,包含:C端核心指標、B端核心指標、核心分析和專題活動指標與分析。前兩者是分別針對C端和B端的指標資料,核心分析是一些綜合的分析,比如轉化率分析,專題活動是針對一些特定的大型運營活動。
  • 資料維度上,包含:時間維度、城市維度和B端品牌維度。時間是最基本最重要的維度,城市維度可以分析各個運營大區的狀態,B端品牌維度主要是針對B端上的業務。

  整理後便形成了下圖所示的Mockup(簡化版),基本涵蓋了上述的思路。雖然在美觀上相對欠缺,但是畢竟是內部使用嘛,重要的資料顯示要能準確、快速。


  搞清楚了要做什麼,接下來就是要將想法落地,考慮如何實現了。

整體架構

  系統的整體架構如下圖所示,主要基於這麼幾點考慮:

  • 前後端分離。前端只負責載入圖表、請求資料並顯示,不做任何資料邏輯處理;後端負責產出資料,並提供REST API與前端互動。
  • 離線與實時計算並存。為了提高資料獲取的速度,曲線指標資料採用離線計算的方式,提供歷史資料供前端展示;圖表分析類資料採用實時計算的方式,其速度取決於所選時間段內的資料量,必要時進行快取。

前端實現

  Dashboard系統的前端並不複雜,前面也提到我們不會做太多樣式上的工作,重點是資料的顯示。那麼,第一件事就是要尋找一款圖表庫。筆者這裡選擇的是,其提供了豐富的圖表型別,視覺化效果很棒,對移動端的支援很友好,重要的是有詳細的示例和文件。事實證明ECharts確實很強大,很好的滿足了我們的各種需求。
  選好了圖表庫,接下來的問題是如何優雅的載入幾十個圖表,甚至更多。這就需要找到圖表顯示共性的地方(行為和屬性),進行抽象。通常,使用ECharts顯示一個數據圖表需要四步(官方文件):第一步,引入ECharts的JS檔案;第二步,宣告一個DIV作為圖表的容器;第三步,初始化一個echart例項,將其與DIV元素繫結,並初始化配置項;第四步,載入圖表的資料並顯示。可以發現,行為上主要分為初始化和更新資料兩個,屬性上主要是初始配置項和資料。
  基於此,筆者使用“Pattern+Engine”的思想來實現前端資料載入。首先,在JS中使用JSON對每個圖表進行配置,即寫Pattern。例如,下面的配置便對應了一個圖表,elementId是DIV的id,title是圖表的標題,names是圖表的曲線名稱,url提供了獲取資料的API,loader表示要載入的圖表Engine。而一個頁面的圖表便由一組這樣的配置項組成。

{
        elementId: 'register_status_app_daily',
        title: 'App註冊統計(日增量)',
        names: ['使用者數'],
        url: '/api/dashboard/register_status_daily/',
        loader: 'line_loader'
}

  頁面載入時,根據Pattern中的配置項生成相應的Loader Engine例項,用來初始化圖表和更新資料。每個Loader對應一個ECharts圖表型別,因為不同圖表型別的初始化和載入資料的方法不同。程式類圖如下所示。

後端實現

  前面提到在早期的資料服務中,存在很多重複勞動和程式碼,因此在Dashboard系統的後端實現中,筆者開始考慮構建資料分析的公共庫,這塊佔據了很大一部分工作量。底層公共庫不針對任何特殊業務需求,主要負責三件事:第一,封裝資料來源連線方法;第二,封裝時間序列的生成方法,產生以天、周、月為間隔的時間序列;第三,封裝基礎的資料查詢、清洗、統計、分析方法,形成格式化的資料,這部分是最重要的。
  完成了底層公共庫的構建後,整個程式碼結構一下子就清爽了很多。在其基礎上,開始構建上層的Analyzer。Analyzer用於完成具體的資料分析需求,每個Analyzer負責一個或多個數據指標的產出,每個曲線圖/圖表的資料由一個Analyzer來負責。離線計算與實時計算,則是分別在Schedule和Web請求的觸發下,呼叫對應的Analyzer來完成資料產出。因此,整個後臺系統分為三層來實現,如下圖所示。


  最後談一談離線資料的問題。目前離線計算是由Schedule來觸發,每日零點計算前一日的資料,資料按照“每個指標在不同維度上每天一個數據點”的原則來生成,由上述的Analyzer來負責產出格式化的資料,存入MongoDB中。由於查詢規則簡單,只需建立一個組合索引就可以解決效率問題了。目前資料量在500W左右,暫時沒有出現效能問題,後期可以考慮將部分歷史資料遷移,當然這是後話。

資料報表

  Dashboard上線後,我們開始考慮將早期的資料報表服務逐步停下來,減少維護的成本。而運營同事希望能繼續保留部分報表,因為Dashboard雖然提供了很多資料指標和分析,但是有些工作需要更精細的資料資訊來做,比如給帶來微信註冊的校園代理結算工資、對新註冊使用者電話回訪等等。經過一番梳理和協商,最終保留了六個資料報表。另一方面,B端的商家期望能在後臺匯出自己的相關資料。綜合兩方面需求,筆者構建了新的資料報表系統。


  新的資料報表系統,按照流程來劃分為三部分:觸發、執行與通知。內部資料報表依舊由Schedule觸發,啟動相應的Worker程序來執行;而提供給外部的報表由Web前端通過REST API來觸發,將相應的任務加入Celery任務佇列中執行。執行體由一組Exporter來完成,Exporter負責獲取資料、生成適合寫入Excel的資料格式、寫Excel檔案,資料獲取部分依賴前面所述的底層公共庫。最後,統一發送郵件通知。
  考慮到早期資料服務中經常遇到異常導致生成報表失敗的問題,筆者在新的資料報表系統中做了兩點與異常相關的處理:

  • 使用Airflow對Schedule觸發的任務進行監控(後續文章會有詳細介紹),手動觸發的任務則由Celery進行監控,遇到異常便傳送郵件通知到開發人員。
  • 如果一個Excel資料檔案由多個Sheet組成,當某個Sheet出現異常時,通常由兩種處理方法:一是丟棄整個檔案,二是保留其他Sheet資訊繼續生成Excel檔案。這裡,內部報告使用了第二種處理方法,外部報告相對嚴謹,使用了第一種。


  以上便是筆者所在公司的運營資料系統的發展歷程和現狀,目前Dashboard與資料報表兩個系統已經趨於穩定,基本提供了90%以上的運營資料服務。當然,隨著資料量的增長、業務需求的發展,一定會面臨更多新的挑戰。