天天看點

【Mongodb】視圖 && 索引

【Mongodb】視圖 && 索引

mangoDB

準備工作

準備2個集合的資料,後面視圖和索引都會用到

1個訂單集合,一個收款資訊集合

var orders = new Array();

var shipping = new Array();

var addresses = ["廣西省玉林市", "湖南省嶽陽市", "湖北省荊州市", "甘肅省蘭州市", "吉林省松原市", "江西省景德鎮", "遼甯省沈陽市", "福建省廈門市", "廣東省廣州市", "北京市朝陽區"];

for (var i = 10000; i < 20000; i++) {

var orderNo = i + Math.random().toString().substr(2, 5);
orders[i] = { orderNo: orderNo, userId: i, 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))) };

var address = addresses[Math.floor(Math.random() * 10)];
shipping[i] = { orderNo: orderNo, address: address, recipienter: "Wilson", province: address.substr(0, 3), city: address.substr(3, 3) }           

}

db.order.insert(orders);

db.shipping.insert(shipping);

視圖

概述

A MongoDB view is a queryable object whose contents are defined by an aggregation pipeline on other collections or views. MongoDB does not persist the view contents to disk. A view’s content is computed on-demand when a client queries the view. MongoDB can require clients to have permission to query the view. MongoDB does not support write operations against views.

Mongodb的視圖基本上和SQL的視圖一樣

資料源(集合或視圖)

提供查詢

不實際存儲硬碟

用戶端發起請求查詢時計算而得

  1. 建立視圖

有兩種方法建立視圖

db.createCollection(

"",

{

"viewOn" : "<source>",
"pipeline" : [<pipeline>],
"collation" : { <collation> }           

)

db.createView(

[],

"collation" : { <collation> }           

一般使用db.createView

viewName : 必須,視圖名稱

source : 必須,資料源,集合/視圖

[] : 可選,一組管道,可見管道是Mongodb比較重要的一環

1.1 單個集合建立視圖

假設現在檢視當天最高的10筆訂單視圖,例如背景某個地方需要實時顯示金額最高的訂單

"orderInfo",         //視圖名稱
"order",             //資料源   
[
    //篩選符合條件的訂單,大于當天,這裡要注意時區
    { $match: { "orderTime": { $gte: ISODate("2020-04-13T16:00:00.000Z") } } },
    //按金額倒序
    { $sort: { "price": -1 } },
    //限制10個文檔
    { $limit: 10 },
    //選擇要顯示的字段
    //0: 排除字段,若字段上使用(_id除外),就不能有其他包含字段
    //1: 包含字段
    { $project: { _id: 0, orderNo: 1, price: 1, orderTime: 1 } }
]           

然後就可以直接使用orderInfo這個視圖查詢資料

db.orderInfo.find({})

傳回結果

{ "orderNo" : "1755149436", "price" : 100, "orderTime" : ISODate("2020-04-14T13:49:42.220Z") }

{ "orderNo" : "1951423853", "price" : 99.99, "orderTime" : ISODate("2020-04-14T15:08:07.240Z") }

{ "orderNo" : "1196303215", "price" : 99.99, "orderTime" : ISODate("2020-04-14T15:15:41.158Z") }

{ "orderNo" : "1580069456", "price" : 99.98, "orderTime" : ISODate("2020-04-14T13:41:07.199Z") }

{ "orderNo" : "1114480559", "price" : 99.98, "orderTime" : ISODate("2020-04-14T13:31:58.150Z") }

{ "orderNo" : "1229542817", "price" : 99.98, "orderTime" : ISODate("2020-04-14T15:15:35.162Z") }

{ "orderNo" : "1208031402", "price" : 99.94, "orderTime" : ISODate("2020-04-14T14:13:02.160Z") }

{ "orderNo" : "1680622670", "price" : 99.93, "orderTime" : ISODate("2020-04-14T15:17:25.210Z") }

{ "orderNo" : "1549824953", "price" : 99.92, "orderTime" : ISODate("2020-04-14T13:09:41.196Z") }

{ "orderNo" : "1449930147", "price" : 99.92, "orderTime" : ISODate("2020-04-14T15:16:15.187Z") }

1.2 多個集合建立視圖

其實跟單個是集合是一樣,隻是多了$lookup連接配接操作符,視圖根據管道最終結果顯示,是以可以關聯多個集合(若出現這種情況就要考慮集合設計是否合理,mongodb本來就是文檔型資料庫)

db.orderDetail.drop()

"orderDetail",
"order",
[
    { $lookup: { from: "shipping", localField: "orderNo", foreignField: "orderNo", as: "shipping" } },
    { $project: { "orderNo": 1, "price": 1, "shipping.address": 1 } }
]           

查詢視圖,得到如下結果

{ "_id" : ObjectId("5e95af8c4ef6faf974b4a6c3"), "orderNo" : "1000039782", "price" : 85.94, "shipping" : [ { "address" : "北京市朝陽區" } ] }

{ "_id" : ObjectId("5e95af8c4ef6faf974b4a6c4"), "orderNo" : "1000102128", "price" : 29.04, "shipping" : [ { "address" : "吉林省松原市" } ] }

{ "_id" : ObjectId("5e95af8c4ef6faf974b4a6c5"), "orderNo" : "1000214514", "price" : 90.69, "shipping" : [ { "address" : "湖南省嶽陽市" } ] }

{ "_id" : ObjectId("5e95af8c4ef6faf974b4a6c6"), "orderNo" : "1000337987", "price" : 75.05, "shipping" : [ { "address" : "遼甯省沈陽市" } ] }

{ "_id" : ObjectId("5e95af8c4ef6faf974b4a6c7"), "orderNo" : "1000468969", "price" : 76.84, "shipping" : [ { "address" : "江西省景德鎮" } ] }

{ "_id" : ObjectId("5e95af8c4ef6faf974b4a6c8"), "orderNo" : "1000572219", "price" : 60.25, "shipping" : [ { "address" : "江西省景德鎮" } ] }

{ "_id" : ObjectId("5e95af8c4ef6faf974b4a6c9"), "orderNo" : "1000611743", "price" : 19.14, "shipping" : [ { "address" : "廣東省廣州市" } ] }

{ "_id" : ObjectId("5e95af8c4ef6faf974b4a6ca"), "orderNo" : "1000773917", "price" : 31.5, "shipping" : [ { "address" : "北京市朝陽區" } ] }

{ "_id" : ObjectId("5e95af8c4ef6faf974b4a6cb"), "orderNo" : "1000879146", "price" : 76.16, "shipping" : [ { "address" : "吉林省松原市" } ] }

{ "_id" : ObjectId("5e95af8c4ef6faf974b4a6cc"), "orderNo" : "1000945977", "price" : 93.98, "shipping" : [ { "address" : "遼甯省沈陽市" } ] }

可以看到,mongodb不是像SQL那樣把連接配接的表當成列列出,而是把連接配接結果放在數組裡面,這很符合Mongodb文檔型結構。

  1. 修改視圖

假設現在需要增加一個數量的字段

db.runCommand({

collMod: "orderInfo",
viewOn: "order",
pipeline: [
    { $match: { "orderTime": { $gte: ISODate("2020-04-13T16:00:00.000Z") } } },
    { $sort: { "price": -1 } },
    { $limit: 10 },
    //增加qty
    { $project: { _id: 0, orderNo: 1, price: 1, qty: 1, orderTime: 1 } }
]           

})

當然,也可以删除視圖,重新用db.createView()建立視圖

  1. 删除視圖

db.orderInfo.drop();

索引

Indexes support the efficient execution of queries in MongoDB. Without indexes, MongoDB must perform a collection scan, i.e. scan every document in a collection, to select those documents that match the query statement. If an appropriate index exists for a query, MongoDB can use the index to limit the number of documents it must inspect.

索引能提供高效的查詢,沒有索引的查詢,mongole執行集合掃描,相當于SQL SERVER的全表掃描,掃描每一個文檔。

資料存在存儲媒體上,大多數情況是為了查詢,查詢的快慢直接影響使用者體驗,mongodb索引也是空間換時間,添加索引,CUD操作都會導緻索引重新生成,影響速度。

1.1 準備200W條資料

var orderNo = 100 * 10000;

for (var i = 0; i < 100; i++) {

//分批次插入,每次20000條
var orders = new Array();
for (var j = 0; j < 20000; j++) {
    var orderNo = orderNo++;
    orders[j] = { orderNo: orderNo, userId: i + j, 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, { writeConcern: { w: 0 } });           

1.2 mongodb的查詢計劃

db.collection.explain().

一般使用執行統計模式,例如

db.order.explain("executionStats").find({orderNo:1000000})

傳回的executionStats對象字段說明

部分字段說明

字段 說明

executionSuccess 是否執行成功

nReturned 傳回比對文檔數量

executionTimeMillis 執行時間,機關:毫秒

totalKeysExamined 索引檢索數目

totalDocsExamined 文檔檢索數目

檢視未加索引前查詢計劃

截取部分傳回結果,可以看出

executionTimeMillis : 用時1437毫秒

totalDocsExamined : 掃描文檔200W

executionStages.stage : 集合掃描

"executionStats" : {

"executionSuccess" : true,
"nReturned" : 1,
"executionTimeMillis" : 1437,
"totalKeysExamined" : 0,
"totalDocsExamined" : 2000000,
"executionStages" : {
        "stage" : "COLLSCAN",
           

1.3 檢視目前集合統計資訊

db.order.stats()

截取部分資訊,可以看出現在存儲檔案大小大概為72M

{

"ns" : "mongo.order",
    "size" : 204000000,
    "count" : 2000000,
    "avgObjSize" : 102,
    "storageSize" : 74473472,
           
  1. 建立索引

db.order.createIndex({ orderNo: 1 }, { name: "ix_orderNo" })

索引名稱不是必須,若不指定,按 字段名稱_排序類型組合自動生成,索引名稱一旦建立不能修改,若要修改,隻能删除索引重新生成索引,建議還是建索引的時候就把索引名稱設定好。

2.1 執行查詢計劃

截取部分結果,直覺就可以感覺查詢速度有了質的提升,再看查詢計劃更加驚訝

nReturned : 比對到1個文檔

executionTimeMillis : 0,呃。。

totalKeysExamined : 總共檢索了1個索引

totalDocsExamined : 總共檢索了1個文檔

executionStages.stage : FETCH,根據索引去檢索指定文檔,像SQL的Index Seek

"executionSuccess" : true,
            "nReturned" : 1,
            "executionTimeMillis" : 0,
            "totalKeysExamined" : 1,
            "totalDocsExamined" : 1,
            "executionStages" : {
                    "stage" : "FETCH"
           

這裡隻介紹最簡單的單個字段索引,mongodb還有很多索引

複合索引(Compound Indexes):對多個字段做索引

多鍵索引(Multikey Indexes): 一個字段多個值做索引,通常是數組

全文索引(Text Indexes): 對文字檢索,可以對字段設定不同權重

通配符索引(Wildcard Indexes):可以将對象的所有/指定的值做索引

更多

參考文章

Views — MongoDB Manual

Indexes — MongoDB Manual

轉發請标明出處:

https://www.cnblogs.com/WilsonPan/p/12704474.html