標籤:blog http io os 使用 java ar for 檔案
【蘇州需要工作的加我QQ,內推介紹費平分】MongoDB 進階
1.資料庫命令
a.命令的工作原理
drop命令,在shell中刪除一個集合,執行db.refactor.drop().其實這個函數實際啟動並執行是drop命令,
可以用runCommand來達到一樣的效果:
db.runCommand({"drop":"refactor"})
{ "nIndexesWas" : 1, "msg" : "indexes dropped for collection", "ns" : "test.refactor", "ok" : 1 } 命令的響應是一個文檔,包含了命令是否執行成功,還可能有些其他的命令輸出的資訊.命令的響應文檔
都有一個"ok"鍵.ok鍵的值為1表示執行成功,為0表示執行失敗,當為0時,會有一個"errmsg"鍵,
它的值表示命令失敗的原因.
MongoDB中的命令其實是作為一種特殊類型的查詢實現的,這些查詢針對$cmd集合來執行.runCommand
僅僅是接受命令文檔,執行等價查詢,因此drop調用實際上是這樣的:
db.$cmd.findOne({"drop":"refactor"})
當MongoDB伺服器得到查詢$cmd集合的請求時,會啟動一套特殊的邏輯來處理,而不是交給普通的查詢代碼來執行.
幾乎所有MongoDB驅動程式都提供一個類似runCommand的協助方法來執行命令,如果有必要,也可以使用一個
簡單查詢的方式來運行命令.
訪問有些命令需要有管理員權限,必須在admin資料庫裡面運行.
b)要獲得所有命令的最新列表,可以在shell中運行db.listCommands(),
也可以使用http://localhost:28017/_commands
2.固定集合
MongoDB不僅支援普通集合,還支援 固定集合,固定集合要事先建立,而且大小固定.
固定集合像一個環形隊列,如果空間不足,最早的文檔就會被刪除,為新的文檔提供空間.
固定集合在新文檔插入的時候自動淘汰最早的文檔.
固定集合不能刪除文檔(自動淘汰文檔除外),更新(尺寸增大)將導致文檔移動.
固定集合中的文檔以插入的順序儲存,不用維護一個已刪除的文檔釋放空間列表.
固定集合預設情況下沒有索引,即便是"_id"上也沒有索引.
a.固定集合的屬性和用法
對固定集合插入的速度很快,做插入操作時,無需額外分配空間,伺服器也不必尋找空閑列表來放置文檔,
直接將文檔插入集合的末尾就行了,如果有必要就將舊的覆蓋.預設情況下插入也無需更新索引.
對固定集合按照插入順序輸出的查詢速度很快,以為文檔本身就是按照插入順序儲存的,按照這個
順序查詢就是遍曆一下,返回結果的順序就是文檔在磁碟上的順序.預設情況下,對固定集合進行尋找都
會以插入的順序返回結果.
固定集合能在新資料插入時,自動淘汰最早的資料.插入快速,按照插入順序查詢也很快,自動淘汰,這幾個
屬性群組合起來使得固定集合特別適合像日誌這種應用情境.事實上,MongoDB中設計固定集合的目的
就是用來儲存內部的複製日誌oplog.固定集合還有一個用法是緩衝少量的文檔,一般來說,固定集合
使用與任何想要自動淘汰到期屬性的情境.
b.建立固定集合
固定集合必須要在使用前顯示的建立.
如:
db.createCollection("refactorCapped",{"capped":true,size:100000,max:100})
上面的命令建立了一個固定集合refactorCapped,大小是100000位元組,最大的文檔數100.
當指定文檔數量的上限時,必須同時指定大小.淘汰機制只有在容量還沒有滿時才會依據文檔數量來工作.
要是容量滿了,淘汰機制則會依據容量來工作.
可以將普通集合轉化成固定集合,如將blog集合轉換成大小為10000位元組的固定集合
db.runCommand({convertToCapped:"blog",size:10000})
c.自然排序
固定集合的排序方式叫做自然排序.自然排序就是文檔在磁碟上的順序.
因為固定集合的文檔總是按照插入的順序儲存的,自然順序就是這樣的.預設情況下,
查詢固定集合後就是按照插入的順序返迴文檔.也可以使用自然排序按照反向插入的順序查詢
如
db.blog.find().sort({"$natural":-1})
3.GridFS
GridFS是一種在MongoDB中儲存大二進位檔案的機制,使用GridFS存檔案的原因:
GridFS可以直接利用已經建立的複製或分區機制,對檔案儲存體來說故障恢複和擴充都很容易
GridFS可以避免用於儲存使用者上傳內容的檔案系統出現的某些問題,如GridFS在同一個目錄下放置大量的檔案是沒有問題的.
GridFS不產生磁碟片段,因為MongoDB分配資料檔案空間時以2GB為一塊.
a.使用GridFS
最簡單使用GridFS的方法是利用mongofiles.mongofiles可以用來在GridFS中上傳,下載,列示,尋找和刪除檔案.
可以用 mongofiles --help獲得協助
如:
b)內部原理
GridFS是一個建立在普通MongoDB文檔基礎上的輕量級檔案儲存體規範.MongoDB伺服器實際上對GridFS請求和普通的
請求一樣,所有相關工作都由用戶端驅動或者工具來完成.
GridFS的基本思想是可以將大檔案分成很多塊,每塊作為一個單獨的文檔儲存,這樣就能儲存大檔案了.由於MongoDB
支援在文檔中儲存位元據,可以最大限度減小塊的儲存開銷.另外,除了隱藏檔本身的快,還有一個單獨的文檔用來
儲存分塊的資訊和檔案的中繼資料.
GridFS的塊有個單獨的集合,預設情況下,塊將使用fs.chunks集合,如果需要可以覆蓋.這個塊集合裡面文檔的結構很簡單
{
"_id":ObjectId("...."),
"n":0,
"data":BindData("..."),
"files_id":ObjectId("....")
}
和別的MongoDB文檔一樣,塊也有自己唯一的"_id".files_id鍵是包含這個塊中繼資料的檔案文檔的"_id".
n表示塊編號,也就是這個塊在源檔案中的順序編號,data包含組成檔案塊的位元據.
檔案的中繼資料放在另一個集合中,預設是fs.files.這裡面的每個文檔代表GridFS中的一個檔案,與檔案相關的
自訂中繼資料也可以存在其中.除了使用者自訂的鍵,GridFS規範定義了一些鍵
_id
檔案唯一的id,在塊中作為files_id鍵的值儲存
length
檔案內容總的位元組數
chunksize
每塊的大小,以位元組為單位,預設是256k,必要時可以調整.
uploadDate
檔案存入GridFS的時間戳記
md5
檔案內容的md5檢驗和,由服務端用filemd5命令產生的,用於計算上傳塊的md5檢驗和
也意味著使用者可以檢驗md5鍵這個值,確保檔案正確上傳了.
db.fs.files.find()
4.伺服器端指令碼
在伺服器端可以通過db.eval函數來執行javascript指令碼,也可以把javascript指令碼儲存在資料庫中,然後
在別的資料庫命令中調用.
a. db.eval
利用db.eval函數可以在MongoDB伺服器端執行javascript指令碼.這個函數先將給定的javascript字串傳遞給
MongoDB伺服器,在伺服器上執行,然後返回結果.
db.eval可以用來類比多文檔事務:db.eval鎖住資料庫,然後執行javascript,再解鎖.雖然沒有內建的復原機制,
但這能確保一系列操作按照指定的數序發生.
發送代碼有兩種方式,封裝一個函數或者不封裝,如:
db.eval("return ‘refactor‘;") db.eval("function(){return ‘refactor‘;}")
只有傳遞參數的時候,才必須要封裝成一個函數.參數通過db.eval的第二個參數傳遞,要寫成一個數組的形式.
如:
db.eval("function(name){return ‘hello,‘+name;}",[‘refactor‘])
若db.eval的運算式要是複雜的話,調試的辦法是將調試資訊寫進資料庫的日誌中
如:
db.eval("print(‘hello refactor‘)")
這樣在日誌裡就能找到hello refactor
b.儲存javascript
每個MongoDB的資料庫中都有個特殊的集合:system.js,用來存放javascript變數.這些變數可以在任何MongoDB的
javascript上下文中調用,包括"$where"子句,db.eval調用,MapReduce作業.用insert可以將變數存在system.js中
如:
db.system.js.insert({"_id":"x","value":1}) db.system.js.insert({"_id":"y","value":2}) db.system.js.insert({"_id":"z","value":3})
上例在全域範圍中定義了x,y,z,對其求和: db.eval("return x+y+z;")
system.js可以存放javascript代碼,這樣就可以很方便的自訂一些指令碼,如用javascript寫一個日誌函數,將其存放在
system.js中:
db.system.js.insert( { "_id":"log", "value":function(msg,level) { var levels=["DEBUG","WARN","ERROR","PATAL"]; level=level?level:0; var now= new Date(); print( now +" "+ levels[level]+msg); } } )
調用:
db.eval("log(‘refactor bolg test‘,1)")
使用儲存的javascript缺點是代碼會與常規的原始碼控制脫離,會弄亂用戶端發送來的javascript.
最適合使用儲存javascript的情況就是程式中有個地方都要用到一個javascript函數,這樣要是更新的話,
只需更新這個函數而不必沒出都修改.要是javascript代碼很長又要繁瑣使用的話,也可以使用儲存javascript,
這樣村一次會節省不少王擴傳輸時間.
c.安全性
執行javascript代碼就要考慮MongoDB的安全性.
如:
>func="function(){print(‘hello,"+username+"!‘);}"
如果username是使用者自訂的,可以使用這樣的字串"‘);db.dropDatabase();print(‘",
代碼就變成了這樣:
>func="function(){print(‘hello,‘);db.dropDatabase();print(‘!‘);}"
為了避免這種情況,要限定範圍.
絕大多數驅動程式都為傳遞給資料庫的代碼提供了一種特殊類型,這是因為代碼實際上可以看成是一個字串和一個
範圍的組合.範圍是一個儲存著變數名和值對應關係的文檔.當javascript函數執行的時候,這種映射就
構成了函數的局部範圍.
5.資料庫引用
DBRef就像url,唯一確定一個到文檔的引用.它自動載入文檔的方式就像網站中url通過連結自動載入web頁面一樣.
a.DBRef是什麼
DBRef是一個內嵌文檔,DBRef有些必選鍵,如:
{"$ref":collectionName,"$id":id_value}
DBRef指向一個集合,還有一個id_value用來在集合裡面根據"_id"確定唯一的文檔.這兩條資訊可以使DBRef能
唯一標識MongoDB資料庫內的任何一個文檔.如想引用另一個資料庫的文檔,DBRef中有可選鍵"$db"
{"$ref":collectionName,"$id":id_value,"$db":database}//注意鍵的順序不能改變.
b.執行個體
兩個集合 users(使用者),notes(筆記),
使用者可以建立筆記,筆記可以引用使用者或者別的筆記.
db.users.insert({"_id":"refactor","displayName":"dis_refactor"}) db.users.insert({"_id":"refactor2","displayName":"dis_refactor2"})
db.notes.insert({"_id":2,"author":"refactor","text":"refactor in mongodb"}) db.notes.insert( { "_id":22, "author":"refactor22", "text":"...DBRef likes url", "references": [ {"$ref":"users","$id":"refactor"}, {"$ref":"notes","$id":2} ] } )
var note=db.notes.findOne({"_id":22}); note.references.forEach( function(ref){ printjson(db[ref.$ref].findOne({"_id":ref.$id})); });
c.什麼時候使用DBRef
在MongoDB中表示這種對其他文檔的參考關聯性,並不是只有DBRef方式.
上面的例子就用了另外一種引用:每個note的author鍵僅儲存了author文檔的"_id"鍵,沒有必要用DBRef,因為已經
知道每個author就是users集合裡面的一個文檔.這種引用在GridFS的塊文檔中"files_id"鍵僅僅就是對文檔"_id"的引用.
在儲存引用的時候是選擇DBRef還是至儲存"_id"?
儲存"_id"會更加緊湊,對開發人員而言就很輕量.但是DBRef能夠引用任意集合(甚至是任意資料庫)的文檔,開發人員
不必知道和記住被引用的文檔在哪些集合裡面.驅動程式和一些工具對DBRef提供了額外的功能(如自動去引用).
總之,儲存一些對 不同 集合的 文檔的引用時,最好用DBRef.否則最好儲存"_id"作為引用來使用,這樣更簡單,也更容易操作.
MongoDB 進階-關聯查詢