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