MongoDB權威指南學習筆記03

餘二五發表於2017-11-07

章節:第三章 建立、更新及刪除文件

時間:2013-07-22

內容:

  1. 插入並儲存文件

    1. 使用insert方法:如

      1
      db.foo.insert({"bar" "baz"})
    2. 批量插入

      1. 批量插入能傳遞一個由文件構成的陣列給資料庫,且一次批量插入只是單個的TCP請求,無需處理大量的訊息頭,避免了許多零碎的請求所帶來的開銷

      2. 只有插入多個文件到一個集合時,才能提高效率,而不能用批量插入一次對多個集合執行操作

      3. 要是隻匯入原始資料,可以使用命令列工具,如mongoimport,而不是批量插入

    3. 插入:原理和作用

      1. 當執行插入時,使用的驅動程式會將資料轉換成BSON形式,然後將其送入資料庫

      2. 資料庫解析BSON,檢驗是否包含“_id”鍵並且文件不超過4MB

      3. MongoDB在插入時不做別的資料校驗,也不執行程式碼,所以也就沒有注入式攻擊的可能,但副作用是允許插入無效的資料

  2. 刪除文件

    1. 刪除特定文件,如

      1
      db.mailing.list.remove({"opt-out" : true})
    2. 刪除集合中的所有文件,如db.users.remove(),但不會刪除集合本身,原有的索引也會保留

    3. 刪除速度:通常刪除文件會很快,但若要清楚整個集合的文件,則直接刪除集合(然後重建索引)會更快,如db.drop_collection(“bar”)即是刪除集合bar

  3. 更新文件

    1. 更新文件的方法update有2個引數,一個是查詢文件,用來找出要更新的文件,另一個是修改器文件,描述對找到的文件做哪些更改

    2. 更新操作是原子的,若是兩個更新同時發生,先到達伺服器的先執行,接著執行另外一個

    3. 文件替換:

      1. 更新最簡單地情形就是完全用一個新文件替代匹配的文件,這適用於模式結構發生了較大變化時,如

        1
        db.users.update({"name" "joe"} , joe)
      2. 常見錯誤就是查詢條件匹配了多個文件,然後更新的時候由於第二次引數的存在就產生重複的“_id”值,資料庫會報錯,不做任何修改

    4. 使用修改器

      1. 通常文件只會有一部分要更新,利用原子的更新修改器,可以使得這種部分更新極為高效

      2. 更新修改器是種特殊的鍵,用來指定複雜的更新操作,比如調整、增加或者刪除鍵,還可以運算元組或者內嵌文件

      3. 使用修改器時,”_id”的值不能改變(而整個文件替換時是可以改變的);其他鍵值,包括其他唯一索引的鍵,都是可以更改的

      4. “$set”修改器:“$set”用來指定一個鍵的值,若鍵不存在,則建立它,如

        1
        db.users.update({"name":"joe"},...{"$set":{"favorite book":"green eggs and ham"}})

        也可以用“$unset”將鍵完全刪除,如

        1
        db.users.update({"name":"joe"},{"$unset":{"favorite book":1}})
      5. “$inc”修改器:“$inc”修改器用來增加已有鍵的值,或者在鍵不存在時建立一個鍵,如

        1
        db.games.update({"game":"pinball","user":"joe"},...{"$inc":{"score":50}})

        “$inc”只能用於整數、長整數或雙精度浮點數,用在其它型別的資料上會導致操作失敗;另外“$inc”鍵的值必須為數字,不能使用字串、陣列或其他非數字的值

      6. 陣列修改器:用在值為陣列的鍵上,如果指定的鍵已存在,”$push”會向已有的陣列末尾加入一個元素,要是沒有就會建立一個新的陣列,如

        1
        db.blog.posts.update({"title":"A blog post"},{$push:{"comments":...{"name":"joe","email":"joe@example.com","content":"nice post."}}})

        如果一個值不在陣列中再將其加進去,可以用“$ne”或“$addToSet”實現,如

        1
        2
        3
        db.papers.update({"authors cited":{"$ne":"Richie"}},...{"$push":{"authors cited":"Richie"}})
        db.users.update({"_id":ObjectId("4b2d75476cc6113d5ee930164")},...{"$addToSet":{"emails":"joe@gmail.com"}})

        將“$addToSet”和“$each”組合起來,可以新增多個不同的值,而用“$ne”和“$push”組合就不能實現,如

        1
        db.users.update({"_id":ObjectId("4b2d75476cc6113d5ee930164"},{"$addToSet":...{"emails":{"$each":["joe@php.net","joe@example.com","joe@python.org"]}}})

        若是把陣列看成佇列或棧,可以用“$pop”,此修改器可以從陣列的任何一端刪除元素,{$pop:{key:1}}從陣列末尾刪除一個元素,{$pop:{key:-1}}則從頭部刪除

        有時需要基於特定條件來刪除元素,而不僅僅是依據位置,“$pull”可以做到,“$pull”會將所有匹配到的部分刪除,如

        1
        db.lists.update({},{"$pull":{"todo":"laundry"}})
      7. 陣列的定位修改器:若是陣列有多個值,而我們只想對其中的一部分進行操作,有2種方法可以實現:通過位置或者定位操作符(“$”),如

        1
        2
        3
        4
        5
        #下標為0表示陣列的第一個元素
        db.blog.update({"post":post_id},...{"$inc":{"comments.0.votes":1}})
        #在大多數情況下,不預先查詢文件就不能知道要修改陣列的下標,故MongoDB提供了定位操作符“$”,用來定位查詢文件已經匹配的元素,並進行更新
        db.blog.update({"comments.author":"John"},...{"$set":{"comments.$.author":"Jim"}})
        #定位符只更新第一個匹配到的元素
      8. 修改器速度:$inc能就地修改,且不需要改變文件的大小,故執行非常快,而資料修改器可能更改了文件的大小,就會慢一些;MongoDB預留了些補白給文件,來適應大小變化,但若超出了原來的空間,則會分配新的空間,就會減慢速度,同時隨著陣列變長,MongoDB需要更長的時間來遍歷整個陣列,對每個陣列的修改也會慢下來。

    5. upsert:是一種特殊的更新,若是沒有文件符合更新條件,就會以這個條件和更新文件為基礎建立一個新的文件,若匹配到了文件,則正常更新;update操作的第3個參數列示是否開啟upsert,如

      1
      db.analytics.update({"url":"/blog"},{"$inc":{"count":3}},true)

      save Shell幫助程式:save是一個shell函式,可以在文件不存在時插入,存在時更新,只有一個引數:文件;若這個文件含有“_id”鍵,save會呼叫upsert,否則呼叫插入,如

      1
      2
      3
      var x = db.foo.findOne()
      x.num = 42
      db.foo.save(x)
    6. 更新多個文件:預設情況下,更新只能對符合匹配條件的第一個文件執行操作,若使所有匹配到的文件都得到更新,可以設定update的第4個引數為true,如

      1
      db.users.update({birthday:"10/13/1978"},...{$set:{gift:"Happy Birthday!"}},false,true)

      要知道多少文件被更新了,可以執行getLastError命令,鍵“n”的值就是要的數字,如

      1
      2
      db.count.update({x:1},{$inc:{x:1}},false,true)
      db.runCommand({getLastError:1})
    7. 返回已更新的文件:用getLastError僅能獲得有限的資訊,並不能返回已更新的文件,可通過findAndModify命令實現,其呼叫方式和普通的更新略有不同,有點慢,因為它要等待資料庫的響應,如

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      ps = db.runCommand({"findAndModify":processes,
      ..."query":{"status":"READY"},
      ..."sort":{"priority":-1},
      ..."update":{"$set":{"status":"RUNNING"}}}).value
      do_something(ps)
      db.process.update({"_id":ps._id},{"$set":{"status":"DONE"}})
      #findAndModify命令中每個鍵的對應值:
      findAndModify:字串,集合名
      query:查詢文件,用來檢索文件的條件
      sort:排序結果的條件
      update:修改器文件,對所找到的文件執行的更新
      remove:布林型別,表示是否刪除文件
      new:布林型別,表示返回的是更新前的文件還是更新後的文件,預設是更新前的文件
      #findAndModify命令的限制:
      #"update"和"remove"必須有一個,也只能有一個;
      #若匹配不到文件,則返回錯誤
      #一次只能處理一個文件,也不能執行upsert操作,只能更新已有文件


瞬間完成:本章討論的3個操作(插入、刪除、更新)都是瞬間完成的,因為它們都不需要等待資料庫響應,只會受客戶端傳送的速度和網路速度的制約;這對於某些應用(如日誌記錄,分析資料等)是可以接受的,但對於某些應用(如付費系統)就不適用了

  1. 安全操作

    1. 若要完成電子商務系統,則需要一個“安全”版本,保證執行時檢查到了錯誤還可以重來;

    2. 安全的版本在執行完了操作後立即執行getLastError命令,來檢查是否執行成功,驅動程式會等待資料庫響應,然後適當地處理錯誤,一般會丟擲一個可被捕獲的異常

    3. “安全”的代價就是效能,即便忽略客戶端處理異常的開銷,等待資料庫響應本身的時間比只傳送訊息的時間多一個數量級,所以,應用程式需要權衡資料的重要性(以及丟失後的後果)及速度需求

  2. 捕獲“常規”錯誤

    1. 安全操作也是一種除錯資料庫“奇怪”行為的好方法,這樣可以避免很多常見的資料庫使用錯誤

    2. 最常見的就是鍵重複的錯誤,即插入一個“_id”值已被佔用的文件

請求和連線

  1. 資料庫會為每一個MongoDB資料庫連線建立一個佇列,存放這個連線的請求

  2. 當客戶端傳送一個請求,會被放到佇列的末尾,只有佇列中的請求都執行完畢,後續的請求才會執行

  3. 所以從單個連線就可以瞭解整個資料庫,並且它總是能讀到自己寫的東西

  4. 每個連線都有獨立的佇列,要是開啟2個shell,就有2個資料庫連線,在一個shell中執行插入,之後在另一個shell中執行查詢不一定能得到插入的文件

  5. 使用Ruby、Python和Java驅動程式時要特別注意這種行為,因為這幾種語言的驅動程式都使用了連線池

本文轉自 xxrenzhe11 51CTO部落格,原文連結:http://blog.51cto.com/xxrenzhe/1254097,如需轉載請自行聯絡原作者


相關文章