一、简介
是一个强大的分布式文件存储的MongoDB
NoSQL
数据库,天然支持高可用、分布式和灵活设计。由C++
编写,运行稳定,性能高。为WEB
应用提供可扩展的高性能数据存储解决方案。主要解决关系型数据库数据量大,并发高导致查询效率低下的问题,通过使用内存代替磁盘提高查询性能。
MongoDB
特点:
【1】模块自由:可以把不同结构的文档存在在同一个数据库里;
【2】面向集合的存储:适合存储JSON
风格文件的形式;
【3】完整的索引支持:对任何属性都可以加索引;
【4】复制和高可用性:支持服务器之间的数据复制,支持主-从模式及服务器之间的相互复制。从而提供冗余备份及自动故障转移;
【5】自动分片:支持云级别的伸缩性,自动分片功能支持水平的数据库集群,可动态添加额外的机器;
【6】丰富的查询:支持丰富的查询表达方式,查询指令使用JSON
形式的标记,可轻易查询文档中内嵌的对象及数组;
【7】快速更新查询:查询优化器会分析查询表示式,并生成一个高效的查询计划;
【8】高效的传统存储方式:支持二进制数据及大型对象(如图片);
二、基本操作
MongoDB
将数据存储在一个文件,数据结构由键值key:value
对组成,类似于JSON
对象,字段值包含其他文档、数组、文档数组。
SQL术语 | MongoDB术语 | 说明 |
---|---|---|
DataBase | DataBase | 数据库 |
Table | Collection | 数据库表/集合:存储多个文档,结构不固定{'name':'zzx','gender':'男'}{'class':'一班','count':'10'} |
Row | Document | 数据记录行/文档就是一个对象,由键值对构成,是JSON 扩展的BSON 格式{'name':'zzx','gender':'男'} |
Column | Field | 数据字段/域 |
Index | Index | 索引 |
Table Joins | 表连接,MongoDB 不支持 | |
Primary Key | Primary Key | 主键,MongoDB 自动将_id 设置为主键 |
集合操作
<span style="color:#393939"><span style="background-color:#faf7ef"><code class="language-java"><span style="color:#008000">// 创建</span>
db.createCollection(name, options)
<span style="color:#008000">// 查看集合</span>
show collections
<span style="color:#008000">// 删除</span>
db.集合名称.drop()
</code></span></span>
name
要创建的集合名称,options
是一个文档,用于制定集合的配置,选项参数是可选的:参数capped
:默认为false
表示不设置上限;参数size
:当capped
值为true
时,需要指定此参数,表示设置上限的大小,会覆盖之前的值,单位为字节。
<span style="color:#393939"><span style="background-color:#faf7ef"><code class="language-java">db.createCollection(<span style="color:#a31515">"stu"</span>)
db.createCollection(<span style="color:#a31515">"stu"</span>, { capped: <span style="color:#a31515">true</span>, size: <span style="color:#880000">20</span>})
</code></span></span>
数据类型
类型 | 说明 |
---|---|
Object ID | 文档ID,每个文档都有一个属性,为_id 保证一个文档的唯一性,可以自己设置,如果没有设置自动提供一个特别的_id ,类型为objectID :12字节的十六进制数,前4个字节时时间戳,接下来3个字节是机器ID,接下来2个字节服务进程ID 最后三位是增量值 |
String | 字符串UTF-8 |
Boolean | 布尔 |
Integer | 整形 |
Double | 布尔 |
Arrays | 数组或列表 |
Object | 嵌入式的文档,MongoDB 是不能维护关系的,可以通过嵌入文档的形式来维护这个关系 |
Null | 空值 |
Timestamp | 时间戳 |
Date | 日期UNIX时间格式 |
文档操作
<span style="color:#393939"><span style="background-color:#faf7ef"><code class="language-java"><span style="color:#008000">// 插入</span>
db.集合名称.insert(document)
<span style="color:#008000">//案例1</span>
db.stu.insert({name:<span style="color:#a31515">'zzx'</span>,gender:<span style="color:#880000">1</span>}) <span style="color:#008000">//自动生成_id</span>
<span style="color:#008000">//案例2</span>
s1={_id:<span style="color:#a31515">'43444433'</span>,name:<span style="color:#a31515">'zzx'</span>}
s1.gender=<span style="color:#880000">0</span>
db.stu.insert(s1)
<span style="color:#008000">// 修改</span>
db.集合名称.update(
<query>, <span style="color:#008000">// 查询条件,类似sql中的where部分</span>
<update>, <span style="color:#008000">// 类似sql中的 set部分</span>
{$multi:<<span style="color:#a31515">boolean</span>>} <span style="color:#008000">// 默认false值改第一条记录,true表示修改满足的所有数据</span>
)
<span style="color:#008000">//案例</span>
db.stu.update({name:<span style="color:#a31515">'zzx'</span>},{name:<span style="color:#a31515">'fj'</span>}) <span style="color:#008000">// 整个文档的结构都会发生变化</span>
db.stu.update({name:<span style="color:#a31515">'zzx'</span>},{$set:{name:<span style="color:#a31515">'fj'</span>}}) <span style="color:#008000">// 只会修改 name的属性</span>
<span style="color:#008000">// 保存:如果数据存在进行修改,不存在进行插入</span>
db.集合名称.save(document)
<span style="color:#008000">// 删除</span>
db.集合名称.remove(
<query>,
{
justOne:<<span style="color:#a31515">boolean</span>> <span style="color:#008000">//默认false会删除多条</span>
}
)
</code></span></span>
查询操作
<span style="color:#393939"><span style="background-color:#faf7ef"><code class="language-java"><span style="color:#008000">// 查询</span>
db.集合名称.find(条件文档)
<span style="color:#008000">// 查询一条数据</span>
db.集合名称.findOne(条件文档)
<span style="color:#008000">//案例 and和or一起使用。运算法符:$lt小于 $lte小于等于 $gt大于 $gte大于等于 $ne不等于 使用//或者$regex编写正则表达式</span>
db.stu.find({$or:[{age: {$gte: <span style="color:#880000">18</span>}}, {gender: <span style="color:#880000">1</span>}], name: <span style="color:#a31515">'zzx'</span>})
db.stu.find({name:{$regex:<span style="color:#a31515">'^黄'</span>}})
db.stu.find({name:/^黄/})
<span style="color:#008000">// ** 自定义查询:使用 $where后面写一个函数</span>
db.stu.find({$where:function(){<span style="color:#0000ff">return</span> <span style="color:#0000ff">this</span>.age > <span style="color:#880000">20</span>}})
<span style="color:#008000">// limit()用于读取指定数量的文档</span>
db.集合名称.find().limit(NUMBER) <span style="color:#008000">//NUMBER表示文档的条数</span>
<span style="color:#008000">// skip()用于跳过指定数量的文件,配合limit实现分页功能,与limit同时使用的时候不分先后顺序。</span>
db.集合名称.find().skip(NUMBER)
<span style="color:#008000">//投影:查询的结构集只选择必要的字段</span>
db.集合名称.find({},{字段名称:<span style="color:#880000">1</span>,...}) <span style="color:#008000">// 对需要显示的字段设置为1,id默认会返回,如果不想返回可以设置为0</span>
<span style="color:#008000">// 排序 sort()</span>
db.集合名称.find().sort({字段:<span style="color:#880000">1</span>,...}) <span style="color:#008000">// 1升序,-1降序</span>
<span style="color:#008000">// 统计 count()</span>
db.集合名称.find().count({条件}) <span style="color:#008000">//find可以省略</span>
db.stu.count({age:{$gt:<span style="color:#880000">20</span>},gender:<span style="color:#880000">1</span>})
<span style="color:#008000">// 去重distinct()</span>
db.集合名称.distinct({去重字段,{条件}})
</code></span></span>
聚合
主要用于计算数据,类似sql
中的sum()
、avg()
。
常用的管道:$group
将集合中的文档分组,可用于统计结果;$match
过滤数据,只输出符合条件的文档;$project
修改输入文档的结构,如重命名、增加、删除字段、创建计算结果;$sort
将输入文档排序后输出;$limit
限制聚合管道返回的文档数;$skip
跳过指定数量的文档,返回剩余文档;$unwind
将数组类型的字段进行拆分;
常用的表达式:$sum
计算综合,$sum:1
同count
表示计数;$avg
计算平均值;$min
获取最小值;$max
获取最大值;$push
在结果文档中插入值到下一个数组中;$first
根据资源文件的排序获取第一个文档数据;$last
根据资源文档的排序获取最后一个文档数据。
<span style="color:#393939"><span style="background-color:#faf7ef"><code class="language-java">db.集合名称.aggregate([{管道:{表达式}}]) <span style="color:#008000">//管道在Linux中一般用于将当前命令的输出结果作为下一个命令的输入,在MongoDB中是同样的作用</span>
</code></span></span>
分组$group
<span style="color:#393939"><span style="background-color:#faf7ef"><code class="language-java"><span style="color:#008000">// 统计男女的总人数 _id表示组分的依据,使用某个字段的格式为'$字段',如果需要得到整个文档可以使用 '$$ROOT' 替换 '$age'</span>
db.stu.aggregate([
{$group:{
_id:<span style="color:#a31515">'$gender'</span>,
counter:{$push:<span style="color:#a31515">'$age'</span>}
}}
])
<span style="color:#008000">// 输入</span>
{<span style="color:#a31515">"_id"</span>:男,<span style="color:#a31515">"counter"</span>:[<span style="color:#880000">12</span>,<span style="color:#880000">45</span>]}
{<span style="color:#a31515">"_id"</span>:女,<span style="color:#a31515">"counter"</span>:[<span style="color:#880000">22</span>,<span style="color:#880000">15</span>]}
</code></span></span>
过滤数据$match
<span style="color:#393939"><span style="background-color:#faf7ef"><code class="language-java">db.stu.aggregate([
{$match:{age:{$gt:<span style="color:#880000">20</span>}}},
{$group:{_id:<span style="color:#a31515">'$gender'</span>,counter:{$sum:<span style="color:#880000">1</span>}}}
])
</code></span></span>
投影$project:只显示某个字段
<span style="color:#393939"><span style="background-color:#faf7ef"><code class="language-java">db.stu.aggregate([
{$group:{_id:<span style="color:#a31515">'$gender'</span>,counter:{$sum:<span style="color:#880000">1</span>}}},
{$project:{_id:<span style="color:#880000">0</span>,counter:<span style="color:#880000">1</span>}}
])
</code></span></span>
排序$sort
<span style="color:#393939"><span style="background-color:#faf7ef"><code class="language-java">db.stu.aggregate([
{$group:{_id:<span style="color:#a31515">'$gender'</span>,counter:{$sum:<span style="color:#880000">1</span>}}},
{$sort:{counter:-<span style="color:#880000">1</span>}}
])
</code></span></span>
$limit 和 $skip
<span style="color:#393939"><span style="background-color:#faf7ef"><code class="language-java">db.stu.aggregate([
{$group:{_id:<span style="color:#a31515">'$gender'</span>,counter:{$sum:<span style="color:#880000">1</span>}}},
{$sort:{counter:-<span style="color:#880000">1</span>}},
{$skip:<span style="color:#880000">1</span>},
{$limit:<span style="color:#880000">1</span>}
])
</code></span></span>
$unwind 将数组类型的字段进行拆分,如果不是数组是一个属性,就单独输出该属性
<span style="color:#393939"><span style="background-color:#faf7ef"><code class="language-java">db.集合名称.aggregate([{$unwind:<span style="color:#a31515">'$字段名称'</span>}])
db.stu.insert({name:<span style="color:#a31515">'zzx'</span>,size:[<span style="color:#a31515">'S'</span>,<span style="color:#a31515">'Z'</span>,<span style="color:#a31515">'L'</span>]})
db.stu.aggregate([
$unwind:<span style="color:#a31515">'$size'</span>
])
<span style="color:#008000">// 处理是空数组,无字段,null的情况使用上述 $unwind数据会丢失,可以使用下面表达式,防止数据丢失</span>
db.stu.aggregate([
{$unwind:{
path:<span style="color:#a31515">'$字段名称'</span>,
preserverNullAndEmptyArrays:<<span style="color:#a31515">boolean</span>> <span style="color:#008000">//防止数据丢失</span>
}}
])
</code></span></span>
三、索引
MongoDB
通过索引提升查询速度
<span style="color:#393939"><span style="background-color:#faf7ef"><code class="language-java"><span style="color:#008000">// 使用explain()命令分析查询性能</span>
db.stu.find({name:<span style="color:#a31515">'zzx100000'</span>}).explain(<span style="color:#a31515">'executionStats'</span>)
<span style="color:#008000">// 分析结果如下:</span>
<span style="color:#a31515">"executionStats"</span>: {
<span style="color:#a31515">"executionSuccess"</span>: <span style="color:#a31515">true</span>,
<span style="color:#a31515">"nReturned"</span>: <span style="color:#880000">1</span>,
<span style="color:#a31515">"executionTimeMillis"</span>: <span style="color:#880000">96</span>,
<span style="color:#a31515">"totalKeysExamined"</span>: <span style="color:#880000">0</span>,
<span style="color:#a31515">"totalDocsExamined"</span>: <span style="color:#880000">100000</span>,
}
</code></span></span>
建立索引: 1
表示升序,-1
表示降序
<span style="color:#393939"><span style="background-color:#faf7ef"><code class="language-java">db.集合.ensureIndex({属性:<span style="color:#880000">1</span>})
db.stu.ensureIndex({name:<span style="color:#880000">1</span>})
<span style="color:#008000">// 对索引属性查询</span>
db.stu.find({name:<span style="color:#a31515">'zzx100000'</span>}).explain(<span style="color:#a31515">'executionStats'</span>)
<span style="color:#008000">// 分析结果如下:</span>
<span style="color:#a31515">"executionStats"</span>: {
<span style="color:#a31515">"executionSuccess"</span>: <span style="color:#a31515">true</span>,
<span style="color:#a31515">"nReturned"</span>: <span style="color:#880000">1</span>,
<span style="color:#a31515">"executionTimeMillis"</span>: <span style="color:#880000">1</span>,
<span style="color:#a31515">"totalKeysExamined"</span>: <span style="color:#880000">0</span>,
<span style="color:#a31515">"totalDocsExamined"</span>: <span style="color:#880000">100000</span>,
}
<span style="color:#008000">// 唯一索引</span>
db.stu.ensureIndex({<span style="color:#a31515">'name'</span>:<span style="color:#880000">1</span>},{<span style="color:#a31515">'unique'</span>:<span style="color:#a31515">true</span>})
<span style="color:#008000">// 联合索引</span>
db.stu.ensureIndex({name:<span style="color:#880000">1</span>,age1})
<span style="color:#008000">// 查看索引</span>
db.stu.getIndexes()
<span style="color:#008000">// 删除所以</span>
db.stu.dropIndexes(<span style="color:#a31515">'索引名称'</span>)
</code></span></span>
四、安全
为了更安全的访问MongoDB
,需要创建用户。采用角色、用户、数据库的安全管理方式。常用的角色如下:root
只在admin
数据库中可用,超级账号,超级权限。Read
用户只读权限,readWrite
用户读写权限。
<span style="color:#393939"><span style="background-color:#faf7ef"><code class="language-java"><span style="color:#008000">// 创建超级管理员</span>
use admin <span style="color:#008000">// admin是数据库</span>
db.createUser({
user:<span style="color:#a31515">'admin'</span>,
pwd:<span style="color:#a31515">'123'</span>,
roles:[{role:<span style="color:#a31515">'root'</span>,db:<span style="color:#a31515">'admin'</span>}]
})
</code></span></span>
启用安全认证:修改配置文件
<span style="color:#393939"><span style="background-color:#faf7ef"><code class="language-shell">sudo vi /etc/mongod.conf
</code></span></span>
启用身份认证:keys and values
之间一定要有空格,否则解析错误。修改完配置文件后,必须重启服务。
<span style="color:#393939"><span style="background-color:#faf7ef"><code class="language-yaml language-yml"><span style="color:#ff0000">security:</span>
<span style="color:#ff0000">authorization:</span> <span style="color:#a31515">enable</span>
</code></span></span>
数据库连接
<span style="color:#393939"><span style="background-color:#faf7ef"><code class="language-shell">mongo -u admin -p 123 --authenticationDatabase admin
</code></span></span>
五、备份与恢复
复制提供数据冗余备份,并在多个服务器上存储数据副本,提高数据的可用性,并保证数据的安全性,允许从硬件故障和服务中断中恢复数据。常见的搭配是一主多从。主节点记录所有操作,从节点定期轮询主节点获取这些操作,然后对自己的数据副本执行这些操作,从而保证数据的一致性。
优点:
【1】数据备份;
【2】数据灾难恢复(自动故障转移,自动恢复);
【3】读写分离;
【4】高数据可用;
【5】无宕机维护;
【6】副本集对应用程序是透明的;
设置复制节点:
<span style="color:#393939"><span style="background-color:#faf7ef"><code class="language-java">mongod --bind_ip <span style="color:#880000">192.168</span><span style="color:#880000">.113</span><span style="color:#880000">.122</span> --port <span style="color:#880000">27017</span> --dbpath ./mongdb/file1 --replSet rs0 <span style="color:#008000">// rs0副本集,master和slave的副本集相同</span>
mongod --bind_ip <span style="color:#880000">192.168</span><span style="color:#880000">.154</span><span style="color:#880000">.132</span> --port <span style="color:#880000">27017</span> --dbpath ./mongdb/file1 --replSet rs0
</code></span></span>
通过客户端连接MongoDB master
,并在master
上面进行初始化,同时添加其他副本集(也就是备份的服务器信息)。如果在从服务器上进行读操作,需要设置rs.slaveOk()
。
<span style="color:#393939"><span style="background-color:#faf7ef"><code class="language-java">mongo --host <span style="color:#880000">192.168</span><span style="color:#880000">.113</span><span style="color:#880000">.122</span> --port <span style="color:#880000">27017</span> <span style="color:#008000">// 主服务器</span>
rs.initiate() <span style="color:#008000">//rs是mongodb提供的副本集管理对象</span>
rs.add(<span style="color:#a31515">'192.168.154.132:27017'</span>) <span style="color:#008000">// 从服务器</span>
rs.salveOk()
</code></span></span>
手动备份:
<span style="color:#393939"><span style="background-color:#faf7ef"><code class="language-java">mongodump -h dbhost -d dbname -o dbdirectory
</code></span></span>
恢复:
<span style="color:#393939"><span style="background-color:#faf7ef"><code class="language-java">mongorestore -h dbhost -d dbname --dir dbdirectory</code></span></span>