MongoDB時間戳轉(zhuǎn)日期及日期分組實例代碼
前言
最近遇到的一個數(shù)據(jù)統(tǒng)計折線圖的性能優(yōu)化點,可以說是一定思維上的轉(zhuǎn)變,就記錄下咯
背景:cron定時任務(wù)讀取當(dāng)前統(tǒng)計數(shù)據(jù)的異常值,頻率為每五分鐘記錄一次,折線圖要求獲取每日的異常項峰值
最一開始的想法:將數(shù)據(jù)讀取到內(nèi)存中進(jìn)行條件過濾、計算
首先根據(jù)時間戳將數(shù)據(jù)以日期作為分組,其次在每個分組中獲取異常項的峰值數(shù)據(jù),時間復(fù)雜度O(n*n),最好以日期分組列表+峰值數(shù)據(jù)列表作為對象返回結(jié)果
遇到性能問題:一天的數(shù)據(jù)量為(60/5)*24
=288,默認(rèn)日期為15天,則統(tǒng)計的數(shù)據(jù)量為4230,接口返回甚至需要8、9秒的時間,作為一個項目的門面折線圖,這種情況 達(dá)咩!
優(yōu)化的念頭:我要拿每天的峰值數(shù)據(jù),怎么才能直接取到每天的峰值呢,mongo的聚合是不是可以做到??? $group可以按日期做分組, $max可以拿到最大值,接下來一個 $sort好像是就成了吧! 說干就干??!
接下來的聚合語句均為mongo pipeline,最后附上golang的bson條件哈
// ResultCountModel _ type ResultCountModel struct { CommonBase `json:",inline" yaml:",inline" bson:",inline"` ErrorCount int `json:"error_count" bson:"error_count"` Timestamp int64 `json:"timestamp" bson:"timestamp"` MaxTime int64 `json:"max_time" bson:"max_time"` }
數(shù)據(jù)結(jié)構(gòu)定義如上,這里使用CommonBase
,是因為在$group聚合后會得到_id
唯一標(biāo)識字段,因此便于獲取最后的聚合結(jié)果,在定義結(jié)構(gòu)體時將其加上;timestamp
單位為毫秒
1、日期篩選
第一步,毫無疑問,對時間戳timestamp
進(jìn)行日期的過濾
{ $match: { timestamp: { $gte: 1671897600000, // min_timestamp $lt: 1673280000000 // max_timestamp } } }
$gte
大于等于$lt
小于
2、日期轉(zhuǎn)換
第二步,根據(jù)時間戳大小進(jìn)行日期的轉(zhuǎn)換,這里是用的是$project, 將具有請求字段的文檔傳遞到管道中的下一階段。指定的字段可以是輸入文檔中的現(xiàn)有字段或是新計算的字段
使用$project主要思路是,將timestamp
時間戳轉(zhuǎn)換為標(biāo)準(zhǔn)日期,之后輸出為想要的format形式;同時使用 $project保留需要的字段
時間戳轉(zhuǎn)換日期
核心方法:$dateToString
{ $dateToString: { date: <dateExpression>, format: <formatString>, timezone: <tzExpression>, onNull: <expression> } }
date
:要轉(zhuǎn)換的字符串日期,必須是解析為Date、Timestamp、ObjectID 的有效表達(dá)式format
: 日期格式規(guī)范timezone
:運算結(jié)果的時區(qū),常用UTC偏移量onNull
: date為null或缺失時要返回的值
日期格式想要“月份-日期”,那format: “%m-%d”
日期數(shù)據(jù)這里,如果直接使用輸入文檔中的現(xiàn)有字段的話 date: “$timestamp”,則會報錯:PlanExecutor error during aggregation :: caused by :: can’t convert from BSON type long to Date
因此我們需要將時間戳轉(zhuǎn)換為日期: 即格林威治開始時間(1970-01-01 00:00:00)+時間戳+時差
date:{ $add:[ new Date(0), "$timestamp", 28800000 ] },
注意??:
- MongoDB時間的基本單位為毫秒,所以本文直接使用”$timestamp”即可;若時間單位為秒級時,則需要使用 $multiply進(jìn)行乘法運算:{ $multiply:[" $timestamp”,1000]}
- MongoDB是UTC時區(qū),即中時區(qū)(0度經(jīng)線), 中國為東八區(qū),因此需要使用timezone添加8小時(即28800000毫秒)
pipeline如下:
day:{ $dateToString:{ format:"%m-%d", date:{ $add:[ new Date(0), "$timestamp", 28800000 ] }, } },
保留需要字段
/** * specifications: The fields to * include or exclude. */ { timestamp:1, error_count:1, }
$project將保留字段置為1即可進(jìn)行數(shù)據(jù)保留操作
第二步完整pipeline如下:
{ $project: { day: { $dateToString: { format: '%m-%d', date: { $add: [ ISODate('1970-01-01T00:00:00.000Z'), '$timestamp', 28800000 ] } } }, timestamp: 1, error_count: 1 } },
3、日期分組
第三步,使用$group進(jìn)行日期分組
{ $group: { _id: <expression>, // Group key <field1>: { <accumulator1> : <expression1> }, ... } }
_id
: 表達(dá)式指定組密鑰field
: 計算使用的累加器運算符
這里我們需要將第二步獲得的日期轉(zhuǎn)換進(jìn)行分組聚合,同時獲取每個分組的異常項最大值即峰值數(shù)據(jù)
{ $group: { _id: '$day', error_count: { $max: '$error_count' }, max_time: { $max: '$timestamp' } } },
這里額外獲取了max_time
字段,主要用于在計算統(tǒng)計數(shù)據(jù)時的排序,在最后排序部分會使用到
4、日期排序
這里做一個假設(shè),如果不使用max_time
的話,如何將數(shù)據(jù)進(jìn)行按日期的排序呢? 如果根據(jù)_id
進(jìn)行排序,則會出現(xiàn)“上年末”排序在“下年初”的情況(感謝現(xiàn)在的??,不然會忘記這個問題)
所以將每個分組的最大時間戳保留下來時很有必要的!
這里取$max $min都是可以的哈
{ $sort: { max_time: 1 } }
最終完整pipeline:
[{ $match: { timestamp: { $gte: 1671897600000, $lt: 1673280000000 } } }, { $project: { day: { $dateToString: { format: '%m-%d', date: { $add: [ ISODate('1970-01-01T00:00:00.000Z'), '$timestamp', 28800000 ] } } }, timestamp: 1, error_count: 1 } }, { $group: { _id: '$day', error_count: { $max: '$error_count' }, max_time: { $max: '$timestamp' } } }, { $sort: { max_time: 1 } }] [{ $match: { timestamp: { $gte: 1671897600000, $lt: 1673280000000 } } }, { $project: { day: { $dateToString: { format: '%m-%d', date: { $add: [ ISODate('1970-01-01T00:00:00.000Z'), '$timestamp', 28800000 ] } } }, timestamp: 1, error_count: 1 } }, { $group: { _id: '$day', error_count: { $max: '$error_count' }, max_time: { $max: '$timestamp' } } }, { $sort: { max_time: 1 } }]
在golang里面,Aggregate則直接使用pipeline即可,亦可轉(zhuǎn)換為filter使用
filter代碼:
filter := bson.A{ bson.D{{"$match", bson.D{ {"timestamp", bson.D{ {"$gte", param.MinTimestamp}, {"$lt", param.MaxTimestamp}, }}}}, }, bson.D{{"$project", bson.D{ {"day", bson.D{ {"$dateToString", bson.D{ {"format", "%m-%d"}, {"date", bson.D{ {"$add", bson.A{ time.Date(1970, 1, 1, 0, 0, 0, 0, time.UTC), "$timestamp", 28800000, }}, }}, }}, }}, {"error_count", 1}, {"timestamp", 1}, }}}, bson.D{{"$group", bson.D{ {"_id", "$day"}, {"max_time", bson.D{{"$max", "$timestamp"}}}, {"error_count", bson.D{{"$max", "$error_count"}}}, }}}, bson.D{{"$sort", bson.D{{"max_time", 1}}}}, }
完結(jié)撒花??
補充:解決MongoDB存儲時間時差的問題
MongoDB存儲時間類型數(shù)據(jù)時,都是先轉(zhuǎn)換為UTC時間,然后存儲到數(shù)據(jù)庫中,當(dāng)我們?nèi)〕龃鎯Φ臅r間時,就會出現(xiàn)時差的問題。比如我們用的北京時間,讀取到的數(shù)值就會看到比當(dāng)前時間少了8個小時,難道說我們在每次讀取的時候都要單獨處理一下時間嗎,這就比較麻煩。其實,我們可以在存儲的時候進(jìn)行相應(yīng)的處理,只需使用getTimezoneOffset()和toISOString()函數(shù)。
getTimezoneOffset函數(shù):返回此地區(qū)的時差(當(dāng)?shù)貢r間與GMT格林威治標(biāo)準(zhǔn)時間的地區(qū)時差),單位為分鐘。
<script> // 我們是東八區(qū) var d = new Date(); var tz = d.getTimezoneOffset(); console.log(tz); // -480 </script>
toISOString()函數(shù):使用ISO標(biāo)準(zhǔn)將 Date 對象轉(zhuǎn)換為字符串。
該標(biāo)準(zhǔn)稱為 ISO-8601 ,格式為: YYYY-MM-DDTHH:mm:ss.sssZ。
封裝時間轉(zhuǎn)換函數(shù)
localDate(v) { const d = new Date(v || Date.now()); d.setMinutes(d.getMinutes() - d.getTimezoneOffset()); return d.toISOString(); },
我們在存儲時間的時候調(diào)用localDate()這個函數(shù)就可以了,無論你處在哪個時區(qū)結(jié)果顯示都和當(dāng)?shù)貢r間一樣。
總結(jié)
到此這篇關(guān)于MongoDB時間戳轉(zhuǎn)日期及日期分組的文章就介紹到這了,更多相關(guān)MongoDB時間戳轉(zhuǎn)日期內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
MongoDB使用mongoexport和mongoimport命令,批量導(dǎo)出和導(dǎo)入JSON數(shù)據(jù)到同一張表的實例
今天小編就為大家分享一篇關(guān)于MongoDB使用mongoexport和mongoimport命令,批量導(dǎo)出和導(dǎo)入JSON數(shù)據(jù)到同一張表的實例,小編覺得內(nèi)容挺不錯的,現(xiàn)在分享給大家,具有很好的參考價值,需要的朋友一起跟隨小編來看看吧2018-10-10Windows系統(tǒng)下安裝MongoDB并內(nèi)網(wǎng)穿透遠(yuǎn)程連接
這篇文章主要給大家介紹了關(guān)于Windows系統(tǒng)下安裝MongoDB并內(nèi)網(wǎng)穿透遠(yuǎn)程連接的相關(guān)資料,文中通過圖文將步驟介紹的非常詳細(xì),對大家學(xué)習(xí)或者使用MongoDB具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下2023-03-03基于Mongodb分布式鎖解決定時任務(wù)并發(fā)執(zhí)行問題
這篇文章主要介紹了基于Mongodb分布式鎖解決定時任務(wù)并發(fā)執(zhí)行問題,網(wǎng)上有很多分布式鎖的實現(xiàn)方案,基于redis、zk、等有很多,但是我的就是一個用了mysql和mongo的小應(yīng)用,本文給大家詳細(xì)講解,需要的朋友可以參考下2023-04-04Pycharm連接MongoDB數(shù)據(jù)庫安裝教程詳解
這篇文章主要介紹了Pycharm連接MongoDB數(shù)據(jù)庫安裝教程,本文通過圖文并茂的形式給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2020-11-11