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.1 MongoDB的高可用原理
- 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的数据模型如下:
向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")来进行权限认证。