1. MongoDB介紹
1.1什么是MongoDB
MongoDB是一種非關系型數據庫(NoSQL),并且是非關系型數據庫里功能最豐富最像關系型數據庫的。數據會優先存儲在內存中,當內存不夠時,只將熱點數據放入內存,其他數據存在磁盤。支持數據持久化。支持排序,支持返回特定字段。
MongoDB 將數據存儲為一個文檔,數據結構由鍵值(key=>value)對組成。MongoDB 文檔叫做BSON類似于 JSON 對象。字段值可以包含其他文檔,數組及文檔數組。在MongoDB中,對于插入的格式并沒有要求,字段類型可以隨意變動。
比如在創建一個集合后,我們可以在這個集合加入下面的數據:
{ "name":"this is a name", "age":12 }
同樣我們也可以在這個數據庫插入這樣的數據:
{ "name":8888, "address":"changsha" }
當插入這兩個數據后,使用MongoDB Compass數據庫可視化工具可以顯示如下(也可以直接用mongo shell工具進行查看):
1.1MongoDB的高可用原理
- Journal:Journal日志是 MongoDB 的預寫日志 WAL,類似 MySQL 的 redo log,然后100ms一次將Journal 日志刷盤。
- Oplog:Oplog 是用來做主從復制的,類似 MySql 里的 binlog。MongoDB 的寫操作都由 Primary 節點負責,Primary 節點會在寫數據時會將操作記錄在 Oplog 中,Secondary 節點通過拉取 oplog 信息,回放操作實現數據同步的。
- Checkpoint:上面提到了 MongoDB 的寫只寫了內存和 Journal 日志 ,并沒有做數據持久化,Checkpoint 就是將內存變更刷新到磁盤持久化的過程。MongoDB 會每60s一次將內存中的變更刷盤,并記錄當前持久化點(checkpoint),以便數據庫在重啟后能快速恢復數據。
- 節點選舉:MongoDB 的節點選舉規則能夠保證在Primary掛掉之后選取的新節點一定是集群中數據最全的一個(會去比較oplog比自己新或者相同才會投票)
從上面 4 點我們可以得出 MongoDB 高可用的如下結論:
- MongoDB 宕機重啟之后可以通過 checkpoint 快速恢復上一個 60s 之前的數據。
- MongoDB 最后一個 checkpoint 到宕機期間的數據可以通過 Journal日志回放恢復。
- Journal日志因為是 100ms 刷盤一次,因此至多會丟失 100ms 的數據
2. MongoDB對比其他類型的數據庫
Redis:
讀寫性能最高超過MongoDB,而且支持存儲list,set(集合), hash等類型。Redis 數據全部存在內存,定期寫入磁盤,當內存不夠時,可以選擇指定的 LRU 算法刪除數據。一旦內存空間不夠,性能會大幅度下降,云計算的數據并不需要這些特殊類型(MongoDB也可以滿足存儲目前的信息了),而且redis不支持條件查詢功能,沒有數據分析功能。
redis應用場景:作為`Key-Value`形態的內存數據庫,可以作為數據緩存(key不能重復,數據量不能太大);好友關系、用戶關注、推薦模型(通過set來取交集)。
Hbase:
以列相關存儲架構進行數據存儲的數據庫,適合大批量數據的處理。從下圖中可以看到HBase的行鍵,由主鍵,時間戳,列簇及列簇里的列和值組成。插入一個數據的操作相對更復雜。小數據量下各項性能HBase和MongoDB寫入性能差不多,海量數據情境下HBase更優秀,但云計算系統并沒有海量的數據需要使用HBase的程度。
HBase的使用場景:頭條類、新聞類的的新聞、網頁、圖片存儲在HBase之中;一些病毒公司的病毒庫也是存儲在HBase之中;滴滴打車的軌跡數據主要存在HBase之中。
Hbase的數據模型如下:
|
Row Key |
Time Stamp |
CF1 |
CF2 |
CF3 |
|
11248112 |
T6 |
|
CF2:q2=v2 |
CF3:q3=val3 |
|
T3 |
|
|
|
|
|
T2 |
CF1:q1=v1 |
|
|
向Hbase中添加數據的命令如下:
3. MongoDB的集群搭建方法
MongoDB集群以一個3節點的主從集群搭建為例:
3.1 需要準備的文件
1)需要準備3個文件夾用于存儲3個節點的數據,假設這3個數據庫要啟動到不同的3個宿主機上,文件夾都可以叫做/var/mongo,每個下面再創建2個文件夾 /var/mongo/conf 和/var/mongo/db
2)準備一個KeyFile用于mongo集群間的通信,命令為sudo openssl rand -base64 512 -out ./mongodb.key 然后需要將這個文件權限改為600,并將此文件放入/var/mongo/conf/下。
3)在各/var/mongo/conf下新建一個mongo.conf文件,內容如下:
dbpath=/data/mongo/db/
auth=false
bind_ip=0.0.0.0
wiredTigerCacheSizeGB=8
replSet=testHYdb
port=27017
#keyFile=/data/mongo/conf/mongodb.key <==這句先不能加。需要等把root用戶創建好,開啟權限認證,重新再啟動集群的時候,再添加這句。
這里需要注意:
1)在首次啟動MongoDB時,需要關閉權限認證。
2)port可以根據需要來進行修改,如果節點都建到不同的宿主機上那么3個節點可以使用相同的port,如果建到相同的宿主機上那么3個mongo需要使用不同的port。
3)replSet這個是MongoDB集群的名字。
4)keyFile配置在首次啟動時這句要去掉,不能寫上。
3.2 啟動mongo
1)使用docker命令進行啟動mongo,下面的命令可以啟動一個節點,一共啟動3個。
docker run -it -d -p 27017:27017 --name mongodb5.0_1 -v /var/mongo/db:/data/mongo/db/ -v /var/mongo/conf/mongo.conf:/data/mongo/conf/mongo.conf mongo:5.0.10 /usr/mongo/bin/mongod -f /data/mongo/mongo.conf
2)使用dockr ps進行查看
3.3 啟動集群
1) 連接任意一個docker里的MongoDB,假設host的ip為10.8.70.40。
docker exec -it 92585b /usr/mongo /bin/mongo 10.8.70.40:27017
2)進去之后輸入以下命令:
var config = {_id:" testHYdb",members:[{_id:0,host:"10.8.70.40:27017"},{_id:1,host:"10.8.70.41:27018"},{_id:2,host:"10.8.70.42:27019"}]}
rs.initiate(config)
rs.secondaryOk()
然后顯示testHYdb:PRIMARY 其中testHYdb是之前conf文件中設置的集群名字,后面的PRIMARY代表這個節點是主節點,從節點會顯示SECONDARY。
3)另外兩個節點的MongoDB中輸入rs.secondaryOk(),這樣可以開啟從節點的讀權限
3.4 創建用戶,開啟權限
1) 首先創建一個root用戶,然后才可以開啟集群的權限
use admin
db.createUser({user:"root",pwd:"admin123!",roles:["root"]}) #創建root用戶
2)開啟權限
將之前創建的mongo.conf文件中的auth改為true,將keyFile=/data/mongo/conf/mongodb.key添加到文件中。
3)重啟MongoDB
3.5測試
1)使用下面的命令連接到MongoDB
docker exec -it 92585b /usr/mongo /bin/mongo 10.8.70.40:27017
2)然后切換到admin這個database中: use admin
3)使用密碼切換到root用戶db.auth(“root”,”$password”) $passwod是root用戶的密碼
4) 測試 插入功能:
> db.createCollection("student")
{ "ok" : 1 }
> db.getCollection('student').insertOne({"name":"a5","age":"11"})
{
"acknowledged" : true,
"insertedId" : ObjectId("62c3e45df7e420ba94fe0b72")
}
5)測試 查詢功能:
> db.getCollection('student').find({"age":{"$gt":10}})
{ "_id" : ObjectId("62c3e45df7e420ba94fe0b72"), "name" : "a5", "age" : 11 }
4. MongoDB的一些常用操作方法
4.1使用Mongo shell的操作方法
4.1.1幫助信息
可以通過以下命令查看各help信息:
help
db.help()
db.yourColl.help()
db.youColl.find().help()
rs.help()
4.1.2 切換/創建數據庫
use yourDB //當不存在database的時候,會自動創建;當存在database時會進行切換
4.1.3 其他常用操作數據庫命令
創建一個collection:
> db.createCollection("student")
{ "ok" : 1 }
插入:
> db.getCollection('student').insertOne({"name":"a2test","age":"9"})
{
"acknowledged" : true,
"insertedId" : ObjectId("62c3e45df7e420ba94fe0b72")
}
刪除:
db.getCollection(‘student’).deleteOne({"name":"a2test"})
查找:
> db.getCollection('student').find({"age":{"$gt":10}}).sort({"age":-1})
sort可以用來排序。
更新:
> db.users.update({age: 9}, {$set: {name: 'changeName'}})
創建用戶:
> db.createUser({user:"root",pwd:"admin123!",roles:["root"]})
4.1.4 MongoDB數據備份、數據恢復
4.2版本以上的MongoDB備份和恢復數據需要另外安裝一個工具包database-tools,然后使用mongodump對數據進行導出為json文件。需要恢復時新建一個空數據的MongoDB,使用mongorestore來恢復。可以使用bsondump xxxx.bson來看備份出來的數據。
具體操作如下:
備份所有庫:
通過/bin/sh進去輸入,記得把bak文件夾要映射到本地(可以先把備的關掉,然后備份完再啟動)
mongodump -u root -p admin123! --port 27017 --authenticationDatabase admin --port 27017 -o ./bak
恢復所有庫:
1.先起一個mongo,然后設置好root賬戶
2.通/bin/sh進去之后
datatools/bin/mongorestore -u root -p admin123! --port 27017 --authenticationDatabase admin ./bak
4.2 通過golang操作MongoDB數據庫的方法
4.2.1 MongoDB的客戶端連接
引入包:
"go.mongodb.org/mongo-driver/bson"
"go.mongodb.org/mongo-driver/mongo"
"go.mongodb.org/mongo-driver/mongo/options"
客戶端代碼:
hosts := []string{192.16.34.100:27017} //需要連接的mongo的地址和端口,如果是集群把所有節點的都寫上
clientOptions := options.Client().SetHosts(hosts) //這句是設置需要連接的mongo的地址
clientOptions.SetAuth(username,password,AuthSource:"admin"}) //如果開啟了權限認證,如果沒開啟可以不加這句
var client *mongo.Client
client, err = mongo.Connect(ctx, clientOptions)
4.2.2 MongoDB的常見操作
插入:
假設有如下數據Student,類型如下,插入一條數據:
type Student struct {
Name string `bson:"name" mapstructure:"name"`
Age int `bson:"age" mapstructure:"age"`
Hobby []string `bson:"hobby" mapstructure:"hobby"`
Tes []Vege `bson:"tes" mapstructure:"tes"`
}
s := Student{Name: "tom", Age: 20}
collection := client.Database("go_db").Collection("test") //連接database
insertResult, err := collection.InsertOne(context.TODO(), s) //插入數據
查詢:
查找并返回特定的字段(只返回memory和id的數據,這樣可以返回的減少數據量)
projection := bson.D{
{"memory", 1},
{"id", 1},
}
cur,err:=collection.Find(ctx,bson.D{{"key","6247569c-47bd-e3c7-c3eefd4435bf0298"}}, options.Find().SetProjection(projection))
更新:
update := bson.D{{"$set", bson.D{{"likenum", 1000}, {"age", 22}}}}
ur, err := c.UpdateMany(ctx, bson.D{{"name", "a2test"}}, update)
前面為更新要匹配的條件,后面為修改的的值。可以只修改某一個字段,其他的沒有修改的不會變,如果沒有這個字段會進行新增。
4.2.3 MongoDB的權限添加
首先在mongo.conf里面把auth=false,關閉權限認證,然后進去之后需要先添加一個root賬戶db.createUser({user:"root",pwd:"admin123!",roles:["root"]}) ,之后就可以繼續其他的賬戶添加權限了。
在admin這個database下面添加,可以給一個賬戶添加多個database的權限。比如db.createUser({user:"mongo1",pwd:"qwe123!",roles:[{role:"readWrite",db:"go_db"},{role:"readWrite",db:"test1"}]})。使用show users可以查看每個賬戶所具有的權限。
然后將auth改為true,重啟數據庫。用mongo命令在admin這個database下可以通過db.auth("username","password")來進行權限認證。