當前位置:成語大全網 - 書法字典 - MongoDB應用程序1-日誌分析

MongoDB應用程序1-日誌分析

在線服務會產生大量的運行和訪問日誌,其中包含壹些信息,如錯誤、警告和用戶行為。通常情況下,服務會以文本的形式記錄日誌信息,可讀性強,方便日常定位問題。但是當產生大量日誌時,需要對數據進行進壹步的存儲和分析,才能從大量日誌中挖掘出有價值的內容。

摘要:以web服務的訪問日誌為例,介紹了如何使用MongoDB存儲和分析日誌數據,從而最大化日誌數據的價值。本文的內容也適用於其他日誌存儲應用。

典型的web服務器的訪問日誌類似於以下,包括訪問源、用戶、訪問的資源地址、訪問結果、用戶使用的系統和瀏覽器類型等。

存儲這些日誌的最簡單方法是將每個日誌存儲在壹個單獨的文檔中。MongoDB中每個日誌的存儲方式如下:

雖然上面的模型可以解決日誌存儲的問題,但是分析這些數據比較麻煩,因為MongoDB不擅長文本分析。更好的方法是在MongoDB的文檔中存儲壹行日誌之前提取每個字段的值。如下所示,上面的日誌被轉換成包含許多字段的文檔。

同時,在這個過程中,如果覺得有些字段對數據分析沒有幫助,可以直接過濾掉,減少存儲消耗。例如,數據分析不會關心用戶信息、請求和狀態信息,這些字段不需要存儲。ObjectId本身包含時間信息,所以不需要存儲單獨的時間字段(當然帶時間也是有好處的,可以更好的表示請求產生的時間,查詢語句也更方便編寫,盡量選擇占用存儲空間少的數據類型)。基於上述考慮,上述日誌的最終存儲內容可能類似於以下內容:

日誌存儲服務需要能夠同時支持大量的日誌寫入。用戶可以定制writeConcern來控制日誌的寫入能力,例如以下定制方法:

同時,為了達到最佳的寫入效率,用戶還可以考慮批量寫入的方式,壹次網絡請求寫入多個日誌。格式如下:

db.events.insert([doc1,doc2,...])

當日誌以上述方式存儲在MongoDB中時,可以根據各種查詢需求查詢日誌。

q _ events = db . events . find({ ' path ':'/Apache _ Pb . gif ' })

如果這種查詢非常頻繁,可以為path字段建立壹個索引來提高查詢效率:

db . events . create index({ path:1 })

通過索引時間字段,可以加速這種查詢:

db . events . create index({ time:1 })

同樣,用戶也可以使用MongoDB的aggregation和mapreduce框架來做壹些更復雜的查詢分析,並且應該盡量建立合理的索引來提高查詢效率。

當日誌寫服務節點越來越多時,日誌存儲服務需要保證可擴展的日誌寫能力和海量的日誌存儲能力。此時,需要使用MongoDB分片在多個分片中擴展和存儲日誌數據。關鍵問題是分片密鑰的選擇。

使用時間戳進行分段(如ObjectId類型的_id或時間字段)有以下問題:

根據_id字段進行哈希分片,可以將數據和寫入均勻分布到所有分片上,寫入能力會隨著分片數量的增加而線性增加。但這種方案的問題是數據離散不規則。所有範圍查詢(常用於數據分析)都需要在所有分片上進行搜索,然後合並查詢結果,這影響了查詢效率。

假設上述場景中路徑字段的分布比較均勻,很多查詢都是按照路徑維度劃分的,那麽可以考慮按照路徑字段對日誌數據進行分段,這樣有以下優點:

缺點是:

當然,以上缺點是有辦法改善的。該方法是在分片密鑰中引入壹個附加因子。比如原來的shard key是{path: 1},引入附加因子後變成:

{path: 1,ssk: 1}其中ssk可以是隨機值,比如_id的哈希值,也可以是時間戳,這樣相同的路徑就按時間排序。

這樣做的效果是分段鍵的值分布比較豐富,不會出現特殊個數的單值。上述分片方式各有利弊,用戶可根據實際需要選擇方案。

碎片化方案可以提供海量數據存儲支持,但是隨著數據越來越多,存儲成本會不斷上升。通常很多日誌數據都有壹個特點,日誌數據的價值是隨著時間遞減的。比如1年前甚至3個月前的歷史數據,根本沒有分析價值,可以把這部分保存下來降低存儲成本,在MongoDB中有很多方法可以支持這種需求。

MongoDB的TTL索引可以支持文檔在壹定時間後自動過期和刪除。例如,上面的日誌時間字段表示生成請求的時間。如果為此字段建立了TTL索引,文檔將在30小時後自動刪除。

db . events . create index({ time:1 },{ expireAfterSeconds:108000 })

如果對日誌存儲的時間沒有嚴格要求,但對總存儲空間有限制,可以考慮使用capped collection來存儲日誌數據。指定最大存儲空間或文檔數量。當達到閾值時,MongoDB將自動刪除capped集合中最舊的文檔。

db.createCollection("event ",{capped: true,size: 104857600000}

例如,在每個月的月底,用當前月份的名稱重命名events集合,然後創建壹個新的events集合進行寫入。例如,2016的日誌最終將存儲在以下12集合中:

當需要清理歷史數據時,直接刪除相應的集合:

缺點是,當時如果要查詢多個月的數據,查詢語句會稍微復雜壹點,需要查詢多個集合的結果進行合並。