MongoDB
一.介绍
1.1 NoSQL 和MongoDB
NoSQL=Not Only SQL,⽀持类似SQL的功能, 与Relational Database相辅相成。其性能较⾼,不使⽤SQL意味着没有结构化的存储要求(SQL为结构化的查询语句),没有约束之后架构更加灵活。
NoSQL数据库四⼤家族 列存储 Hbase,键值(Key-Value)存储 Redis,图像存储 Neo4j,⽂档存储 MongoDB
MongoDB 是⼀个基于分布式⽂件存储的数据库,由 C++ 编写,可以为 WEB 应⽤提供可扩展、⾼性能、易部署的数据存储解决⽅案。
MongoDB 是⼀个介于关系数据库和⾮关系数据库之间的产品,是⾮关系数据库中功能最丰富、最像关系数据库的。在⾼负载的情况下,通过添加更多的节点,可以保证服务器性能。
1.2 MongoDB 体系结构
RDBMS | MongoDB |
---|---|
database(数据库) | database(数据库) |
table (表) | collection( 集合) |
row( ⾏) | document( BSON ⽂档) |
column (列) | field (字段) |
index(唯⼀索引、主键索引) | index (⽀持地理位置索引、全⽂索引 、哈希索引) |
join (主外键关联) | embedded Document (嵌套⽂档) |
primary key(指定1⾄N个列做主键) | primary key (指定_id field做为主键) |
1.3 BSON
BSON是⼀种类json的⼀种⼆进制形式的存储格式,简称Binary JSON,它和JSON⼀样,⽀持内嵌的⽂档对象和数组对象,但是BSON有JSON没有的⼀些数据类型,如Date和Binary Data类型。BSON可以做为⽹络数据交换的⼀种存储形式,是⼀种schema-less的存储形式,它的优点是灵活性⾼,但它的缺点是空间利⽤率不是很理想。
{key:value,key2:value2} 这是⼀个BSON的例⼦,其中key是字符串类型,后⾯的value值,它的类型⼀般是字符串,double,Array,ISODate等类型。
BSON有三个特点:轻量性、可遍历性、⾼效性
MongoDB使⽤了BSON这种结构来存储数据和⽹络数据交换。Document就是BSON格式
⼀个Document对应关系数据库就是⼀条记录(Record)
MongoDB中Document 中可以出现的数据类型
数据类型 | 说明 | 解释 | 举例 |
---|---|---|---|
String | 字符串 | UTF-8 编码的字符串才是合法的 | {key:"cba"} |
Integer | 整型数值 | 根据你所采⽤的服务器,可分为32位或64位 | {key:1} |
Boolean | 布尔值 | ⽤于存储布尔值 | {key:true} |
Double | 双精度浮点值 | ⽤于存储浮点值 | {key:3.14} |
ObjectId | 对象ID | ⽤于创建⽂档的ID | {_id:new ObjectId()} |
Array | 数组 | ⽤于将数组或列表或多个值存储为⼀个键 | {arr:["a","b"]} |
Timestamp | 时间戳 | 从开始纪元开始的毫秒数 | { ts: new Timestamp() } |
Object | 内嵌⽂档 | ⽂档可以作为⽂档中某个key的value | {o:{foo:"bar"}} |
Null | 空值 | 表示空值或者未定义的对象 | {key:null} |
Date或者ISODate | 格林尼治时间 | ⽇期时间,⽤Unix⽇期格式来存储当前⽇期或时间 | {birth:new Date()} |
Code | 代码 | 可以包含JS代码 | {x:function(){}} |
File | ⽂件 | 1、⼆进制转码(Base64)后存储 (<16M) 2.GridFS(>16M) | GridFS ⽤两个集合来存储⼀个⽂件:fs.files与fs.chunks 真正存储需要使⽤mongofiles -d gridfs put song.mp3 |
1.4 适⽤场景
适用场景:
- ⽹站数据:Mongo ⾮常适合实时的插⼊,更新与查询,并具备⽹站实时数据存储所需的复制及⾼度伸缩性。
- 缓存:由于性能很⾼,Mongo 也适合作为信息基础设施的缓存层。在系统重启之后,由Mongo 搭建的持久化缓存层可以避免下层的数据源过载
- ⼤尺⼨、低价值的数据:使⽤传统的关系型数据库存储⼀些⼤尺⼨低价值数据时会⽐较浪费,在此之前,很多时候程序员往往会选择传统的⽂件进⾏存储。
- ⾼伸缩性的场景:Mongo ⾮常适合由数⼗或数百台服务器组成的数据库,Mongo 的路线图中已经包含对MapReduce 引擎的内置⽀持以及集群⾼可⽤的解决⽅案。
- ⽤于对象及JSON 数据的存储:Mongo 的BSON 数据格式⾮常适合⽂档化格式的存储及查询
具体应⽤场景:
-
游戏场景
使⽤ MongoDB 存储游戏⽤户信息,⽤户的装备、积分等直接以内嵌⽂档的形式存储,⽅便查询、更新。
-
物流场景
使⽤ MongoDB 存储订单信息,订单状态在运送过程中会不断更新,以 MongoDB 内嵌数组的形式来存储,⼀次查询就能将订单所有的变更读取出来。
-
社交场景
使⽤ MongoDB 存储存储⽤户信息,以及⽤户发表的朋友圈信息,通过地理位置索引实现附近的⼈、地点等功能。
-
物联⽹场景
使⽤ MongoDB 存储所有接⼊的智能设备信息,以及设备汇报的⽇志信息,并对这些信息进⾏多维度的分析。
-
直播
使⽤ MongoDB 存储⽤户信息、礼物信息等。
应⽤特征:
- 应⽤必须不需要事务及复杂 join ⽀持
- 新应⽤,需求会变,数据模型⽆法确定,想快速迭代开发
- 应⽤需要2000-3000以上的读写QPS(更⾼也可以)
- 应⽤需要TB甚⾄ PB 级别数据存储
- 应⽤发展迅速,需要能快速⽔平扩展
- 应⽤要求存储的数据不丢失
- 应⽤需要99.999%⾼可⽤
- 应⽤需要⼤量的地理位置查询、⽂本查询
1.5 搭建
去官网下载社区版,本文用的MongoDB 4.2.24,机器是mac,然后将压缩包解压即可
1.5.1 单机
-
cd到解压路径下
-
创建一个保存数据的目录(mongo/data)
-
单机启动:
./bin/mongod
-
指定配置⽂件⽅式的启动
./bin/mongod -f mongo.conf
-
配置⽂件样例
#数据库⽬录 dbpath=mongo/data/ #监听的端⼝ port=27017 # 监听IP地址,默认全部可以访问 bind_ip=0.0.0.0 #是否以后台启动的⽅式启动 fork=true #⽇志路径 logpath = mongo/data/MongoDB.log #是否追加⽇志 logappend = true #是开启⽤户密码登陆 auth=false
-
shell 的启动
#启动mongo shell ./bin/mongo #指定主机和端⼝的⽅式启动 ./bin/mongo --host=主机IP --port=端⼝
1.5.2 复制集
-
cd到解压路径下
-
创建三个保存数据的目录(mongo/data/server1、mongo/data/server2、mongo/data/server3)
-
新建三个conf文件,命名分别是mongo_27017、mongo_27018、mongo_27019
-
mongo_27017.conf
# 主节点配置 dbpath=mongo/data/server1 bind_ip=0.0.0.0 port=27017 fork=true logpath=/data/mongo/logs/server1.log replSet=testReplicaRets
-
mongo_27018.conf
# 主节点配置 dbpath=mongo/data/server2 bind_ip=0.0.0.0 port=27018 fork=true logpath=/data/mongo/logs/server2.log replSet=testReplicaRets
-
mongo_27019.conf
# 主节点配置 dbpath=mongo/data/server3 bind_ip=0.0.0.0 port=27019 fork=true logpath=/data/mongo/logs/server3.log replSet=testReplicaRets
-
启动
./bin/mongod -f mongo_27017.conf ./bin/mongod -f mongo_27018.conf ./bin/mongod -f mongo_27019.conf
-
随便进入一个节点,进行初始化
var cfg ={"_id":"testReplicaRets", "protocolVersion" : 1, "members":[ {"_id":1,"host":"localhost:27017","priority":10}, {"_id":2,"host":"localhost:27018"}, {"_id":3,"host":"localhost:27019"}, ] } rs.initiate(cfg) rs.status()
-
节点的动态增删
增加节点 rs.add("localhost:27019") 删除slave 节点 rs.remove("localhost:27019")
-
仲裁节点动态增删
rs.addArb("localhost:37020")
复制集成员的配置参数
参数字段 | 类型说明 | 取值 | 说明 |
---|---|---|---|
_id | 整数 | _id:0 | 复制集中的标示,不能重复 |
host | 字符串 | host:"主机:端⼝" | 节点主机名 |
arbiterOnly | 布尔值 | arbiterOnly:true | 是否为仲裁(裁判)节点 |
priority | 整数 | priority=0|1 | 权重,默认1,是否有资格变成主节点,取值范围0-1000,0永远不会变成主节点 |
hidden | 布尔值 | hidden=true|false,0|1 | 是否隐藏,权重必须为0,才可以设置 |
votes | 整数 | votes= 0|1 | 是否为投票节点,0 不投票,1投票 |
slaveDelay | 整数 | slaveDelay=3600 | 从库的延迟多少秒 |
buildIndexes | 布尔值 | buildIndexes=true|false,0|1 | 主库的索引,从库也创建,_id索引⽆效 |
1.5.3 分片集群
-
配置config节点集群
-
创建好各个配置节点需要的目录
-
配置conf文件
-
config_37017.conf
dbpath=config_37017 port=37017 bind_ip=0.0.0.0 fork=true logpath = config_37017/config.log logappend = true auth=false replSet=testConfig configsvr=true
-
config_37018.conf
dbpath=config_37018 port=37017 bind_ip=0.0.0.0 fork=true logpath = config_37018/config.log logappend = true auth=false replSet=testConfig configsvr=true
-
config_37019.conf
dbpath=config_37019 port=37017 bind_ip=0.0.0.0 fork=true logpath = config_37019/config.log logappend = true auth=false replSet=testConfig configsvr=true
-
-
启动
./bin/mongod -f config/config_37017.conf ./bin/mongod -f config/config_37018.conf ./bin/mongod -f config/config_37019.conf
-
初始化,要切换到admin库下
use admin var cfg ={"_id":"testConfig", "members":[ {"_id":1,"host":"localhost:37017"}, {"_id":2,"host":"localhost:37018"}, {"_id":3,"host":"localhost:37019"}] }; rs.initiate(cfg)
-
-
配置shard(分片)节点集群
-
创建节点需要的目录
-
配置conf文件
和配置复制集一样,只不过要在配置文件中加上
shardsvr=true
-
启动
#分片1 ./bin/mongod -f config/mongo_27018.conf ./bin/mongod -f config/mongo_27019.conf ./bin/mongod -f config/mongo_27020.conf #分片2 ./bin/mongod -f config/mongo_27021.conf ./bin/mongod -f config/mongo_27022.conf ./bin/mongod -f config/mongo_27023.conf
-
初始化
进入某个分片集群任意节点对该集群进行初始化
#分片1 var cfg ={"_id":"shard1", "protocolVersion" : 1, "members":[ {"_id":1,"host":"localhost:27020"}, {"_id":2,"host":"localhost:27018"}, {"_id":3,"host":"localhost:27019","arbiterOnly":true} ] }; rs.initiate(cfg) rs.reconfig(cfg) rs.status() #分片2 var cfg ={"_id":"shard2", "protocolVersion" : 1, "members":[ {"_id":1,"host":"localhost:27023"}, {"_id":2,"host":"localhost:27021"} {"_id":3,"host":"localhost:27022","arbiterOnly":true} ] } rs.initiate(cfg) rs.reconfig(cfg) rs.status()
-
-
配置路由节点
-
创建节点需要的目录
-
配置conf文件
和配置单机启动时一样,只不过要在配置文件中加上
configdb=testConfig/localhost:37017,localhost:37018,localhost:37019
-
启动
./bin/mongos -f route/route_27017.conf
-
-
为路由添加分⽚节点
进⼊路由mongos
sh.status() sh.addShard("shard1/localhost:27018,localhost:27019,localhost:27020"); sh.addShard("shard1/localhost:27021,localhost:27022,localhost:27023"); sh.status()
-
指定⽚键
进⼊路由mongos
为数据库开启分⽚功能
sh.enableSharding("testdb")
为指定集合开启分⽚功能
sh.shardCollection("testdb.test_datas",{"⽚键字段名如name":索引说明,如hashed})
二.命令
2.1 基本操作
tex
查看数据库:show dbs;
切换数据库 如果没有对应的数据库则创建:use 数据库名;
创建集合:db.createCollection("集合名")
查看集合:show tables;或show collections;
删除集合:db.集合名.drop();
删除当前数据库:db.dropDatabase()
2.2 CURD
2.2.1 插入
单条:db.集合名.insertOne(BSON)
sql
db.testdb.insertOne({name:"张晓峰",birthday:new ISODate("2000-07-01"),gender:1,expectSalary:15000,city:"bj"})
多条:db.集合名.insertMany([BSON,BSON])
sql
db.testdb.insertMany(
[{name:"李丽",birthday:new Date("1996-05-01 14:20:09"),gender:0,expectSalary:21000,city:"sz"},
{name:"李平",birthday:new Date("1997-07-01 14:20:09"),gender:0,expectSalary:22000,city:"sz"}])
2.2.2 查询
db.集合名.find(条件)
比较查询
操作 | 条件格式 | 例⼦ | RDBMS中的条件 |
---|---|---|---|
等于 | {key:value} | db.col.find({字段名:值}).pretty() | where 字段名=值 |
大于 | {key:{$gt:value}} | db.col.find({字段名:{$gt:值}}).pretty() | where 字段名>值 |
大于等于 | {key:{$gte:value}} | db.col.find({字段名:{$gte:值}}).pretty() | where 字段名>=值 |
小于 | {key:{$lt:value}} | db.col.find({字段名:{$lt:值}}).pretty() | where 字段名<值 |
小于等于 | {key:{$lte:value}} | db.col.find({字段名:{$lte:值}}).pretty() | where 字段名<=值 |
不等于 | {key:{$ne:value}} | db.col.find({字段名:{$ne:值}}).pretty() | where 字段名!=值 |
逻辑条件查询
tex
and 条件:MongoDB 的 find() ⽅法可以传⼊多个键(key),每个键(key)以逗号隔开,即常规 SQL 的 AND 条件 db.集合名.find({key1:value1, key2:value2}).pretty()
or 条件:db.集合名.find({$or:[{key1:value1}, {key2:value2}]}).pretty()
not 条件:db.集合名.find({key:{$not:{$操作符:value}}).pretty()
分⻚查询
db.集合名.find({条件}).sort({排序字段:排序⽅式})).skip(跳过的⾏数).limit(⼀⻚显示多少数据)
2.2.3 修改
db.集合名.update(<query>,<update>,{upsert: <boolean>,multi: <boolean>,writeConcern: <document>})
参数说明:
-
query : update的查询条件,类似sql update查询内where后⾯的。
-
update : update的对象和⼀些更新的操作符(如 s e t , set, set,inc...)等,也可以理解为sql update中set后⾯的
- $set:设置字段值
- $unset:删除指定字段
- $inc:对修改的值进⾏⾃增
-
upsert : 可选,这个参数的意思是,如果不存在update的记录,是否插⼊objNew,true为插⼊,默认是false,不插⼊。
-
multi:可选,MongoDB 默认是false,只更新找到的第⼀条记录,如果这个参数为true,就把按条件查出来多条记录全部更新。
-
writeConcern:可选,⽤来指定mongod对写操作的回执⾏为⽐如写的⾏为是否需要确认。
writeConcern 包括以下字段:{ w: , j: , wtimeout: }
-
w:该选项要求确认操作已经传播到指定数量的mongod实例或指定标签的mongod实例
w=0:非应答式写入,不返回任何响应,所以无法知道写入是否成功,但是对于尝试向已关闭的套接字写入或者网络故障会返回socket exceptions和 networking errors
w=1(默认):应答式写入,要求确认操作已经传播到指定的单个mongod实例或副本集主实例
w>1(用于副本集环境):该值用于设定写入节点的数目,包括主节点
w="majority":适用于集群架构,要求写入操作已经传递到绝大多数投票节点以及主节点后进行应答
:要求写入操作已经传递到指定tag标记副本集中的成员后进行应答
-
j:该选项要求确认写操作已经写入journal日志之后应答客户端(需要开启journal功能)
-
wtimeout:指定write concern的时间限制,以防止写操作无限制被阻塞导致无法应答给客户端
wtimeout的单位为ms,当w值大于1时生效,该参数即仅适用于集群环境
当某个节点写入时超出指定wtimeout之后,mongod将返回一个错误
在捕获到超时之前,mongod并不会撤销其他节点已成功完成的写入
wtimeout值为0时等同于没有配置wtimeout选项,容易导致由于某个节点挂起而无法应答
-
对于单实例应答的情形,是将数据写入到内存后开始应答,除非j:true,则保证掉电后不会丢失数据
2.2.4 删除
db.collection.remove(<query>,{justOne: <boolean>,writeConcern: <document>})
参数说明:
- query :(可选)删除的⽂档的条件。
- justOne : (可选)如果设为 true 或 1,则只删除⼀个⽂档,如果不设置该参数,或使⽤默认值 false,则删除所有匹配条件的⽂档。
- writeConcern :(可选)⽤来指定mongod对写操作的回执⾏为。
2.3 聚合操作
聚合是MongoDB的⾼级查询语⾔,它允许我们通过转化合并由多个⽂档的数据来⽣成新的在单个⽂档⾥不存在的⽂档信息。⼀般都是将记录按条件分组之后进⾏⼀系列求最⼤值,最⼩值,平均值的简单操作,也可以对记录进⾏复杂数据统计,数据挖掘的操作。聚合操作的输⼊是集中的⽂档,输出可以是⼀个⽂档也可以是多个⽂档。
分类
-
单⽬的聚合操作(Single Purpose Aggregation Operation)
-
聚合管道(Aggregation Pipeline)
-
MapReduce 编程模型
2.3.1 单⽬的聚合操作
单⽬的聚合命令常⽤的有:count() 和 distinct()
tex
db.lg_resume_preview.find({}).count()
2.3.2 聚合管道(Aggregation Pipeline)
MongoDB中聚合(aggregate)主要⽤于统计数据(诸如统计平均值,求和等),并返回计算后的数据结果。
表达式:处理输⼊⽂档并输出。表达式只能⽤于计算当前聚合管道的⽂档,不能处理其它的⽂档。
表达式 | 描述 |
---|---|
$sum | 计算某个字段的总和或者求count |
$avg | 计算平均值 |
$min | 获取集合中所有⽂档对应值得最⼩值 |
$max | 获取集合中所有⽂档对应值得最⼤值 |
$push | 在结果⽂档中插⼊值到⼀个数组中 |
$addToSet | 在结果⽂档中插⼊值到⼀个数组中,但数据不重复 |
$first | 根据资源⽂档的排序获取第⼀个⽂档数据 |
$last | 根据资源⽂档的排序获取最后⼀个⽂档数据 |
MongoDB 中使⽤ db.COLLECTION_NAME.aggregate([{},...]) ⽅法来构建和使⽤聚合管道,每个⽂档通过⼀个由⼀个或者多个阶段(stage)组成的管道,经过⼀系列的处理,输出相应的结果。
MongoDB的聚合管道将MongoDB⽂档在⼀个管道处理完毕后将结果传递给下⼀个管道处理。管道操作是可以复的。
聚合框架中常⽤的⼏个操作:
- $group:将集合中的⽂档分组,可⽤于统计结果。
- $project:修改输⼊⽂档的结构。可以⽤来重命名、增加或删除域,也可以⽤于创建计算结果以及嵌套⽂档。
- ** m a t c h ∗ ∗ :⽤于过滤数据,只输出符合条件的⽂档。 match**:⽤于过滤数据,只输出符合条件的⽂档。 match∗∗:⽤于过滤数据,只输出符合条件的⽂档。match使⽤MongoDB的标准查询操作。
- $limit:⽤来限制MongoDB聚合管道返回的⽂档数。
- $skip:在聚合管道中跳过指定数量的⽂档,并返回余下的⽂档。
- $sort:将输⼊⽂档排序后输出。
- $geoNear:输出接近某⼀地理位置的有序⽂档。
tex
db.testdb.aggregate([{$group : {_id: "$city", avgSal:{$avg:"$expectSalary"}}},{$project : {city:"$city", salary : "$avgSal"}}])
db.testdb.aggregate([{$group:{_id: "$city",count:{$sum : 1}}}, {$match:{count:{$gt:1}}}])
2.3.3 MapReduce 编程模型
Pipeline查询速度快于MapReduce,但是MapReduce的强⼤之处在于能够在多台Server上并⾏执⾏复杂的聚合逻辑。MongoDB不允许Pipeline的单个聚合操作占⽤过多的系统内存,如果⼀个聚合操作消耗20%以上的内存,那么MongoDB直接停⽌操作,并向客户端输出错误消息。
MapReduce是⼀种计算模型,简单的说就是将⼤批量的⼯作(数据)分解(MAP)执⾏,然后再将结果合并成最终结果(REDUCE)。
tex
db.collection.mapReduce(
function() {emit(key,value);}, //map 函数
function(key,values) {return reduceFunction}, //reduce 函数
{
out: collection,
query: document,
sort: document,
limit: number,
finalize: <function>,
verbose: <boolean>
}
)
使⽤ MapReduce 要实现两个函数 Map 函数和 Reduce 函数,Map 函数调⽤ emit(key, value), 遍历 collection 中所有的记录, 将 key 与 value 传递给 Reduce 函数进⾏处理。
参数说明:
- map:是JavaScript 函数,负责将每⼀个输⼊⽂档转换为零或多个⽂档,⽣成键值对序列,作为 reduce 函数参数
- reduce:是JavaScript 函数,对map操作的输出做合并的化简的操作(将key-value变成key-values,也就是把values数组变成⼀个单⼀的值value)
- out:统计结果存放集合
- query: ⼀个筛选条件,只有满⾜条件的⽂档才会调⽤map函数。
- sort: 和limit结合的sort排序参数(也是在发往map函数前给⽂档排序),可以优化分组机制
- limit: 发往map函数的⽂档数量的上限(要是没有limit,单独使⽤sort的⽤处不⼤)
- finalize:可以对reduce输出结果再⼀次修改
- verbose:是否包括结果信息中的时间信息,默认为fasle
tex
db.testdb.mapReduce(
function() { emit(this.city,this.expectSalary); },
function(key, value) {return Array.avg(value)},
{
query:{expectSalary:{$gt: 15000}},
out:"cityAvgSal"
}
)
三.索引Index
索引是⼀种单独的、物理的对数据库表中⼀列或多列的值进⾏排序的⼀种存储结构,它是某个表中⼀列或若⼲列值的集合和指向这些值数据⻚的逻辑指针清单。
索引⽬标是提⾼数据库的查询效率,没有索引的话,查询会进⾏全表扫描(scan every document in a collection),数据量⼤时严重降低了查询效率。默认情况下Mongo在⼀个集合(collection)创建时,⾃动地对集合的_id创建了唯⼀索引。
3.1 索引类型
- 单键索引 (Single Field)
MongoDB⽀持所有数据类型中的单个字段索引,并且可以在⽂档的任何字段上定义。
对于单个字段索引,索引键的排序顺序⽆关紧要,因为MongoDB可以在任⼀⽅向读取索引。
db.集合名.createIndex({"字段名":排序⽅式})
特殊的单键索引 过期索引TTL(Time To Live)
TTL索引是MongoDB中⼀种特殊的索引, 可以⽀持⽂档在⼀定时间之后⾃动过期删除,⽬前TTL索引只能在单字段上建⽴,并且字段类型必须是⽇期类型。
db.集合名.createIndex({"⽇期字段":排序⽅式}, {expireAfterSeconds: 秒数})
-
复合索引(Compound Index)
复合索引⽀持基于多个字段的索引, 复合索引要注意的重要事项包括:字段顺序与索引⽅向。
db.集合名.createIndex( { "字段名1" : 排序⽅式, "字段名2" : 排序⽅式 } ) 排序⽅式参数是1或-1
-
多键索引(Multikey indexes)
针对属性包含数组数据的情况,MongoDB⽀持针对数组中每⼀个element创建索引,Multikey indexes⽀持strings,numbers和nested documents
-
地理空间索引(Geospatial Index)
针对地理空间坐标数据创建索引。
2dsphere索引,⽤于存储和查找球⾯上的点
2d索引,⽤于存储和查找平⾯上的点
db.company.insert(
{
loc : { type: "Point", coordinates: [ 116.482451, 39.914176 ] },
name: "⼤望路地铁",
category : "Parks"
}
)
db.company.ensureIndex( { loc : "2dsphere" } )
参数为2dsphere 或者 2d。还可以建⽴组合索引。
db.company.find({
"loc" : {
"$geoWithin" : {
"$center":[[116.482451,39.914176],0.05]
}
}
})
-
全⽂索引
MongoDB提供了针对string内容的⽂本查询,Text Index⽀持任意属性值为string或string数组元素的索引询。
注意:⼀个集合仅⽀持最多⼀个Text Index,中⽂分词不理想 推荐ES。
db.集合.createIndex({"字段": "text"}) db.集合.find({"$text": {"$search": "coffee"}})
-
哈希索引 Hashed Index
针对属性的哈希值进⾏索引查询,当要使⽤Hashed index时,MongoDB能够⾃动的计算hash值,⽆需程序计算hash值。注:hash index仅⽀持等于查询,不⽀持范围查询。
db.集合.createIndex({"字段": "hashed"})
3.2 索引管理
-
创建索引并在后台运⾏
db.COLLECTION_NAME.createIndex({"字段":排序⽅式}, {background: true});
-
获取针对某个集合的索引
db.COLLECTION_NAME.getIndexes()
-
索引的⼤⼩
db.COLLECTION_NAME.totalIndexSize()
-
索引的重建
db.COLLECTION_NAME.reIndex()
-
索引的删除
db.COLLECTION_NAME.dropIndex("INDEX-NAME") db.COLLECTION_NAME.dropIndexes() 注意: _id 对应的索引是删除不了的
3.3 explain 分析
explain参数:
-
queryPlanner:queryPlanner是默认参数
参数 含义 plannerVersion 查询计划版本 namespace 要查询的集合(该值返回的是该query所查询的表)数据库.集合 indexFilterSet 针对该query是否有indexFilter parsedQuery 查询条件 winningPlan 被选中的执⾏计划 winningPlan.stage 被选中执⾏计划的stage(查询⽅式),常⻅的有:COLLSCAN/全表扫描、IXSCAN/索引扫描、FETCH/根据索引去检索⽂档、SHARD_MERGE/合并分⽚结果、IDHACK/针对_id进⾏查询等 winningPlan.inputStage ⽤来描述⼦stage,并且为其⽗stage提供⽂档和索引关键字。 winningPlan.stage的child stage 如果此处是IXSCAN,表示进⾏的是index scanning。 winningPlan.keyPattern 所扫描的index内容 winningPlan.indexName winning plan所选⽤的index winningPlan.isMultiKey 是否是Multikey,此处返回是false,如果索引建⽴在array上,此处将是true。 winningPlan.direction 此query的查询顺序,默认是forward,如果⽤了.sort({字段:-1})将显示backward。 filter 过滤条件 winningPlan.indexBounds winningplan所扫描的索引范围,如果没有制定范围就是[MaxKey, MinKey],这主要是直接定位到mongodb的chunck中去查找数据,加快数据读取。 rejectedPlans 被拒绝的执⾏计划的详细返回,其中具体信息与winningPlan的返回中意义相同 serverInfo MongoDB服务器信息 -
executionStats:executionStats会返回执⾏计划的⼀些统计信息(有些版本中和allPlansExecution等同)。
参数 含义 executionSuccess 是否执⾏成功 nReturned 返回的⽂档数 executionTimeMillis 查询的执⾏耗时,越小越好 totalKeysExamined 索引扫描次数 totalDocsExamined ⽂档扫描次数 executionStages 这个分类下描述执⾏的状态 stage 扫描⽅式,具体可选值与上⽂的相同 nReturned 查询结果数量 executionTimeMillisEstimate 检索document获得数据的时间 inputStage.executionTimeMillisEstimate 该查询扫描⽂档 index所⽤时间 works ⼯作单元数,⼀个查询会分解成⼩的⼯作单元 advanced 优先返回的结果数 docsExamined ⽂档检查数⽬,与totalDocsExamined⼀致。检查了总共的document个数,⽽从返回上⾯的nReturned数量 -
allPlansExecution:allPlansExecution⽤来获取所有执⾏计划,结果参数是queryPlanner 参数和executionStats的拼接。
常见的stage的类型如下:
- COLLSCAN:全表扫描
- IXSCAN:索引扫描
- FETCH:根据索引去检索指定document
- SHARD_MERGE:将各个分⽚返回数据进⾏merge
- SORT:表明在内存中进⾏了排序
- LIMIT:使⽤limit限制返回数
- SKIP:使⽤skip进⾏跳过
- IDHACK:针对_id进⾏查询
- SHARDING_FILTER:通过mongos对分⽚数据进⾏查询
- COUNT:利⽤db.coll.explain().count()之类进⾏count运算
- TEXT:使⽤全⽂索引进⾏查询时候的stage返回
- PROJECTION:限定返回字段时候stage的返回
对于普通查询,比较快的stage的组合(查询的时候尽可能⽤上索引):
Fetch+IDHACK
Fetch+IXSCAN
Limit+(Fetch+IXSCAN)
PROJECTION+IXSCAN
SHARDING_FITER+IXSCAN
需要优化的的stage:
COLLSCAN(全表扫描)
SORT(使⽤sort但是⽆index)
COUNT 不使⽤index进⾏count)
3.4 慢查询分析
-
开启内置的查询分析器,记录读写操作效率
db.setProfilingLevel(n,m),n的取值可选0,1,2
- 0:表示不记录
- 1:表示记录慢速操作,如果值为1,m必须赋值单位为ms,⽤于定义慢速查询时间的阈值
- 2:表示记录所有的读写操作
-
查询监控结果
db.system.profile.find().sort({millis:-1}).limit(3)
-
分析慢速查询
应⽤程序设计是否合理
是否有不正确的数据模型
是否有硬件配置问题,是否缺少索引等
-
解读explain结果 确定是否缺少索引
3.5 索引底层实现原理
MongoDB 是⽂档型的数据库,它使⽤BSON 格式保存数据,⽐关系型数据库存储更⽅便。MySql是关系型数据库,数据的关联性是⾮常强的,区间访问是常⻅的⼀种情况,底层索引组织数据使⽤B+树,B+树由于数据全部存储在叶⼦节点,并且通过指针串在⼀起,这样就很容易的进⾏区间遍历甚⾄全部遍历。MongoDB使⽤B-树,所有节点都有Data域,只要找到指定索引就可以进⾏访问,单次查询从结构上来看要快于MySql。
B-树的特点:
- 多路⾮⼆叉树
- 每个节点既保存数据⼜保存索引
- 搜索时相当于⼆分查找
B-树是⼀种⾃平衡的搜索树,形式很简单:
B+树是B-树的变种,B+ 树的特点:
- 多路⾮⼆叉
- 只有叶⼦节点保存数据
- 搜索时 也相当于⼆分查找
- 增加了 相邻节点指针
MongoDB和MySql的差别:
- B+树相邻接点的指针可以⼤⼤增加区间访问性,可使⽤在范围查询等,⽽B-树每个节点 key 和 data 在⼀起适合随机读写 ,⽽区间查找效率很差。
- B+树更适合外部存储,也就是磁盘存储,使⽤B-结构的话,每次磁盘预读中的很多数据是⽤不上的数据。因此,它没能利⽤好磁盘预读的提供的数据。由于节点内⽆ data 域,每个节点能索引的范围更⼤更精确。
- B-树每个节点即保存数据⼜保存索引,树的深度⼤,所以磁盘IO的次数多,B+树只有叶⼦节点保存数据,较B-树⽽⾔深度⼩,磁盘IO少,有利于区间访问
四.架构
4.1 逻辑结构
MongoDB 与 MySQL 中的架构相差不多,底层都使⽤了可插拔的存储引擎以满⾜⽤户的不同需要。⽤户可以根据程序的数据特征选择不同的存储引擎,在最新版本的 MongoDB 中使⽤了 WiredTiger 作为默认的存储引擎,WiredTiger 提供了不同粒度的并发控制和压缩机制,能够为不同种类的应⽤提供了最好的性能和存储率。
在存储引擎上层的就是 MongoDB 的数据模型和查询语⾔了,由于 MongoDB 对数据的存储与 RDBMS 有较⼤的差异,所以它创建了⼀套不同的数据模型和查询语⾔
4.2 数据模型
类型:
-
内嵌
内嵌的⽅式指的是把相关联的数据保存在同⼀个⽂档结构之中。MongoDB的⽂档结构允许⼀个字段或者⼀个数组内的值作为⼀个嵌套的⽂档。
-
引⽤
引⽤⽅式通过存储数据引⽤信息来实现两个不同⽂档之间的关联,应⽤程序可以通过解析这些数据引⽤来访问相关数据。
选择:
-
选择内嵌
-
数据对象之间有包含关系 ,⼀般是数据对象之间有⼀对多或者⼀对⼀的关系 。
-
需要经常⼀起读取的数据。
-
有 map-reduce/aggregation 需求的数据放在⼀起,这些操作都只能操作单个 collection。
-
-
选择引⽤
-
当内嵌数据会导致很多数据的重复,并且读性能的优势⼜不⾜于覆盖数据重复的弊端 。
-
需要表达⽐较复杂的多对多关系的时候 。
-
⼤型层次结果数据集嵌套不要太深。
-
4.3 WiredTiger存储引擎
存储引擎是MongoDB的核⼼组件,负责管理数据如何存储在硬盘和内存上。MongoDB⽀持的存储引擎有MMAPv1,WiredTiger和InMemory。InMemory存储引擎⽤于将数据只存储在内存中,只将少量的元数据(meta-data)和诊断⽇志(Diagnostic)存储到硬盘⽂件中,由于不需要Disk的IO操作,就能获取所需的数据,InMemory存储引擎⼤幅度降低了数据查询的延迟(Latency)。从mongodb3.2开始默认的存储引擎是WiredTiger,3.2版本之前的默认存储引擎是MMAPv1,mongodb4.x版本不再⽀持MMAPv1存储引擎
storage:
journal:
enabled: true
dbPath: /data/mongo/
##是否⼀个库⼀个⽂件夹
directoryPerDB: true
##数据引擎
engine: wiredTiger
##WT引擎配置
WiredTiger:
engineConfig:
##WT最⼤使⽤cache(根据服务器实际情况调节)
cacheSizeGB: 2
##是否将索引也按数据库名单独存储
directoryForIndexes: true
journalCompressor:none (默认snappy)
##表压缩配置
collectionConfig:
blockCompressor: zlib (默认snappy,还可选none、zlib)
##索引配置
indexConfig:
prefixCompression: true
4.3.1 优势
-
⽂档空间分配⽅式
WiredTiger使⽤的是BTree存储 MMAPV1 线性存储 需要Padding
-
并发级别
WiredTiger ⽂档级别锁 MMAPV1引擎使⽤表级锁
-
数据压缩
snappy (默认) 和 zlib ,相⽐MMAPV1(⽆压缩) 空间节省数倍。
-
内存使⽤
WiredTiger 可以指定内存的使⽤⼤⼩。
-
Cache使⽤
WT引擎使⽤了⼆阶缓存WiredTiger Cache, File System Cache来保证Disk上的数据的最终⼀致性。⽽MMAPv1 只有journal ⽇志。
4.3.2 ⽂件和作⽤
- WiredTiger.basecfg: 存储基本配置信息,与 ConfigServer有关系
- WiredTiger.lock: 定义锁操作
- table.wt*: 存储各张表的数据
- WiredTiger.wt: 存储table* 的元数据
- WiredTiger.turtle: 存储WiredTiger.wt的元数据
- journal: 存储WAL(Write Ahead Log)
4.3.3 实现原理
写请求
WiredTiger的写操作会默认写⼊ Cache ,并持久化到 WAL (Write Ahead Log),每60s或Log⽂件达到2G做⼀次 checkpoint (当然我们也可以通过在写⼊时传⼊ j: true 的参数强制 journal ⽂件的同步 ,writeConcern { w: , j: ,wtimeout: }) 产⽣快照⽂件。WiredTiger初始化时,恢复⾄最新的快照状态,然后再根据WAL恢复数据,保证数据的完整性。
Cache是基于BTree的,节点是⼀个page,root page是根节点,internal page是中间索引节点,leaf page真正存储数据,数据以page为单位读写。WiredTiger采⽤Copy on write的⽅式管理写操作(insert、update、delete),写操作会先缓存在cache⾥,持久化时,写操作不会在原来的leaf page上进⾏,⽽是写⼊新分配的page,每次checkpoint都会产⽣⼀个新的root page。
checkpoint流程
-
对所有的table进⾏⼀次checkpoint,每个table的checkpoint的元数据更新⾄WiredTiger.wt
-
对WiredTiger.wt进⾏checkpoint,将该table checkpoint的元数据更新⾄临时⽂件WiredTiger.turtle.set
-
将WiredTiger.turtle.set重命名为WiredTiger.turtle。
-
上述过程如果中间失败,WiredTiger在下次连接初始化时,⾸先将数据恢复⾄最新的快照状态,然后根据WAL恢复数据,以保证存储可靠性
Journaling
在数据库宕机时 , 为保证 MongoDB 中数据的持久性,MongoDB 使⽤了 Write Ahead Logging 向磁盘上的journal ⽂件预先进⾏写⼊。除了 journal ⽇志,MongoDB 还使⽤检查点(checkpoint)来保证数据的⼀致性,当数据库发⽣宕机时,我们就需要 checkpoint 和 journal ⽂件协作完成数据的恢复⼯作。
- 在数据⽂件中查找上⼀个检查点的标识符
- 在 journal ⽂件中查找标识符对应的记录
- 重做对应记录之后的全部操作
五.集群⾼可⽤
5.1 主从复制架构
master-slave架构中master节点负责数据的读写,slave没有写⼊权限只负责读取数据
在主从结构中,主节点的操作记录成为oplog(operation log)。oplog存储在系统数据库local的oplog.$main集合中,这个集合的每个⽂档都代表主节点上执⾏的⼀个操作。从服务器会定期从主服务器中获取oplog记录,然后在本机上执⾏!对于存储oplog的集合,MongoDB采⽤的是固定集合,也就是说随着操作过多,新的操作会覆盖旧的操作!
主从结构没有⾃动故障转移功能,需要指定master和slave端,不推荐在⽣产中使⽤。
mongodb4.0后不再⽀持主从复制!
5.2 复制集replica sets
复制集是由⼀组拥有相同数据集的mongod实例做组成的集群。
复制集是⼀个集群,它是2台及2台以上的服务器组成,以及复制集成员包括Primary主节点,secondary从节点和投票节点。
复制集提供了数据的冗余备份,并在多个服务器上存储数据副本,提⾼了数据的可⽤性,保证数据的安全性。
特点:
-
⾼可⽤
防⽌设备(服务器、⽹络)故障。
提供⾃动failover 功能。
技术来保证⾼可⽤
-
灾难恢复
当发⽣故障时,可以从其他节点恢复⽤于备份。
-
功能隔离
我们可以在从节点上执⾏读操作,减少主节点的压⼒。⽐如:⽤于分析、报表,数据挖掘,系统任务等。
5.2.1架构原理
⼀个复制集中Primary节点上能够完成读写操作,Secondary节点仅能⽤于读操作。Primary节点需要记录所有改变数据库状态的操作,这些记录保存在 oplog 中,这个⽂件存储在 local 数据库,各个Secondary节点通过此 oplog 来复制数据并应⽤于本地,保持本地的数据与主节点的⼀致。oplog 具有幂等性,即⽆论执⾏⼏次其结果⼀致,这个比mysql 的⼆进制⽇志更好⽤。
oplog的组成结构
{
"ts" : Timestamp(1446011584, 2),
"h" : NumberLong("1687359108795812092"),
"v" : 2,
"op" : "i",
"ns" : "test.nosql",
"o" : { "_id" : ObjectId("563062c0b085733f34ab4129"), "name" : "mongodb", "score" :"10"}
}
-
ts:操作时间,当前timestamp + 计数器,计数器每秒都被重置
-
h:操作的全局唯⼀标识
-
v:oplog版本信息
-
op:操作类型
-
i:插⼊操作
-
u:更新操作
-
d:删除操作
-
c:执⾏命令(如createDatabase,dropDatabase)
-
n:空操作,特殊⽤途
-
-
ns:操作针对的集合
-
o:操作内容
-
o2:更新查询条件,仅update操作包含该字段
复制集数据同步分为初始化同步和keep复制同步。初始化同步指全量从主节点同步数据,如果Primary节点数据量⽐较⼤同步时间会⽐较⻓。⽽keep复制指初始化同步过后,节点之间的实时同步⼀般是增量同步。
初始化同步有以下两种情况会触发
- Secondary第⼀次加⼊
- Secondary落后的数据量超过了oplog的⼤⼩,这样也会被全量复制
MongoDB的Primary节点选举基于⼼跳触发。⼀个复制集N个节点中的任意两个节点维持⼼跳,每个节点维护其他N-1个节点的状态。
⼼跳检测:整个集群需要保持⼀定的通信才能知道哪些节点活着哪些节点挂掉。mongodb节点会向副本集中的其他节点每2秒就会发送⼀次pings包,如果其他节点在10秒钟之内没有返回就标示为不能访问。每个节点内部都会维护⼀个状态映射表,
表明当前每个节点是什么⻆⾊、⽇志时间戳等关键信息。如果主节点发现⾃⼰⽆法与⼤部分节点通讯则把⾃⼰降级为
secondary只读节点。
主节点选举触发的时机:
- 第⼀次初始化⼀个复制集
- Secondary节点权重⽐Primary节点⾼时,发起替换选举
- Secondary节点发现集群中没有Primary时,发起选举
- Primary节点不能访问到⼤部分(Majority)成员时主动降级
当触发选举时,Secondary节点尝试将⾃身选举为Primary。主节点选举是⼀个⼆阶段过程+多数派协议。
-
第⼀阶段:
检测⾃身是否有被选举的资格 如果符合资格会向其它节点发起本节点是否有选举资格的FreshnessCheck,进⾏同僚仲裁
-
第⼆阶段:
发起者向集群中存活节点发送Elect(选举)请求,仲裁者收到请求的节点会执⾏⼀系列合法性检查,如果检查通过,则仲裁者(⼀个复制集中最多50个节点 其中只有7个具有投票权)给发起者投⼀票。
pv0通过30秒选举锁防⽌⼀次选举中两次投票。
pv1使⽤了terms(⼀个单调递增的选举计数器)来防⽌在⼀次选举中投两次票的情况。
多数派协议:
发起者如果获得超过半数的投票,则选举通过,⾃身成为Primary节点。获得低于半数选票的原因,除了常⻅的⽹络问题外,相同优先级的节点同时通过第⼀阶段的同僚仲裁并进⼊第⼆阶段也是⼀个原因。因此,当选票不⾜时,会sleep[0,1]秒内的随机时间,之后再次尝试选举。
5.3 分⽚集群 Shard Cluster
分⽚(sharding)是MongoDB⽤来将⼤型集合⽔平分割到不同服务器(或者复制集)上所采⽤的⽅法。不需要功能强⼤的⼤型计算机就可以存储更多的数据,处理更⼤的负载
分片原因:
- 存储容量需求超出单机磁盘容量。
- 活跃的数据集超出单机内存容量,导致很多请求都要从磁盘读取数据,影响性能。
- IOPS超出单个MongoDB节点的服务能⼒,随着数据的增⻓,单机实例的瓶颈会越来越明显。
- 副本集具有节点数量限制。
扩展方式:
-
垂直扩展:增加更多的CPU和存储资源来扩展容量。
-
⽔平扩展:将数据集分布在多个服务器上。⽔平扩展即分⽚
5.3.1 ⼯作原理
分⽚集群由以下3个服务组成:
-
Shards Server: 每个shard由⼀个或多个mongod进程组成,⽤于存储数据。
-
Router Server: 数据库集群的请求⼊⼝,所有请求都通过Router(mongos)进⾏协调,不需要在应⽤程序添加⼀个路由选择器,Router(mongos)就是⼀个请求分发中⼼它负责把应⽤程序的请求转发到对应的Shard服务器上。
-
Config Server: 配置服务器。存储所有数据库元信息(路由、分⽚)的配置
概念
-
⽚键(shard key):为了在数据集合中分配⽂档,MongoDB使⽤分⽚主键分割集合。
-
区块(chunk):在⼀个shard server内部,MongoDB还是会把数据分为chunks,每个chunk代表这个shard server内部⼀部分数据。MongoDB分割分⽚数据到区块,每⼀个区块包含基于分⽚主键的左闭右开的区间范围
-
分⽚策略:
-
范围分⽚(Range based sharding)
范围分⽚是基于分⽚主键的值切分数据,每⼀个区块将会分配到⼀个范围。
范围分⽚适合满⾜在⼀定范围内的查找,例如查找X的值在[20,30)之间的数据,mongo 路由根据Config server中存储的元数据,可以直接定位到指定的shard的Chunk中。
缺点: 如果shard key有明显递增(或者递减)趋势,则新插⼊的⽂档多会分布到同⼀个chunk,⽆法扩展写的能⼒
-
hash分⽚(Hash based sharding)
Hash分⽚是计算⼀个分⽚主键的hash值,每⼀个区块将分配⼀个范围的hash值。
Hash分⽚与范围分⽚互补,能将⽂档随机的分散到各个chunk,充分的扩展写能⼒,弥补了范围分⽚的不⾜,缺点是不能⾼效的服务范围查询,范围查询要分发到后端所有的Shard才能找出满⾜条件的⽂档。
-
组合⽚键
数据库中没有⽐较合适的⽚键供选择,或者是打算使⽤的⽚键基数太⼩(即变化少如星期只有7天可变化),可以选另⼀个字段使⽤组合⽚键,甚⾄可以添加冗余字段来组合。⼀般是粗粒度+细粒度进⾏组合。
分片策略的选择⽆⾮从两个⽅⾯考虑,数据的查询和写⼊,最好的效果就是数据查询时能命中更少的分⽚,数据写⼊时能够随机的写⼊每个分⽚,关键在于如何权衡性能和负载。
-
六.安全认证
MongoDB 默认是没有账号的,可以直接连接,⽆须身份验证。
MongoDB 服务端开启安全检查之前,⾄少需要有⼀个管理员账号,admin 数据库中的⽤户都被视为管理员如果 admin 库没有任何⽤户的话,即使在其他数据库中创建了⽤户,启⽤身份验证,默认的连接⽅式依然会有超级权限,即仍然可以不验证账号密码照样能进⾏ CRUD,安全认证相当于⽆效。
6.1 用户相关操作
在为库添加的用户之前要先切换到该库下
-
⽤户添加⻆⾊
use admin;#切换到admin db.createUser( { user: "账号", pwd: "密码", roles: [ { role: "⻆⾊(MonngoDB 已经约定好的⻆⾊)", db: "安全认证的数据库" }, { role: "⻆⾊(MonngoDB 已经约定好的⻆⾊)", db: "安全认证的数据库" } ] } )
-
修改密码
db.changeUserPassword( 'root' , 'rootNew' );
-
⽤户添加⻆⾊
db.grantRolesToUser( '⽤户名' , [{ role: '⻆⾊名' , db: '数据库名'}])
-
以auth启动mongod
./bin/mongod -f conf/mongo.conf --auth (也可以在mongo.conf 中添加auth=true 参数)
-
验证⽤户
db.auth("账号","密码")
-
删除⽤户
db.dropUser("⽤户名")
6.2 角色
- read:允许⽤户读取指定数据库(数据库⽤户⻆⾊)
- readWrite:允许⽤户读写指定数据库(数据库⽤户⻆⾊)
- dbAdmin:允许⽤户在指定数据库中执⾏管理函数,如索引创建、删除,查看统计或访问system.profile(数据库管理⻆⾊)
- userAdmin:允许⽤户向system.users集合写⼊,可以在指定数据库⾥创建、删除和管理⽤户(数据库管理⻆⾊)
- clusterAdmin:只在admin数据库中可⽤,赋予⽤户所有分⽚和复制集相关函数的管理权限(集群管理⻆⾊)
- readAnyDatabase:只在admin数据库中可⽤,赋予⽤户所有数据库的读权限(所有数据库⻆⾊)
- readWriteAnyDatabase:只在admin数据库中可⽤,赋予⽤户所有数据库的读写权限(所有数据库⻆⾊)
- userAdminAnyDatabase:只在admin数据库中可⽤,赋予⽤户所有数据库的userAdmin权限(所有数据库⻆⾊)
- dbAdminAnyDatabase:只在admin数据库中可⽤,赋予⽤户所有数据库的dbAdmin权限(所有数据库⻆⾊)
- root:只在admin数据库中可⽤。超级账号,超级权限(超级⽤户⻆⾊)
- dbOwner:库拥有者权限,即readWrite、dbAdmin、userAdmin⻆⾊的合体(数据库管理⻆⾊)
dbOwner 、userAdmin、userAdminAnyDatabase间接或直接提供了系统超级⽤户的访问
6.3 分⽚集群安全认证
和上面的操作一样只是多一个密钥文件
-
生成密钥文件
openssl rand -base64 756 > data/mongodb/testKeyFile.file chmod 600 data/mongodb/keyfile/testKeyFile.file
-
配置节点集群和分⽚节点集群开启安全认证和指定密钥⽂件
auth=true keyFile=data/mongodb/testKeyFile.file
-
在路由配置⽂件中 设置密钥⽂件
keyFile=data/mongodb/testKeyFile.file
七.数据备份与恢复
7.1 目的
- 防止硬件故障引起的数据丢失
- 防止人为错误误删数据
- 时间回溯
- 监管要求
7.2 实现方式
- 文件系统快照
- 复制数据文件
- mongodump
7.3 mongodump
该命令可以导出所有数据到指定目录中。 mongodump命令可以通过参数指定导出的数据库或者集合。
mongodump -h dbhost -d dbname -o dbdirectory
-
-h:MongoDB所在服务器地址,例如:127.0.0.1或者127.0.0.1:37017
-
--db 或者 -d :需要备份的数据库,例如:testdb
-
--out 或者-o:备份的数据存放位置,例如:/root/bdatas 在备份完成后,系统自动在root目录下建立一个bdatas目录,这个目录里面存放该数据库实例的备份数据。
-
-c :和-d 一起使用,备份指定数据库的指定集合
./bin/mongodump --host=192.168.211.136 --port=37017 -d local -c oplog.rs -o=/root/oplog_bak
7.4 mongorestore
使用 mongorestore 命令来恢复备份的数据。
mongorestore -h <hostname><:port> -d dbname <path>
-
--host <:port>, -h <:port>:
MongoDB所在服务器地址,默认为: localhost:37017
-
--db 或者 -d :
需要恢复的数据库,例如:testdb,这个名称也可以和备份时候的不一样
-
--drop:
恢复的时候,先删除当前数据,然后恢复备份的数据。
-
mongorestore:
设置备份数据所在位置,例如:/root/bdatas/testdb
不能同时指定--dir 选项,--dir也可以设置备份目录。
注意: 恢复指定的数据库 需要在恢复的路径中出现数据库的名字
-
--dir:
指定备份的目录
./bin/mongorestore -h 127.0.0.1:37017 -d testdb /root/bdatas/testdb
7.5 备份和恢复的重要选项
mongodump有一个选项是 --oplog(replica set或者master/slave模式专用),
--oplog选项只对全库导出有效,所以不能指定-d选项
oplog有幂等性:已存在的数据,重做oplog不会重复;不存在的数据重做oplog就可以
./bin/mongodump -h 127.0.0.1:37017 --oplog -o /root/bdatas
mongorestore的--oplogReplay选项,可以重放oplog.bson中的操作内容
--oplogLimit可以设置回放的时间节点,即此时间之前的数据恢复,假设你后面有误操作,误操作的不恢复
mongorestore -h localhost:37017 --oplogReplay /root/dump
通过 oplog 查询误操作的最后时间
/root/mongodb/bin/bsondump oplog.rs.bson | grep ""op":"d"" | head
或者 使用
db.oplog.rs.find({"op" : "d"}).sort({"ts":-1})
mongorestore -h localhost:37017 --oplogReplay --oplogLimit "1443024507:1" /root/dump/local
7.6 定时备份
-
准备备份目录
mkdir -p /root/backup/mongod_bak/mongod_bak_now /root/backup/mongod_bak/mongod_bak_list
-
编写备份脚本
vi /root/backup/mongobk.sh #!/bin/sh # dump 命令执行路径,根据mongodb安装路径而定 DUMP=/root/mongodb/bin/mongodump # 临时备份路径 OUT_DIR=/root/backup/mongod_bak/mongod_bak_now # 压缩后的备份存放路径 TAR_DIR=/root/backup/mongod_bak/mongod_bak_list # 当前系统时间 DATE=`date +%Y_%m_%d%H%M%S` # 数据库账号 # DB_USER=user # 数据库密码 # DB_PASS=password # 代表删除7天前的备份,即只保留近 7 天的备份 DAYS=7 # 最终保存的数据库备份文件 TAR_BAK="mongod_bak_$DATE.tar.gz" cd $OUT_DIR rm -rf $OUT_DIR/* mkdir -p $OUT_DIR/$DATE $DUMP -h 127.0.0.1 --port 37017 -o $OUT_DIR/$DATE # 压缩格式为 .tar.gz 格式 tar -zPcvf $TAR_DIR/$TAR_BAK $OUT_DIR/$DATE # 删除 7 天前的备份文件 find $TAR_DIR/ -mtime +$DAYS -delete exit
-
修改脚本权限
chmod +x /root/backup/mongobk.sh
-
编辑crontab
crontab -e #表示每天凌晨2点30执行备份 30 2 * * * /root/backup/mongobk.sh
-
查看crontab 的状态
service crond status
-
如果没有启动 可以使用下面的命令 启动定时服务 和加入开机自启动
# 启动定时任务 service crond start # 加入开机自动启动 chkconfig --level 35 crond on
-
查看定时任务和删除定时任务
crontab -l crontab -r crontab -e