摘要:固定集合不能被分片。為固定集合指定文檔數(shù)量限制時,必須同時指定固定集合的大小。沒有索引的集合默認(rèn)情況下,每個集合都有一個索引。
上一篇文章:MongoDB指南---13、索引類型、索引管理
下一篇文章:MongoDB指南---15、特殊的索引和集合:地理空間索引、使用GridFS存儲文件
本章介紹MongoDB中一些特殊的集合和索引類型,包括:
用于類隊列數(shù)據(jù)的固定集合(capped collection);
用于緩存的TTL索引;
用于簡單字符串搜索的全文本索引;
用于二維平面和球體空間的地理空間索引;
用于存儲大文件的GridFS。
固定集合MongoDB中的“普通”集合是動態(tài)創(chuàng)建的,而且可以自動增長以容納更多的數(shù)據(jù)。MongoDB中還有另一種不同類型的集合,叫做固定集合,固定集合需要事先創(chuàng)建好,而且它的大小是固定的(如圖6-1所示)。說到固定大小的集合,有一個很有趣的問題:向一個已經(jīng)滿了的固定集合中插入數(shù)據(jù)會怎么樣?答案是,固定集合的行為類似于循環(huán)隊列。如果已經(jīng)沒有空間了,最老的文檔會被刪除以釋放空間,新插入的文檔會占據(jù)這塊空間(如圖6-2所示)。也就是說,當(dāng)固定集合被占滿時,如果再插入新文檔,固定集合會自動將最老的文檔從集合中刪除。
圖6-1 新文檔被插入到隊列末尾
圖6-2 如果隊列已經(jīng)被占滿,那么最老的文檔會被之后插入的新文檔覆蓋
固定集合的訪問模式與MongoDB中的大部分集合不同:數(shù)據(jù)被順序?qū)懭氪疟P上的固定空間。因此它們在碟式磁盤(spinning disk)上的寫入速度非??欤绕涫羌蠐碛袑S么疟P時(這樣就不會因為其他集合的一些隨機(jī)性的寫操作而“中斷”)。
固定集合不能被分片。
固定集合可以用于記錄日志,盡管它們不夠靈活。雖然可以在創(chuàng)建時指定集合大小,但無法控制什么時候數(shù)據(jù)會被覆蓋。
創(chuàng)建固定集合不同于普通集合,固定集合必須在使用之前先顯式創(chuàng)建。可以使用create命令創(chuàng)建固定集合。在shell中,可以使用createCollection函數(shù):
> db.createCollection("my_collection", {"capped" : true, "size" : 100000}); { "ok" : true }
上面的命令創(chuàng)建了一個名為my_collection大小為100 000字節(jié)的固定集合。
除了大小,createCollection還能夠指定固定集合中文檔的數(shù)量:
> db.createCollection("my_collection2", ... {"capped" : true, "size" : 100000, "max" : 100}); { "ok" : true }
可以使用這種方式來保存最新的10則新聞,或者是將每個用戶的文檔數(shù)量限制為1000。
固定集合創(chuàng)建之后,就不能改變了(如果需要修改固定集合的屬性,只能將它刪除之后再重建)。因此,在創(chuàng)建大的固定集合之前應(yīng)該仔細(xì)想清楚它的大小。
為固定集合指定文檔數(shù)量限制時,必須同時指定固定集合的大小。不管先達(dá)到哪一個限制,之后插入的新文檔就會把最老的文檔擠出集合:固定集合的文檔數(shù)量不能超過文檔數(shù)量限制,固定集合的大小也不能超過大小限制。
創(chuàng)建固定集合時還有另一個選項,可以將已有的某個常規(guī)集合轉(zhuǎn)換為固定集合,可以使用convertToCapped命令實現(xiàn)。下面的例子將test集合轉(zhuǎn)換為一個大小為10 000字節(jié)的固定集合:
> db.runCommand({"convertToCapped" : "test", "size" : 10000}); { "ok" : true }
無法將固定集合轉(zhuǎn)換為非固定集合(只能將其刪除)。
自然排序對固定集合可以進(jìn)行一種特殊的排序,稱為自然排序(natural sort)。自然排序返回結(jié)果集中文檔的順序就是文檔在磁盤上的順序(如圖6-3所示)。
圖6-3 使用{"$natural" : 1}進(jìn)行排序
對大多數(shù)集合來說,自然排序的意義不大,因為文檔的位置經(jīng)常變動。但是,固定集合中的文檔是按照文檔被插入的順序保存的,自然順序就是文檔的插入順序。因此,自然排序得到的文檔是從舊到新排列的。當(dāng)然也可以按照從新到舊的順序排列(如圖6-4所示)。
> db.my_collection.find().sort({"$natural" : -1})
圖6-4 使用{"$natural" : -1}進(jìn)行排序
循環(huán)游標(biāo)循環(huán)游標(biāo)(tailable cursor)是一種特殊的游標(biāo),當(dāng)循環(huán)游標(biāo)的結(jié)果集被取光后,游標(biāo)不會被關(guān)閉。循環(huán)游標(biāo)的靈感來自tail -f命令(循環(huán)游標(biāo)跟這個命令有點兒相似),會盡可能久地持續(xù)提取輸出結(jié)果。由于循環(huán)游標(biāo)在結(jié)果集取光之后不會被關(guān)閉,因此,當(dāng)有新文檔插入到集合中時,循環(huán)游標(biāo)會繼續(xù)取到結(jié)果。由于普通集合并不維護(hù)文檔的插入順序,所以循環(huán)游標(biāo)只能用在固定集合上。
循環(huán)游標(biāo)通常用于當(dāng)文檔被插入到“工作隊列”(其實就是個固定集合)時對新插入的文檔進(jìn)行處理。如果超過10分鐘沒有新的結(jié)果,循環(huán)游標(biāo)就會被釋放,因此,當(dāng)游標(biāo)被關(guān)閉時自動重新執(zhí)行查詢是非常重要的。下面是一個在PHP中使用循環(huán)游標(biāo)的例子(不能在mongo shell中使用循環(huán)游標(biāo)):
$cursor = $collection->find()->tailable(); while (true) { if (!$cursor->hasNext()) { if ($cursor->dead()) { break; } sleep(1); } else { while ($cursor->hasNext()) { do_stuff($cursor->getNext()); } } }
這個游標(biāo)會不斷對查詢結(jié)果進(jìn)行處理,或者是等待新的查詢結(jié)果,直到游標(biāo)被關(guān)閉(超過10分鐘沒有新的結(jié)果或者人為中止查詢操作)。
沒有_id索引的集合默認(rèn)情況下,每個集合都有一個"_id"索引。但是,如果在調(diào)用createCollection創(chuàng)建集合時指定autoIndexId選項為false,創(chuàng)建集合時就不會自動在"_id"上創(chuàng)建索引。實踐中不建議這么使用,但是對于只有插入操作的集合來說,這確實可以帶來速度的稍許提升。
如果創(chuàng)建了一個沒有"_id"索引的集合,那就永遠(yuǎn)都不能復(fù)制它所在的mongod了。復(fù)制操作要求每個集合上都要有"_id"索引(對于復(fù)制操作,能夠唯一標(biāo)識集合中的每一個文檔是非常重要的)。
在2.2版本之前,固定集合默認(rèn)是沒有"_id"索引的,除非顯式地將autoIndexId置為true。如果正在使用舊版的固定集合,要確保你的應(yīng)用程序能夠填充"_id"字段(大多數(shù)驅(qū)動程序會自動填充"_id"字段),然后使用ensureIndex命令創(chuàng)建"_id"索引。
記住,"_id"索引必須是唯一索引。不同于其他索引,"_id"索引一經(jīng)創(chuàng)建就無法刪除了,因此在生產(chǎn)環(huán)境中創(chuàng)建索引之前先自己實踐一下是非常重要的。所以創(chuàng)建"_id"索引必須一次成功!如果創(chuàng)建的"_id"索引不合規(guī)范,就只能刪除集合再重建了。
上一節(jié)已經(jīng)講過,對于固定集合中的內(nèi)容何時被覆蓋,你只擁有非常有限的控制權(quán)限。如果需要更加靈活的老化移出系統(tǒng)(age-out system),可以使用TTL索引(time-to-live index,具有生命周期的索引),這種索引允許為每一個文檔設(shè)置一個超時時間。一個文檔到達(dá)預(yù)設(shè)置的老化程度之后就會被刪除。這種類型的索引對于緩存問題(比如會話的保存)非常有用。
在ensureIndex中指定expireAfterSecs選項就可以創(chuàng)建一個TTL索引:
> // 超時時間為24小時 > db.foo.ensureIndex({"lastUpdated" : 1}, {"expireAfterSecs" : 60*60*24})
這樣就在"lastUpdated"字段上建立了一個TTL索引。如果一個文檔的"lastUpdated"字段存在并且它的值是日期類型,當(dāng)服務(wù)器時間比文檔的"lastUpdated"字段的時間晚expireAfterSecs秒時,文檔就會被刪除。
為了防止活躍的會話被刪除,可以在會話上有活動發(fā)生時將"lastUpdated"字段的值更新為當(dāng)前時間。只要"lastUpdated"的時間距離當(dāng)前時間達(dá)到24小時,相應(yīng)的文檔就會被刪除。
MongoDB每分鐘對TTL索引進(jìn)行一次清理,所以不應(yīng)該依賴以秒為單位的時間保證索引的存活狀態(tài)??梢允褂胏ollMod命令修改expireAfterSecs的值:
> db.runCommand({"collMod" : "someapp.cache", "expireAfterSecs" : 3600})
在一個給定的集合上可以有多個TTL索引。TTL索引不能是復(fù)合索引,但是可以像“普通”索引一樣用來優(yōu)化排序和查詢。
全文本索引MongoDB有一個特殊類型的索引用于在文檔中搜索文本。前面幾章都是使用精確匹配和正則表達(dá)式來查詢字符串,但是這些技術(shù)有一些限制。使用正則表達(dá)式搜索大塊文本的速度非常慢,而且無法處理語言的理解問題(比如entry與entries應(yīng)該算是匹配的)。使用全文本索引可以非??斓剡M(jìn)行文本搜索,就如同內(nèi)置了多種語言分詞機(jī)制的支持一樣。
創(chuàng)建任何一種索引的開銷都比較大,而創(chuàng)建全文本索引的成本更高。在一個操作頻繁的集合上創(chuàng)建全文本索引可能會導(dǎo)致MongoDB過載,所以應(yīng)該是離線狀態(tài)下創(chuàng)建全文本索引,或者是在對性能沒要求時。創(chuàng)建全文本索引時要特別小心謹(jǐn)慎,內(nèi)存可能會不夠用(除非你有SSD)。
全文本索引也會導(dǎo)致比“普通”索引更嚴(yán)重的性能問題,因為所有字符串都需要被分解、分詞,并且保存到一些地方。因此,可能會發(fā)現(xiàn)擁有全文本索引的集合的寫入性能比其他集合要差。全文本索引也會降低分片時的數(shù)據(jù)遷移速度:將數(shù)據(jù)遷移到其他分片時,所有文本都需要重新進(jìn)行索引。
寫作本書時,全文本索引仍然只是一個處于“試驗階段”的功能,所以需要專門啟用這個功能才能進(jìn)行使用。啟動MongoDB時指定--setParameter textSearch Enabled=true選項,或者在運行時執(zhí)行setParameter命令,都可以啟用全文本索引:
> db.adminCommand({"setParameter" : 1, "textSearchEnabled" : true})
假如我們使用這個非官方的Hacker News JSON API(http://api.ihackernews.com)將最近的一些文章加載到了MongoDB中。
為了進(jìn)行文本搜索,首先需要創(chuàng)建一個"text"索引:
> db.hn.ensureIndex({"title" : "text"})
現(xiàn)在,必須通過text命令才能使用這個索引(寫作本書時,全文本索引還不能用在“普通”查詢中):
test> db.runCommand({"text" : "hn", "search" : "ask hn"}) { "queryDebugString" : "ask|hn||||||", "language" : "english", "results" : [ { "score" : 2.25, "obj" : { "_id" : ObjectId("50dcab296803fa7e4f000011"), "title" : "Ask HN: Most valuable skills you have?", "url" : "/comments/4974230", "id" : 4974230, "commentCount" : 37, "points" : 31, "postedAgo" : "2 hours ago", "postedBy" : "bavidar" } }, { "score" : 0.5625, "obj" : { "_id" : ObjectId("50dcab296803fa7e4f000001"), "title" : "Show HN: How I turned an old book...", "url" : "http://www.howacarworks.com/about", "id" : 4974055, "commentCount" : 44, "points" : 95, "postedAgo" : "2 hours ago", "postedBy" : "AlexMuir" } }, { "score" : 0.5555555555555556, "obj" : { "_id" : ObjectId("50dcab296803fa7e4f000010"), "title" : "Show HN: ShotBlocker - iOS Screenshot detector...", "url" : "https://github.com/clayallsopp/ShotBlocker", "id" : 4973909, "commentCount" : 10, "points" : 17, "postedAgo" : "3 hours ago", "postedBy" : "10char" } } ], "stats" : { "nscanned" : 4, "nscannedObjects" : 0, "n" : 3, "timeMicros" : 89 }, "ok" : 1 }
匹配到的文檔是按照相關(guān)性降序排列的:"Ask HN"位于第一位,然后是兩個部分匹配的文檔。每個對象前面的"score"字段描述了每個結(jié)果與查詢的匹配程度。
如你所見,這個搜索是不區(qū)分大小寫不的,至少對于[a-zA-Z]這些字符是這樣。全文本索引會使用toLower將單詞變?yōu)樾?,但這是與本地化相關(guān)的,所以某些語言的用戶可能會發(fā)現(xiàn)MongoDB會不可預(yù)測性地變得區(qū)分大小寫,這取決于toLower在不同字符集上的行為。MongoDB一直在努力提高對不同字符集的支持。
全文本索引只會對字符串?dāng)?shù)據(jù)進(jìn)行索引:其他的數(shù)據(jù)類型會被忽略,不會包含在索引中。一個集合上最多只能有一個全文本索引,但是全文本索引可以包含多個字段:
> db.blobs.ensureIndex({"title" : "text", "desc" : "text", "author" : "text"})
與“普通”的多鍵索引不同,全文本索引中的字段順序不重要:每個字段都被同等對待??梢詾槊總€字段指定不同的權(quán)重來控制不同字段的相對重要性:
> db.hn.ensureIndex({"title" : "text", "desc" : "text", "author" : "text"}, ... {"weights" : {"title" : 3, "author" : 2}})
默認(rèn)的權(quán)重是1,權(quán)重的范圍可以是1~1 000 000 000。使用上面的代碼設(shè)置權(quán)重之后,"title"字段成為其中最重要的字段,"author"其次,最后是"desc"(沒有指定,因此它的權(quán)重是默認(rèn)值1)。
索引一經(jīng)創(chuàng)建,就不能改變字段的權(quán)重了(除非刪除索引再重建),所以在生產(chǎn)環(huán)境中創(chuàng)建索引之前應(yīng)該先在測試數(shù)據(jù)集上實際操作一下。
對于某些集合,我們可能并不知道每個文檔所包含的字段??梢允褂?$**"在文檔的所有字符串字段上創(chuàng)建全文本索引:這不僅會對頂級的字符串字段建立索引,也會搜索嵌套文檔和數(shù)組中的字符串字段:
> db.blobs.ensureIndex({"$**" : "text"})
也可以為"$**"設(shè)置權(quán)重:
> db.hn.ensureIndex({"whatever" : "text"}, ... {"weights" : {"title" : 3, "author" : 1, "$**" : 2}})
"whatever"可以指代任何東西。在設(shè)置權(quán)重時指明了是對所有字段進(jìn)行索引,因此MongoDB并不要求你明確給出字段列表。
搜索語法默認(rèn)情況下,MongoDB會使用OR連接查詢中的每個詞:“ask OR hn”。這是執(zhí)行全文本查詢最有效的方式,但是也可以進(jìn)行短語的精確匹配,以及使用NOT。為了精確查詢“ask hn”這個短語,可以用雙引號將查詢內(nèi)容括起來:
> db.runCommand({text: "hn", search: ""ask hn""}) { "queryDebugString" : "ask|hn||||ask hn||", "language" : "english", "results" : [ { "score" : 2.25, "obj" : { "_id" : ObjectId("50dcab296803fa7e4f000011"), "title" : "Ask HN: Most valuable skills you have?", "url" : "/comments/4974230", "id" : 4974230, "commentCount" : 37, "points" : 31, "postedAgo" : "2 hours ago", "postedBy" : "bavidar" } } ], "stats" : { "nscanned" : 4, "nscannedObjects" : 0, "n" : 1, "nfound" : 1, "timeMicros" : 20392 }, "ok" : 1 }
這比使用OR的匹配慢一些,因為MongoDB首先要執(zhí)行一個OR匹配,然后再對匹配結(jié)果進(jìn)行AND匹配。
可以將查詢字符串的一部分指定為字面量匹配,另一部分仍然是普通匹配:
> db.runCommand({text: "hn", search: ""ask hn" ipod"})
這會精確搜索"ask hn"這個短語,也會可選地搜索"ipod"。
也可以使用"-"字符指定特定的詞不要出現(xiàn)在搜索結(jié)果中:
> db.runCommand({text: "hn", search: "-startup vc"})
這樣就會返回匹配“vc”但是不包含“startup”這個詞的文檔。
優(yōu)化全文本搜索有幾種方式可以優(yōu)化全文本搜索。如果能夠使用某些查詢條件將搜索結(jié)果的范圍變小,可以創(chuàng)建一個由其他查詢條件前綴和全文本字段組成的復(fù)合索引:
> db.blog.ensureIndex({"date" : 1, "post" : "text"})
這就是局部的全文本索引,MongoDB會基于上面例子中的"date"先將搜索范圍分散為多個比較小的樹。這樣,對于特定日期的文檔進(jìn)行全文本查詢就會快很多了。
也可以使用其他查詢條件后綴,使索引能夠覆蓋查詢。例如,如果要返回"author"和"post"字段,可以基于這兩個字段創(chuàng)建一個復(fù)合索引:
> db.blog.ensureIndex({"post" : "text", "author" : 1})
前綴和后綴形式也可以組合在一起使用:
> db.blog.ensureIndex({"date" : 1, "post" : "text", "author" : 1})
這里的前綴索引字段和后綴索引字段都不可以是多鍵字段。
創(chuàng)建全文本索引會自動在集合上啟用usePowerOf2Sizes選項,這個選項可以控制空間的分配方式。這個選項能夠提高寫入速度,所以不要禁用它。
當(dāng)一個文檔被插入之后(或者索引第一次被創(chuàng)建之后),MongoDB會查找索引字段,對字符串進(jìn)行分詞,將其減小為一個基本單元(essential unit)。然后,不同語言的分詞機(jī)制是不同的,所以必須指定索引或者文檔使用的語言。文本類型的索引允許指定"default_language"選項,它的默認(rèn)值是"english",可以被設(shè)置為多種其他語言(MongoDB的在線文檔提供了最新的支持語言列表)。
例如,要創(chuàng)建一個法語的索引,可以這么做:
> db.users.ensureIndex({"profil" : "text", "intérêts" : "text"}, ... {"default_language" : "french"})
這樣,這個索引就會默認(rèn)使用法語的分詞機(jī)制,除非指定了其他的分詞機(jī)制。如果在插入文檔時指定"language"字段,就可以為每個文檔分別指定分詞時使用的語言:
> db.users.insert({"username" : "swedishChef", ... "profile" : "Bork de bork", language : "swedish"})
上一篇文章:MongoDB指南---13、索引類型、索引管理
下一篇文章:MongoDB指南---15、特殊的索引和集合:地理空間索引、使用GridFS存儲文件
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/19577.html
摘要:固定集合不能被分片。為固定集合指定文檔數(shù)量限制時,必須同時指定固定集合的大小。沒有索引的集合默認(rèn)情況下,每個集合都有一個索引。 上一篇文章:MongoDB指南---13、索引類型、索引管理下一篇文章:MongoDB指南---15、特殊的索引和集合:地理空間索引、使用GridFS存儲文件 本章介紹MongoDB中一些特殊的集合和索引類型,包括: 用于類隊列數(shù)據(jù)的固定集合(capped...
摘要:復(fù)合唯一索引也可以創(chuàng)建復(fù)合的唯一索引。中的稀疏索引與關(guān)系型數(shù)據(jù)庫中的稀疏索引是完全不同的概念。但是這里不會指明索引是否是多鍵索引。上一篇文章指南使用和何時不應(yīng)該使用索引下一篇文章指南特殊的索引和集合固定集合索引全文本索引 上一篇文章:MongoDB指南---12、使用explain()和hint()、何時不應(yīng)該使用索引下一篇文章:MongoDB指南---14、特殊的索引和集合:固定集合...
摘要:復(fù)合唯一索引也可以創(chuàng)建復(fù)合的唯一索引。中的稀疏索引與關(guān)系型數(shù)據(jù)庫中的稀疏索引是完全不同的概念。但是這里不會指明索引是否是多鍵索引。上一篇文章指南使用和何時不應(yīng)該使用索引下一篇文章指南特殊的索引和集合固定集合索引全文本索引 上一篇文章:MongoDB指南---12、使用explain()和hint()、何時不應(yīng)該使用索引下一篇文章:MongoDB指南---14、特殊的索引和集合:固定集合...
摘要:下面列出了使用作為文件存儲的理由。如果已經(jīng)在使用,那么可以使用來代替獨立的文件存儲工具。在中,文件存儲的集中度會比較高,因為是以為單位來分配數(shù)據(jù)文件的。揭開的面紗是一種輕量級的文件存儲規(guī)范,用于存儲中的普通文檔。 上一篇文章:MongoDB指南---14、特殊的索引和集合:固定集合、TTL索引、全文本索引下一篇文章:MongoDB指南---16、聚合 地理空間索引 MongoDB支持...
摘要:下面列出了使用作為文件存儲的理由。如果已經(jīng)在使用,那么可以使用來代替獨立的文件存儲工具。在中,文件存儲的集中度會比較高,因為是以為單位來分配數(shù)據(jù)文件的。揭開的面紗是一種輕量級的文件存儲規(guī)范,用于存儲中的普通文檔。 上一篇文章:MongoDB指南---14、特殊的索引和集合:固定集合、TTL索引、全文本索引下一篇文章:MongoDB指南---16、聚合 地理空間索引 MongoDB支持...
閱讀 2468·2021-11-19 09:40
閱讀 3601·2021-11-17 17:08
閱讀 3807·2021-09-10 10:50
閱讀 2229·2019-08-27 10:56
閱讀 1953·2019-08-27 10:55
閱讀 2649·2019-08-26 12:14
閱讀 1002·2019-08-26 11:58
閱讀 1501·2019-08-26 10:43