1. 程式人生 > >我思,故我在!--My data life

我思,故我在!--My data life



索引簡介


MongoDB同傳統資料庫索引一樣,同樣使用的是B-tree索引,絕大多數優化MySQL/Oracle/SQLlite索引技術也同樣適用於MongoDB.
建立索引使用ensureIndex方法

建立普通索引


> db.users1.find()
{ "_id" : 1, "username" : "smith", "age" : 48, "user_id" : 0 }
{ "_id" : 2, "username" : "smith", "age" : 30, "user_id" : 1 }
{ "_id" : 3, "username" : "john", "age" : 36, "user_id" : 2 }
{ "_id" : 4, "username" : "john", "age" : 18, "user_id" : 3 }
{ "_id" : 5, "username" : "joe", "age" : 36, "user_id" : 4 }
{ "_id" : 6, "username" : "john", "age" : 7, "user_id" : 5 }
{ "_id" : 7, "username" : "simon", "age" : 3, "user_id" : 6 }
{ "_id" : 8, "username" : "joe", "age" : 27, "user_id" : 7 }
{ "_id" : 9, "username" : "jacob", "age" : 17, "user_id" : 8 }
{ "_id" : 10, "username" : "sally", "age" : 52, "user_id" : 9 }
{ "_id" : 11, "username" : "simon", "age" : 59, "user_id" : 10 }

--在username欄位上建立正序索引
> db.users1.ensureIndex({"username":1})

--對下面查詢建立有效索引
> db.people.find({"date" : date1}).sort({"date" : 1, "username" : 1})
> db.people.ensureIndex({"date" : 1, "username" : 1})


建立索引內嵌文件索引


--內嵌文件如下
> db.blog.posts.findOne({"comments.name":"licz"})
{
        "_id" : ObjectId("4b2d75476cc613d5ee930164"),
        "comments" : [
                {
                        "content" : "nice post.",
                        "date" : ISODate("2016-02-17T08:01:43.813Z"),
                        "email" : "

[email protected]",
                        "name" : "joe",
                        "visits" : 1
                },
                {
                        "content" : "good post.",
                        "date" : ISODate("2016-02-17T08:00:45.746Z"),
                        "email" : "[email protected]
",
                        "name" : "licz"
                }
        ],
        "content" : "...",
        "title" : "A blog post"
}
> db.bolg.posts.ensureIndex({"comments.date":1})

建立唯一索引


> db.people.ensureIndex({"username" : 1}, {"unique" : true})


--消除重複
當為已有的集合中建立唯一索引時,可能一些已經重複了。這時建立索引會失敗,有時可能需要把重複的文件都刪掉,dropDups選項就可以保留髮現的第一個文件,而刪除有重複的文件。
> db.people.ensureIndex({"username" : 1}, {"unique" : true, "dropDups" : true})


使用explain和hint
explain會返回查詢使用索引的情況,耗時及掃描文件的統計資訊。
> db.users1.find({"username":"joe"})
{ "_id" : 5, "username" : "joe", "age" : 36, "user_id" : 4 }
{ "_id" : 8, "username" : "joe", "age" : 27, "user_id" : 7 }
> db.users1.find({"username":"joe"}).explain()
{
        "cursor" : "BtreeCursor username_1",
        "isMultiKey" : false,
        "n" : 2,
        "nscannedObjects" : 2,
        "nscanned" : 2,
        "nscannedObjectsAllPlans" : 2,
        "nscannedAllPlans" : 2,
        "scanAndOrder" : false,
        "indexOnly" : false,
        "nYields" : 0,
        "nChunkSkips" : 0,
        "millis" : 3,
        "indexBounds" : {
                "username" : [
                        [
                                "joe",
                                "joe"
                        ]
                ]
        },
        "server" : "racdb:27017"
}

如果發現MongoDB使用了非預期的索引,可以使用hint強制使用某個索引。如:
> db.c.find({"age" : 14, "username" : /.*/}).hint({"username" : 1, "age" : 1})

和Oracle中使用hint一樣,hint多數情況下是沒必要指定的。因為MongoDB非常智慧,會替你選擇選擇使用如個索引。

管理索引


索引元資訊都在system.indexes集合中
> db.system.indexes.find()
{ "v" : 1, "key" : { "_id" : 1 }, "name" : "_id_", "ns" : "test.my_collection" }
{ "v" : 1, "key" : { "x" : 1 }, "name" : "x_1", "ns" : "test.my_collection" }
{ "v" : 1, "key" : { "_id" : 1 }, "name" : "_id_", "ns" : "test.people" }
......
{ "v" : 1, "key" : { "_id" : 1 }, "name" : "_id_", "ns" : "test.stocks" }
{ "v" : 1, "name" : "username_1", "key" : { "username" : 1 }, "ns" : "test.users1" }
{ "v" : 1, "name" : "_id_", "key" : { "_id" : 1 }, "ns" : "test.bolg.posts" }
{ "v" : 1, "name" : "comments.date_1", "key" : { "comments.date" : 1 }, "ns" : "test.bolg.posts" }

--刪除索引
> db.users1.dropIndexes({"username":1})
{
        "nIndexesWas" : 2,
        "msg" : "non-_id indexes dropped for collection",
        "ok" : 1
}

--或是使用runCommand命令刪除索引
> db.runCommand({"dropIndexes":"bolg.posts","index":"comments.date_1"})
{ "nIndexesWas" : 2, "ok" : 1 }

--修改索引(重建索引)
當原來的索引不好用時,需要重建索引,這時可以使用backgroud選項使建立過程在後臺執行,這樣會避免資料庫產生阻塞。
> db.users1.ensureIndex({username:1},{backgroud:true})

地理空間索引


還有一查詢變得越來越流行:要找到離當前位置最近的N個場所,如要找到給定經緯度座標周圍最近的咖啡館。
MongoDB為座標平面提供了專門的索引,稱作:地理空間索引
同樣可以用ensureIndex來建立,只不過引數不是1或-1,而是"2d"
db.map.ensureIndex({"gps":"2d"})
"gps"必需是某種形式的一對值:一個包含兩個元素的陣列或是包含兩個鍵的內嵌文件;鍵值名可以任意。如下:
{ "gps" : [ 0, 100 ] }
{ "gps" : { "x" : -30, "y" : 30 } }
{ "gps" : { "latitude" : -180, "longitude" : 180 } }

預設情況下地理空間的範圍是-180~180(經緯度),要想用其它值,可以通過ensureIndex選項指定最大最小值:
> db.star.trek.ensureIndex({"light-years" : "2d"}, {"min" : -1000, "max" : 1000})
這樣就建立了一個2000光年見方的索引。

地理空間查詢方法


使用$near
返回離[40, -73]最近的10個文件
> db.map.find({"gps" : {"$near" : [40, -73]}}).limit(10)
或是:
> db.runCommand({geoNear : "map", near : [40, -73], num : 10});

查詢指定開頭內的文件
即將原來的$near換成$within
$within形狀引數文件(http://www.mongodb.org/display/DOCS/Geospatial+Indexing)
矩形:使用"$box"
> db.map.find({"gps" : {"$within" : {"$box" : [[10, 20], [15, 30]]}}})
圓形:使用"$center"
> db.map.find({"gps" : {"$within" : {"$center" : [[12, 25], 5]}}})


複合地理空間索引


應用經常要找的東西不只是一個地點。例如,使用者要找出周圍所有的咖啡店或披薩店。將地理空間索引與普通索引組合起來就可以滿足這種需求。
例如,要查詢"location"和"desc",就可以這樣建立索引:

> db.ensureIndex({"location" : "2d", "desc" : 1})

然後就可能很快找到最近的咖啡館了

> db.map.find({"location" : {"$near" : [-70, 30]}, "desc" : "coffeeshop"}).limit(1)
{
"_id" : ObjectId("4c0d1348928a815a720a0000"),
"name" : "Mud",
"location" : [x, y],
"desc" : ["coffee", "coffeeshop", "muffins", "espresso"]
}

注意:建立一個關鍵片語對於使用者自定義查詢很有幫助。