基於MongoDB的KOC項目實戰分享
這篇文章是小夥伴寫的,主要分享MongoDB應用實戰,MongoDB是MySQL的有效補充,也能作爲大數據分析的橋樑,個人是非常看好,在很多場景下可以好好利用,具體文章可以見文末[原文連接]。
前段時間做的一個KOC項目,時間有點久遠今天通過寫文章的方式重新梳理一下。項目目標主要是用來統計KOL用戶的粉絲數和文章的曝光閱讀以及點贊收藏等數據的每日增長趨勢。
實現方案
PHP+MongoDB
實現步驟
1、導入日誌
目前日誌系統分爲服務端日誌和客戶端日誌分爲兩個日誌文件存儲,本項目源數據是客戶端日誌文章曝光數據以及服務端日誌裏的文章閱讀、粉絲取消關注數據。
這裏有兩個腳本,分別把客戶端和服務端前一日的原始數據導入到mongodb中,這裏遇到過導入速度效率不高的問題在下面會介紹。
2、分析mongodb中的原始數據
第一步導入日誌處理完成後,會把mongo中的原始數據以用戶維度和文章維度進行分別處理,統計出用戶的粉絲和文章的數據增長趨勢。
3、接口查詢
根據用戶ID查詢mongo中處理後的數據返回
遇到的問題
1、導入日誌時的插入問題
因爲把操作mongo的各種方法是自己封裝的,以便和PHP操作mysql的封裝寫法一致。封裝mongo的時候只寫了單條數據的插入,其實這種方式在沒有索引大數據量插入的情況下效率也不會下降的太厲害,有索引特別是有多個索引效率就會看到明顯差別,最後加入了批量插入。
每個索引佔據一定的存儲空間,在進行插入,更新和刪除操作時也需要對索引進行操作。所以,如果你很少對集合進行讀取操作,建議不使用索引。由於索引是存儲在內存(RAM)中,你應該確保該索引的大小不超過內存的限制。如果索引的大小大於內存的限制,MongoDB會刪除一些索引,這將導致性能下降。
和mysql一樣,有些查詢是不能被查詢使用的,例如:正則表達式及非操作符,如 $nin, $not, 等;算術運算符,如 $mod等。
下面是數據不同方式插入的代碼:
單條插入:
try { $this->mgoBulk = new \MongoDB\Driver\BulkWrite; $this->mgoBulk->insert($data); $result = $this->mgoSDK->executeBulkWrite($this->db, $this->mgoBulk, $this->writeConcern); return $result->getInsertedCount(); } catch (\MongoDB\Driver\Exception\BulkWriteException $e) { return $e->getMessage(); }
批量插入:
try { $this->mgoBulk = new \MongoDB\Driver\BulkWrite; foreach ($data as $key => $val){ $this->mgoBulk->insert($val); } $result = $this->mgoSDK->executeBulkWrite($this->db, $this->mgoBulk, $this->writeConcern); return $result->getInsertedCount(); } catch (\MongoDB\Driver\Exception\BulkWriteException $e) { return $e->getMessage(); }
批量插入的優點是隻用請求一次數據庫。
2、查詢
$mongolog = new \Library\MongoSDK($config, $initStatus, 'koc_log'); $result = $mongolog->select(['xwj_event' => 'delFollow','xwj_date' => $date], []); 封裝的方法:/** * @desc 查詢 * @param array $query 查詢條件 * @param array $fields 結果集返回的字段, array():表示返回所有字段 array('id'=>1, 'name'=>0):表示返回字段"id", 不返回字段"name" * @param array $sort 排序字段, array('id'=>1):表示按id字段升序 array('id'=>-1):表示按id字段降序 array('id'=>1, 'age'=>-1):表示按id升序後再按age降序 * @param int $limit 取多少條記錄 * @param int $skip 跳過多少條(從多少條開始) 可用於分頁 * @return mixed[] */ public function select($query = [], $fields = [], $sort = [], $limit = 0, $skip = 0){ $options = []; //指定返回哪些字段 1 表示返回 0表示不返回 if ($fields) { $options['projection'] = $fields; } //指定排序字段 if ($sort) { $options['sort'] = $sort; } //指定返回的條數 if($limit > 0){ $options['limit'] = $limit; } //指定起始位置 if($skip > 0){ $options['skip'] = $skip; } $query = new \MongoDB\Driver\Query($query, $options); $cursor = $this->mgoSDK->executeQuery($this->db, $query); $result = []; foreach ($cursor as $val) { $result[] = $this->_parseArr((array)$val); } return $result; } 查詢很簡單和mysql查詢基本類似。
3、聚合運算
部分例子:
$command = new \MongoDB\Driver\Command([ 'aggregate' => 'apilogs', 'pipeline' => [ ['$match' => ['add_date'=>['$gte' => '2020-04-28', '$lte' => '2020-05-12']]], ['$group' => ['_id' => ['api' => '$api', 'version' => '$version'], 'count' => ['$sum' => 1]]], ['$project' => ['version' => '$_id.version', 'api' => '$_id.api', 'count' => 1]], ['$sort' => ['count' => -1]], ], 'cursor' => new stdClass, ]); try { $cursor = $mgoSDK->executeCommand($mgodb, $command); } catch(\MongoDB\Driver\Exception $e) { //return []; } $data = []; foreach ($cursor as $dv) { $data[] = [ 'api' => $dv->api, 'version' => $dv->version, 'count' => $dv->count, ]; }
這段代碼是統計一段時間內API不同版本的調用數量。
較常用的關鍵字:
-
$project:修改輸入文檔的結構。可以用來重命名、增加或刪除域,也可以用於創建計算結果以及嵌套文檔。
-
$match使用MongoDB的標準查詢操作。
-
$limit:用來限制MongoDB聚合管道返回的文檔數。
-
$skip:在聚合管道中跳過指定數量的文檔,並返回餘下的文檔。
-
$group:將集合中的文檔分組,可用於統計結果。
-
$sort:將輸入文檔排序後輸出。
mongodb雖然支持事務,但官方建議能不用就不用,所以,在你的項目中應用時,要注意這點。
但是mongodb提供了許多原子操作,比如文檔的保存,修改,刪除等,都是原子操作。所謂原子操作就是要麼這個文檔保存到Mongodb,要麼沒有保存到Mongodb,不會出現查詢到的文檔沒有保存完整的情況。