概述
數(shù)據(jù)存儲是為了可查詢组底,統(tǒng)計丈积。若數(shù)據(jù)只需存儲筐骇,不需要查詢,這種數(shù)據(jù)也沒有多大價值
本篇介紹Mongodb
- 聚合查詢(Aggregation)
- 固定集合(Capped Collections)
準(zhǔn)備工作
準(zhǔn)備10000條數(shù)據(jù)
var orders = new Array();
for (var i = 10000; i < 20000; i++) {
orders[i] = {
orderNo: i + Math.random().toString().substr(3, 3),
price: Math.round(Math.random() * 10000) / 100,
qty: Math.floor(Math.random() * 10) + 1,
orderTime: new Date(new Date().setSeconds(Math.floor(Math.random() * 10000)))
};
}
db.order.insert(orders);
聚合查詢
Mongodb的聚合函數(shù)操作都在db.collection.aggregate江滨,通過定義聚合管道(一組規(guī)則)铛纬,達(dá)到分組,統(tǒng)計等功能唬滑,下面介紹常用的幾種聚合函數(shù)
- 分組管道($group)
格式
{
$group:
{
_id: <expression>, // Group By Expression
<field1>: { <accumulator1> : <expression1> },
...
}
}
_id 是分組字段告唆,若指定_id = null 或常量字段,就是將整個結(jié)果集分組间雀。
分組統(tǒng)計字段格式{ <accumulator1> : <expression1> }
累計器操作(Accumulator Operator)參考Accumulator Operator
假設(shè)現(xiàn)在需要統(tǒng)計每天每個小時的訂單總價格,平均價格镊屎,最大惹挟,最小,總訂單數(shù)等
db.order.aggregate([
{
$group: {
//分組字段缝驳,這里用到$dateToString格式化连锯,這里按小時統(tǒng)計
_id: { $dateToString: { format: "%Y-%m-%d %H", date: "$orderTime" } },
//總價格
totalPrice: { $sum: "$price" },
//分組第一個訂單
firstOrder: { $first: "$orderNo" },
//分組最后一個訂單
lastOrder: { $last: "$orderNo" },
//平均價格
averagePrice: { $avg: "$price" },
//最大價格
maxPrice: { $max: "$price" },
//最小價格
minPrice: { $min: "$price" },
//總訂單數(shù)
totalOrders: { $sum: 1 },
}
}
])
返回結(jié)果
{ "_id" : "2020-04-12 15", "totalPrice" : 172813.68, "firstOrder" : "10000263", "lastOrder" : "19999275", "averagePrice" : 49.20662870159453, "maxPrice" : 99.94, "minPrice" : 0.01, "totalOrders" : 3512 }
{ "_id" : "2020-04-12 13", "totalPrice" : 80943.98, "firstOrder" : "10004484", "lastOrder" : "19991554", "averagePrice" : 50.780414052697616, "maxPrice" : 99.81, "minPrice" : 0.08, "totalOrders" : 1594 }
{ "_id" : "2020-04-12 14", "totalPrice" : 181710.15, "firstOrder" : "10001745", "lastOrder" : "19998830", "averagePrice" : 49.76996713229252, "maxPrice" : 99.93, "minPrice" : 0.01, "totalOrders" : 3651 }
{ "_id" : "2020-04-12 16", "totalPrice" : 63356.12, "firstOrder" : "10002711", "lastOrder" : "19995793", "averagePrice" : 50.97032984714401, "maxPrice" : 99.95, "minPrice" : 0.01, "totalOrders" : 1243 }
- 篩選管道($match)
格式
{ $match: { <query> } }
這個比較簡單,就是篩選數(shù)據(jù)
假設(shè)我現(xiàn)在需要篩選金額在(10用狱,15)之間的
db.orders.aggregate([
{
$match: {
"price": { $gt: 10, $lt: 15 }
}
}
])
- 排序管道($sort)
格式
{ $sort: { <field1>: <sort order>, <field2>: <sort order> ... } }
指定字段排序运怖,1:升序,-1:倒序
- 限制條數(shù)($limit)
格式
{ $limit: <positive integer> }
Mongodb的管道有很多夏伊,具體不一一列出摇展,參考Aggregation Pipeline Stages — MongoDB Manual
帶有(aggregation)就是都可以用于聚合管道
說了那么多,其實都沒有使用Mongodb聚合函數(shù)最強(qiáng)大的功能溺忧,就是組合管道使用咏连,查詢我們需要數(shù)據(jù),因為Mongodb提供的聚合管道函數(shù)非常多鲁森,所以組合起來使用是非常強(qiáng)大祟滴。
值得注意是管道的順序,Mongodb是按你定義的順序歌溉,將每一步執(zhí)行的結(jié)果集傳給下一個管道處理垄懂,輸出是最后一個管道的結(jié)果集,所以不同的管道順序會有可能得到不是預(yù)期的結(jié)果痛垛,甚至報錯(這種情況報錯甚至比得到不是預(yù)期的結(jié)果可能還好)
假設(shè)現(xiàn)在按每天小時統(tǒng)計符合下列條件的訂單
- 訂單金額大于10元 小于 50元 && 數(shù)量小于等于5 and
- 去掉金額最小的50條訂單 && 去掉金額最大的50條訂單 and
- 統(tǒng)計每個小時內(nèi)訂單數(shù)量草慧,訂單金額
- 按訂單金額升序輸出
db.order.aggregate([
{
$match: { "price": { $gt: 10, $lt: 50 }, "qty": { $lte: 5 } }
},
{
$sort: {
"price": -1
}
},
{
$skip: 50
},
{
$sort: {
"price": 1
}
},
{
$skip: 50
},
{
$group: {
_id: { $dateToString: { format: "%Y-%m-%d %H", date: "$orderTime" } },
totalPrice: { $sum: "$price" },
totalOrders: { $sum: 1 }
}
},
{
$sort: {
"totalPrice": 1
}
}
])
解決思路
- 篩選符合條件的記錄($match)
- 按金額倒序($sort:-1)
- 跳過金額最大的50條記錄($skip:50)
- 按金額升序($sort:1)
- 跳過金額最小的50條記錄($skip:50)
- 按每天每小時統(tǒng)計($group)
- 統(tǒng)計結(jié)果總金額升序($sort:1)
固定集合
概述
capped-collection are fixed-size collections that support high-throughput operations that insert and retrieve documents based on insertion order. Capped collections work in a way similar to circular buffers: once a collection fills its allocated space, it makes room for new documents by overwriting the oldest documents in the collection.
從上面定義可以看出固定集合具有幾個特性
- 固定大小
- 高吞吐量
- 根據(jù)插入順序檢索文檔
- 超過限制大小覆蓋舊的文檔
根據(jù)固定集合特性,固定集合適合用于以下場景
- 只需保留最近的日志查詢系統(tǒng)
- 緩存數(shù)據(jù)(熱點數(shù)據(jù))
- 等等
固定集合限制
- 固定集合的大小創(chuàng)建之后不能修改
- 不能刪除固定集合里的文檔匙头,只能刪除集合再重新建固定集合
- 固定集合不能使用固定分區(qū)
- 聚合管道$out不能使用在固定集合
固定集合使用
- 創(chuàng)建固定集合
db.createCollection("log", { capped : true, size : 4096, max : 5000 } )
字段 | 必須 | 說明 |
---|---|---|
capped | 是?????? | 是否創(chuàng)建固定集合 |
size | 是 | 固定集合大小冠蒋,單位:字節(jié) |
max | 否 | 文檔數(shù)量大小限制 |
size 和 max 是或關(guān)系,超出其中一個限制都會覆蓋舊文檔
- 檢查集合是否固定集合
db.collection.isCapped()
- 將一個非固定的集合轉(zhuǎn)換固定集合
db.runCommand({"convertToCapped": "mycoll", size: 100000});
測試固定集合
- 超過限制文檔數(shù)
// 1. 創(chuàng)建固定集合乾胶,大小1M抖剿,最大文檔數(shù)量10
db.createCollection("log", { capped: true, size: 1024 * 1024, max: 10 });
// 2. 插入200條數(shù)據(jù)
for (var i = 0; i < 200; i++) {
db.log.insertOne({
"_id": i + 1,
"userId": Math.floor(Math.random() * 1000),
"content": "登錄" + ("0000" + i).slice(-4),
"createTime": new Date(),
});
}
再查詢現(xiàn)在Mongodb存儲情況
db.log.stats()
可以看出每個對象都是占有78個字節(jié)朽寞,因為字段都是定長的
- 驗證操作存儲大小
If the size field is less than or equal to 4096, then the collection will have a cap of 4096 bytes. Otherwise, MongoDB will raise the provided size to make it an integer multiple of 256.
如果size的字段設(shè)置小于4096,Mongodb將會提供一個256的倍數(shù)的數(shù)據(jù)存儲大小
假設(shè)256的大小斩郎,256 / 78 = 3.282051282051282脑融,應(yīng)該能存3個文檔
// 1. 刪除之前固定集合
db.log.drop();
// 2. 創(chuàng)建固定集合,size < 78 , 驗證是否創(chuàng)建一個256的大小
db.createCollection("log", { capped: true, size: 78 });
// 2. 插入200條數(shù)據(jù)
for (var i = 0; i < 200; i++) {
db.log.insertOne({
"_id": i + 1,
"userId": Math.floor(Math.random() * 1000),
"content": "登錄" + ("0000" + i).slice(-4),
"createTime": new Date(),
});
}
查看集合統(tǒng)計
db.log.stats()
可以看出log集合使用了234個字節(jié)(78 * 3)缩宜,也即3個文檔的大小肘迎,最大能使用大小是256
- 查詢固定集合
Mongodb若沒指定排序字段,是按存入順序檢索锻煌,可以使用.sort( { $natural: -1 } )改變排序
db.log.find({}).sort( { $natural: -1 } )
- 將非固定集合轉(zhuǎn)換固定集合
將order轉(zhuǎn)換試試
db.runCommand({"convertToCapped": "order", size: 8096});
查看order集合統(tǒng)計
只剩下90條數(shù)據(jù)
參考文章
Aggregation — MongoDB Manual
Capped Collections — MongoDB Manual