簡明 MongoDB 入門教程

notebin發表於2017-08-08

MongoDB 是免費開源的跨平臺 NoSQL 資料庫,命名源於英文單詞 humongous,意思是「巨大無比」,可見開發組對 MongoDB 的定位。與關係型資料庫不同,MongoDB 的資料以類似於 JSON 格式的二進位制文件儲存:

{
    name: "Angeladady",
    age: 18,
    hobbies: ["Steam", "Guitar"]
}

文件型的資料儲存方式有幾個重要好處:文件的資料型別可以對應到語言的資料型別,如陣列型別(Array)和物件型別(Object);文件可以巢狀,有時關係型資料庫涉及幾個表的操作,在 MongoDB 中一次就能完成,可以減少昂貴的連線花銷;文件不對資料結構加以限制,不同的資料結構可以儲存在同一張表。

MongoDB 的文件資料模型和索引系統能有效提升資料庫效能;複製集功能提供資料冗餘,自動化容災容錯,提升資料庫可用性;分片技術能夠分散單伺服器的讀寫壓力,提高併發能力,提升資料庫的可擴充性。MongoDB 高效能,高可用性、可擴充套件性等特點,使其至 2009 年釋出以來,逐漸被認可,並被越來越多的用於生產環境中。AWS、GCP、阿里雲等雲平臺都提供了十分便捷的 MongoDB 雲服務。

開啟 MongoDB 之旅

第一步,安裝 MongoDB。到官網下載壓縮包或安裝包,解壓或安裝後,可以把 bin 目錄新增到環境變數,方便後續命令的執行。在開始階段,要用到 mongo 和 mongod 兩個命令。文章示例以 Linux 為標準。

啟動例項

第二步,通過 mongod 命令啟動例項。啟動之前,新建一個目錄來存放 mongoDB 的資料和日誌:

mkdir mongoDB_example
cd mongoDB_example
mkdir bin data conf log
touch log/mongodb.log

上面的命令建立了四個目錄:

  • bin 如果不打算把 mongodb 的命令放到環境變數,可以將常用命令複製到 bin 目錄,方便呼叫,如 mongo 和 mongod。也可以把編寫的指令碼,如副本集的啟動指令碼,放在 bin 目錄裡

  • data 用來存放資料

  • log 用來存放日誌檔案

  • conf 用來存放配置檔案

在專案目錄中,使用 mongod 命令來啟動 mongoDB 程式:

mongod --fork --port 12345 --dbpath datadb  --logpath log/mongodb.log

上面命令中選項的意義:

  • port 指定埠

  • dbpath 指定資料存放路徑

  • logpath 指定日誌存放路徑

  • fork 後臺執行(退出 shell 後程式不會被中斷了)

啟動順利的話,就可以輸入ps aux | grep mongod,檢視到啟動的 mongod 例項。如果啟動沒有成功,最好先檢查下路徑設定和埠占用。

配置檔案啟動例項

相比設定選項來啟動例項,通過配置檔案啟動更具有可維護性。在 conf 目錄下新建一個配置檔案:

vi conf/mongodb.conf

配置檔案使用<option> = <value>的格式。接下來,把剛才的啟動配置輸入到配置檔案中:

port = 12345
dbpath = data
logpath = log/mongodb.log
fork = true

啟動之前別忘了先殺掉之前啟動的例項kill -2 <pid>,然後再重新啟動 mongoDB:

mongod -f conf/mongodb.conf

在沒有指定介面時,預設啟動在 27017 埠。在配置的時候使用絕對路徑替代相對路徑,有利於在排查故障時查詢 mongoDB 程式啟動的目錄。

連線例項

第三步,通過 mongo 命令來連線 mongoDB 例項:

mongo [options] [db address] [file names]

之前啟動例項的是在本地 12345 埠,安全模式未被開啟,所以不需要輸入使用者名稱和密碼即可直接連線:

mongo 127.0.0.1:12345

或者通過--host--port選項指定主機和埠。一切順利的話,就進入了 mongoDB shell,shell 會報出一連串許可權警告,不過不用擔心,這並不會影響之後的操作。在新增授權使用者和開啟認證後,這些警告會自動消失。

資料庫 CRUD 操作

在進行增查改刪操作之前,先了解下常用的 shell 操作:

  • db 顯示當前所在資料庫,預設為 test

  • show dbs 列出可用資料庫

  • show tables show collections 列出資料庫中可用集合

  • use <database> 用於切換資料庫

mongoDB 預設有兩個資料庫,admin 和 local,admin 用來存放系統資料,local 用來存放該例項資料,在副本集中,一個例項的 local 資料庫對於其它例項是不可見的。使用 use 命令切換資料庫:

> use admin
> use local
> use newDatabase

可以 use 一個不存在的資料庫,當你存入新資料時,mongoDB 會建立這個資料庫:

> use newDatabase
> db.newCollection.insert({x:1})
WriteResult({ "nInserted" : 1 })

以上命令向資料庫中插入一個文件,返回 1 表示插入成功,mongoDB 自動建立 newCollection 集合和資料庫 newDatabase。下面將建立一個 drivers 集合,進行增查改刪操作。

建立(Create)

MongoDB 提供 insert 方法建立新文件:

  • db.collection.inserOne() 插入單個文件
    WriteResult({ “nInserted” : 1 })

  • db.collection.inserMany() 插入多個文件

  • db.collection.insert() 插入單條或多條文件

這裡以 insert 方法為例:

> db.drivers.insert({name:"Chen1fa",age:18})
> db.drivers.insert({name:"Xiaose",age:35})
> db.drivers.insert({_id:91,name:"Sun1feng",age:34})

要注意,age:18age:"18"是不一樣的,前者插入的是數值,後者插入的是字串。插入新文件如果未指定 _id,mongoDB 會自動為插入的文件新增 _id 欄位。使用 db.dirvers.find() 命令即可看到剛剛插入的文件:

> db.dirvers.find()
{ "_id" : ObjectId("598964bd56b8c69ae1e5f36a"), "name" : "Chen1fa", "age" : 18 }
{ "_id" : ObjectId("598964d456b8c69ae1e5f36b"), "name" : "Xiaose", "age" : 35 }
{ "_id" : 91, "name" : "Sun1feng", "age" : 34 }

查詢(Read)

MongoDB 提供 find 方法查詢文件,第一個引數為查詢條件:

> db.drivers.find() #查詢所有文件
{ "_id" : ObjectId("598964bd56b8c69ae1e5f36a"), "name" : "Chen1fa", "age" : 18 }
{ "_id" : ObjectId("598964d456b8c69ae1e5f36b"), "name" : "Xiaose", "age" : 35 }
{ "_id" : 91, "name" : "Sun1feng", "age" : 34 }

> db.drivers.find({name: "Xiaose"}) #查詢 name 為 Xiaose 的文件
{ "_id" : ObjectId("598964d456b8c69ae1e5f36b"), "name" : "Xiaose", "age" : 35 }

> db.drivers.find({age:{$gt:20}}) #查詢 age 大於 20 的文件
{ "_id" : ObjectId("598964d456b8c69ae1e5f36b"), "name" : "Xiaose", "age" : 35 }
{ "_id" : 91, "name" : "Sun1feng", "age" : 34 }

上述程式碼中的$gt對應於大於號>的轉義。第二個引數可以傳入投影(projection,投影儀中,白色光源通過分光鏡、液晶板、投影鏡頭的光學變換後,投射到反射面上,顯示出了彩色影像)文件對映資料:

> db.drivers.find({age:{$gt:20}},{name:1})
{ "_id" : ObjectId("598964d456b8c69ae1e5f36b"), "name" : "Xiaose" }
{ "_id" : 91, "name" : "Sun1feng" }

上述命令將查詢 age 大於 20 的文件,返回 name 欄位,排除其它欄位。投影文件中欄位為 1 或真值表示包含,0 或假值表示排除,可以設定多個欄位為 1 或 0,但不能混合使用。

除此之外,還可以通過 count、skip、limit 等指標(Cursor)方法,改變文件查詢的執行方式:

> db.drivers.find().count() #統計查詢文件數目
3
> db.drivers.find().skip(1).limit(10).sort({age:1})
{ "_id" : 91, "name" : "Sun1feng", "age" : 34 }
{ "_id" : ObjectId("598964d456b8c69ae1e5f36b"), "name" : "Xiaose", "age" : 35 }

上述查詢命令跳過 1 個文件,限制輸出 10 個,以 name 子段正序排序(大於 0 為正序,小於 0 位反序)輸出結果。最後,可以使用 Cursor 方法中的 pretty 方法,提升查詢文件的易讀性,特別是在檢視巢狀的文件和配置檔案的時候:

> db.drivers.find().pretty()

更新(Update)

MongoDB 提供 updata 方法更新文件:

  • db.collection.updateOne() 更新最多一個符合條件的文件

  • db.collection.updateMany() 更新所有符合條件的文件

  • db.collection.replaceOne() 替代最多一個符合條件的文件

  • db.collection.update() 預設更新一個文件,可配置 multi 引數,跟新多個文件

以 update() 方法為例。其格式:

> db.collection.update(
    <query>,
    <update>,
    {
        upsert: <boolean>,
        multi: <boolean>
    }
)

各引數意義:

  • query 為查詢條件

  • update 為修改的文件

  • upsert 為真,查詢為空時插入文件

  • multi 為真,更新所有符合條件的文件

下面的命令將 name 欄位為 Chen1fa 的文件,更新 age 欄位為 30:

> db.drivers.update({name:"Chen1fa"},{name:"Chen1fa", age:30})

要注意的是,如果更新文件只傳入 age 欄位,那麼文件會被更新為{age: 30},而不是{name:"Chen1fa", age:30}。要避免文件被覆蓋,需要用到 $set 指令,$set 僅替換或新增指定欄位:

> db.drivers.update({name:"Chen1fa"},{$set:{age:30}})

如果要在查詢的文件不存在的時候插入文件,要把 upsert 引數設定真值:

> db.drivers.update({name:"Alen"},{age:24},{upsert: true})

update 方法預設情況只更新一個文件,如果要更新符合條件的所有文件,要把 multi 設為真值,並使用 $set 指令:

> db.drivers.update({age:{$gt:25}},{$set:{license:`A`}},{multi: true})
> db.drivers.update({age:{$lt:25}},{$set:{license:`C`}},{multi: true})

最終結果:

> db.dirvers.find()
{ "_id" : ObjectId("598964bd56b8c69ae1e5f36a"), "name" : "Chen1fa", "age" : 30, "license" : "A" }
{ "_id" : ObjectId("598964d456b8c69ae1e5f36b"), "name" : "Xiaose", "age" : 35, "license" : "A" }
{ "_id" : 91, "name" : "Sun1feng", "age" : 34, "license" : "A" }
{ "_id" : ObjectId("598968b3ed1eccef17e79abe"), "age" : 24, "license" : "C" }

刪除(Delete)

MongoDB 提供了 delete 方法刪除文件:

  • db.collection.deleteOne() 刪除最多一個符合條件的文件

  • db.collection.deleteMany() 刪除所有符合條件的文件

  • db.collection.remove() 刪除一個或多個文件

以 remove 方法為例:

> db.drivers.remove({name:"Xiaose"}) #刪除所有 name 為 Xiaose 的文件
> db.drivers.remove({age:{$gt:30}},{justOne:true}) #刪除所有 age 大於 30 的文件
> db.drivers.find()
{ "_id" : ObjectId("598964bd56b8c69ae1e5f36a"), "name" : "Chen1fa", "age" : 30, "license" : "A" }
{ "_id" : ObjectId("598968b3ed1eccef17e79abe"), "age" : 24, "license" : "C" }

MongoDB 提供了 drop 方法刪除集合,返回 true 表面刪除集合成功:

> db.drivers.drop() 

後話

相比傳統關係型資料庫,MongoDB 的 CURD 操作更像是編寫程式,更符合開發人員的直覺,不過 MongoDB 同樣也支援 SQL 語言。MongoDB 的 CURD 引擎配合索引技術、資料聚合技術和 JavaScript 引擎,賦予 MongoDB 使用者更強大的操縱資料的能力。

關閉例項

關閉 mongoDB 服務:

> use admin
> db.shutdownServer()

使用 exit 或 Ctrl + C 斷開連線:

> exit

結語

上述,我們啟動了一個不安全的 MongoDB 例項,應用到生產環境中,還需要了解 MongoDB 的安全機制,瞭解如何建立索引提升資料庫的讀寫速度。隨著資料的增長,需要了解副本集技術如何將多個例項部署到不同的伺服器、瞭解分片技術對資料庫進行橫向擴充套件。為保證伺服器效能和安全,需要了解如何對 MongoDB 進行測試和監控…

「冰封三尺,非一日之寒」,同樣,要想完全掌握 MongoDB 還有很長的路要走。願戒驕戒躁,在學習的路上,與君共勉。

參考

相關文章