文章目录
注意事项
- 我们始终以下面的几条数据为测试用例:
"name" : "张三", "age" : "18"
"name" : "李四", "age" : "28"
"name" : "王五", "age" : "38"
- col即为collection,是我们创建的集合,相当于mysql中的table,我们始终以
test
为集合名
一、启动和关闭服务
// 启动
sudo mongod
// 关闭
mongo
use admin
db.shutdownServer()
二、数据库的使用
2.1 连接数据库
// 连接
mongo
// 查看所有数据库
show dbs
2.2 登录数据库
// 登录账户
mongodb://username:password@ip
// 例如
mongodb://admin:123456@localhost/
// 登录账户并使用指定数据库
mongodb://username:password@ip/dbname
// 例如
mongodb://admin:123456@localhost/test_db
2.3 创建/删除/选择数据库
// 数据库不存在则创建数据库,否则切换到数据库
use test_db
// 查看当前选择的数据库
db
// 查看所有数据库
show dbs
// 删除当前数据库
db.dropDatabase()
三、集合collection
集合相当于mysql中的数据表(table)。
- db.createCollection(name, options)
- name: 要创建的集合名称
- options: 可选参数, 指定有关内存大小及索引的选项
// 创建集合 test
db.createCollection("test")
db.createCollection("test", { capped : true, autoIndexId : true, size :
6142800, max : 10000 } )
// 删除集合test
db.test.drop()
// 查看集合
show collections
options 可以是如下参数:
字段 | 类型 | 描述 |
---|---|---|
capped | 布尔 | (可选)如果为 true,则创建固定集合。固定集合是指有着固定大小的集合,当达到最大值时,它会自动覆盖最早的文档。 当该值为 true 时,必须指定 size 参数。 |
autoIndexId | 布尔 | (可选)如为 true,自动在 _id 字段创建索引。默认为 false。 |
size | 数值 | (可选)为固定集合指定一个最大值(以字节计)。 如果 capped 为 true,也需要指定该字段。 |
max | 数值 | (可选)指定固定集合中包含文档的最大数量。 |
在 MongoDB 中,往往不需要创建集合。当插入一些文档时,MongoDB 会自动创建集合。
db.test.insert({"name":"张三","age":18)
四、文档document
文档相当于mysql中数据表(table)中的行(row),对应着一条数据。
4.1 插入文档
- db.COLLECTION_NAME.insert(document)
db.test.insert({"name":"张三","age":18})
// 或者去掉引号
db.test.insert({name:"张三",age:18})
// 也可以先声明document
doc = {name:"王五",age:18}
db.test.insert(doc)
// 接下来我们查询一下
db.test.find()
4.2 更新文档
使用 update() 和 save() 方法来更新集合中的文档。
4.2.1 update
update() 方法用于更新已存在的文档。
-
db.db.collection.update(
query,
update,
{
upsert: boolean,
multi: boolean,
writeConcern: document
}
)参数说明:
- query : update的查询条件,类似sql update查询内where后面的。
- update : update的对象和一些更新的操作符(如 , , ,inc…)等,也可以理解为sql update查询内set后面的
- upsert : 可选,这个参数的意思是,如果不存在update的记录,是否插入objNew,true为插入,默认是false,不插入。
- multi : 可选,mongodb 默认是false,只更新找到的第一条记录,如果这个参数为true,就把按条件查出来多条记录全部更新。
- writeConcern :可选,抛出异常的级别。
Demo:
我们将李四的年龄改成28
db.test.update({name:"李四"}, {$set:{age:28}})
// 只更新找到的第一条记录
db.test.update({name:"李四"}, {$set:{age:28}},{multi:true})
4.2.2 save
save() 方法通过传入的文档来替换已有文档。
-
db.collection.save(
Document,
{
writeConcern: document
}
)参数说明:
- document : 文档数据。
- writeConcern :可选,抛出异常的级别。
Demo:
我们先来查询已存在的文档
db.test.find()
我这里打印出的是这些
{ "_id" : ObjectId("5d491fdcc76a65c99c61aa4f"), "name" : "张三", "age" : 18 } { "_id" : ObjectId("5d492019c76a65c99c61aa50"), "name" : "李四", "age" : 28 } { "_id" : ObjectId("5d49208ec76a65c99c61aa51"), "name" : "王五", "age" : 18 }
然后我们替换王五,并把年龄改成38
db.test.save({ "_id" : ObjectId("5d49208ec76a65c99c61aa51"), "name" : "王五", "age" : 38 })
王五的整条数据就都被替换了。
更多实例:
// 只更新第一条记录 db.col.update( { "count" : { $gt : 1 } } , { $set : { "test2" : "OK"} } ); // 全部更新 db.col.update( { "count" : { $gt : 3 } } , { $set : { "test2" : "OK"} },false,true ); // 只添加第一条 db.col.update( { "count" : { $gt : 4 } } , { $set : { "test5" : "OK"} },true,false ); // 全部添加进去 db.col.update( { "count" : { $gt : 5 } } , { $set : { "test5" : "OK"} },true,true ); // 全部更新 db.col.update( { "count" : { $gt : 15 } } , { $inc : { "count" : 1} },false,true ); // 只更新第一条记录 db.col.update( { "count" : { $gt : 10 } } , { $inc : { "count" : 1} },false,false );
4.2.3 补充
在3.2版本开始,MongoDB提供以下更新集合文档的方法:
- db.collection.updateOne() 向指定集合更新单个文档
- db.collection.updateMany() 向指定集合更新多个文档
WriteConcern说明
- WriteConcern.NONE:没有异常抛出
- WriteConcern.NORMAL:仅抛出网络错误异常,没有服务器错误异常
- WriteConcern.SAFE:抛出网络错误异常、服务器错误异常;并等待服务器完成写操作。
- WriteConcern.MAJORITY: 抛出网络错误异常、服务器错误异常;并等待一个主服务器完成写操作。
- WriteConcern.FSYNC_SAFE: 抛出网络错误异常、服务器错误异常;写操作等待服务器将数据刷新到磁盘。
- WriteConcern.JOURNAL_SAFE:抛出网络错误异常、服务器错误异常;写操作等待服务器提交到磁盘的日志文件。
- WriteConcern.REPLICAS_SAFE:抛出网络错误异常、服务器错误异常;等待至少2台服务器完成写操作。
移除集合中的键值对,使用的 $unset 操作符:
db.collection.update({"_id":"56064f89ade2f21f36b03136"}, {$unset:{ "test2" : "OK"}})
4.3 删除文档
4.3.1 remove,已过时
remove() 方法 并不会真正释放空间。
需要继续执行 db.repairDatabase() 来回收磁盘空间。
-
db.collection.remove(
query,
justOne
) -
如果你的 MongoDB 是 2.6 版本以后的,语法格式如下:
db.collection.remove(
query,
{
justOne: boolean,
writeConcern: document- 参数说明:
- query :(可选)删除的文档的条件。
- justOne : (可选)如果设为 true 或 1,则只删除一个文档,如果不设置该参数,或使用默认值 false,则删除所有匹配条件的文档。
- writeConcern :(可选)抛出异常的级别。
- 参数说明:
Demo:
db.test.remove({'name':'王五'})
如果你想删除所有数据
db.test.remove({})
4.3.2 deleteOne() 和 deleteMany()
这里很简单,对应上面的
// 删除王五
db.test.deleteOne({'name':'王五'})
// 删除所有叫王五的
db.test.deleteMany({'name':'王五'})
// 删除所有数据
db.test.deleteMany({})
4.4 查询文档
- db.collection.find(query, projection)
- query :可选,使用查询操作符指定查询条件
- projection :可选,使用投影操作符指定返回的键。查询时返回文档中所有键值, 只需省略该参数即可(默认省略)。
- db.col.find(query, {name: 1, age: 1}) // inclusion模式 指定返回的键,不返回其他键
- db.col.find(query, {name: 0, age: 0}) // exclusion模式 指定不返回的键,返回其他键
- _id 键默认返回,需要主动指定 _id:0 才会隐藏
- 两种模式不可混用
- db.collection.find().pretty() , pretty()表示只读查询
示例:
// 查询全部
db.test.find()
// 只读查询全部
db.test.find().pretty()
// 查询一个
db.test.findOne()
4.4.1 MongoDB 与 RDBMS Where 语句比较
如果你熟悉常规的 SQL 数据,通过下表可以更好的理解 MongoDB 的条件语句查询:
操作 | 格式 | 范例 | RDBMS中的类似语句 |
---|---|---|---|
等于 | {<key>:<value> } | db.col.find({"by":"测试的啦"}).pretty() | where by = '测试的啦' |
小于 | {<key>:{$lt:<value>}} | db.col.find({"likes":{$lt:50}}).pretty() | where likes < 50 |
小于或等于 | {<key>:{$lte:<value>}} | db.col.find({"likes":{$lte:50}}).pretty() | where likes <= 50 |
大于 | {<key>:{$gt:<value>}} | db.col.find({"likes":{$gt:50}}).pretty() | where likes > 50 |
大于或等于 | {<key>:{$gte:<value>}} | db.col.find({"likes":{$gte:50}}).pretty() | where likes >= 50 |
不等于 | {<key>:{$ne:<value>}} | db.col.find({"likes":{$ne:50}}).pretty() | where likes != 50 |
// 查询年龄大于20且小于30的人
db.test.find( { age: { $gt: 20 ,$lt: 30}} )
4.4.2 And条件
- db.col.find({key1:value1, key2:value2}).pretty()
示例:
// 查询姓名为 李四,年龄为 28 的人
db.test.fins({"name":"李四", "age":"28"})
4.4.3 Or条件
OR 条件语句使用了关键字 $or,语法格式如下:
- db.col.find(
{
$or: [
{key1: value1}, {key2:value2}
]
}
).pretty()
示例:
// 查询姓名为 李四,或者年龄为 18 的人
db.test.find({$or:[{"name":"李四"},{"age":"18"}]})
4.4.4 And和Or的联合使用
// 查询年龄为 28,且姓名为 张三 或者 李四 的人
db.test.find({"age":"28", $or:[{"name":"张三"}, {"name":"李四"}]})
4.4.5 type操作符
我们可以通过type操作符检索集合中匹配的数据类型
类型 | 数字 | 备注 |
---|---|---|
Double | 1 | |
String | 2 | |
Object | 3 | |
Array | 4 | |
Binary data | 5 | |
Undefined | 6 | 已废弃。 |
Object id | 7 | |
Boolean | 8 | |
Date | 9 | |
Null | 10 | |
Regular Expression | 11 | |
JavaScript | 13 | |
Symbol | 14 | |
JavaScript (with scope) | 15 | |
32-bit integer | 16 | |
Timestamp | 17 | |
64-bit integer | 18 | |
Min key | 255 | Query with -1 . |
Max key | 127 |
// 查询所有 age 字段为 String 的数据, 得到的是所有数据,因为我们插入的age并不是int型,记着吗,我们insert的时候是 "age":"xxx"
db.test.find({age:{$type:2}})
db.test.find({age:{$type:"string"}})
4.4.6 分页查询:Limit与Skip方法
skip和limit方法只适合小数据量分页,如果是百万级效率就会非常低,因为skip方法是一条条数据数过去的,建议使用where_limit
4.4.6.1 Limit方法
如果你需要在MongoDB中读取指定数量的数据记录,可以使用MongoDB的Limit方法,limit()方法接受一个数字参数,该参数指定从MongoDB中读取的记录条数。
- db.col.find().limit(num)
4.4.6.2 Skip()方法
我们除了可以使用limit()方法来读取指定数量的数据外,还可以使用skip()方法来跳过指定数量的数据,skip方法同样接受一个数字参数作为跳过的记录条数。
- db.col.find().limit(num).skip(num)
// 从第10调开始读取100条
db.col.find().skip(10).limit(100)
// 下面的会跳过 张三, 只显示一条 李四
db.test.find().limit(1).skip(1)
4.4.7 排序
使用 sort() 方法对数据进行排序,sort() 方法可以通过参数指定排序的字段,并使用 1 和 -1 来指定排序的方式,其中 1 为升序排列,而 -1 是用于降序排列。
- db.col.find().sort({key:1})
// 根据 age 字段降序排列,现实的顺序为 王五 李四 张三
db.test.find().sort({age:-1})
4.4.8 索引
使用 createIndex() 方法来创建索引。
注意在 3.0.0 版本前创建索引方法为 db.collection.ensureIndex(),之后的版本使用了 db.collection.createIndex() 方法,ensureIndex() 还能用,但只是 createIndex() 的别名。
- db.collection.createIndex(keys, options)
// 1 为指定按升序创建索引,如果你想按降序来创建索引指定为 -1 即可
db.test.createIndex({"age":1})
createIndex() 方法中你也可以设置使用多个字段创建索引(关系型数据库中称作复合索引)。
db.test.createIndex({"age":1,"name":-1})
createIndex() 接收可选参数,可选参数列表如下:
Parameter | Type | Description |
---|---|---|
background | Boolean | 建索引过程会阻塞其它数据库操作,background可指定以后台方式创建索引,即增加 “background” 可选参数。 “background” 默认值为false。 |
unique | Boolean | 建立的索引是否唯一。指定为true创建唯一索引。默认值为false. |
name | string | 索引的名称。如果未指定,MongoDB的通过连接索引的字段名和排序顺序生成一个索引名称。 |
dropDups | Boolean | **3.0+版本已废弃。**在建立唯一索引时是否删除重复记录,指定 true 创建唯一索引。默认值为 false. |
sparse | Boolean | 对文档中不存在的字段数据不启用索引;这个参数需要特别注意,如果设置为true的话,在索引字段中不会查询出不包含对应字段的文档.。默认值为 false. |
expireAfterSeconds | integer | 指定一个以秒为单位的数值,完成 TTL设定,设定集合的生存时间。 |
v | index version | 索引的版本号。默认的索引版本取决于mongod创建索引时运行的版本。 |
weights | document | 索引权重值,数值在 1 到 99,999 之间,表示该索引相对于其他索引字段的得分权重。 |
default_language | string | 对于文本索引,该参数决定了停用词及词干和词器的规则的列表。 默认为英语 |
language_override | string | 对于文本索引,该参数指定了包含在文档中的字段名,语言覆盖默认的language,默认值为 language. |
// 在后台创建索引
db.test.createIndex({name: 1, age: 1}, {background: true})
- 查看集合索引
db.col.getIndexes()
- 查看集合索引大小
db.col.totalIndexSize()
- 删除集合所有索引
db.col.dropIndexes()
- 删除集合指定索引
db.col.dropIndex("索引名称")
4.4.9 聚合
聚合主要用于处理数据(诸如统计平均值,求和等),并返回计算后的数据结果。有点类似sql语句中的 count(*)。
- db.COLLECTION_NAME.aggregate(AGGREGATE_OPERATION)
示例:
假设我们现在有四条数据:
{ "_id" : ObjectId("5d522cd625e9232e10114e20"), "name" : "张三", "age" : 18, "like" : 100 }
{ "_id" : ObjectId("5d522cde25e9232e10114e21"), "name" : "李四", "age" : 28, "like" : 150 }
{ "_id" : ObjectId("5d522ce625e9232e10114e22"), "name" : "王五", "age" : 38, "like" : 200 }
{ "_id" : ObjectId("5d5252242825b2cef2d05dd3"), "name" : "赵六", "age" : 48, "like" : 200 }
执行:
// 以like分组,并且求like字段相同的人数总和
// 相当于 select like as _id, count(*) as total from test group by like;
db.test.aggregate([{$group: {_id:"$like", total:{$sum: 1}}}])
// log
{ "_id" : 200, "total" : 2 }
{ "_id" : 150, "total" : 1 }
{ "_id" : 100, "total" : 1 }
执行:
// 以like分组,并且求like字段相同的年龄的平均值
db.test.aggregate({$group: {_id:"$like", avg_age:{$avg:"$age"}} })
// log
{ "_id" : 200, "avg_age" : 43 }
{ "_id" : 150, "avg_age" : 28 }
{ "_id" : 100, "avg_age" : 18 }
4.4.10 管道
管道在Unix和Linux中一般用于将当前命令的输出结果作为下一个命令的参数。
MongoDB的聚合管道将MongoDB文档在一个管道处理完毕后将结果传递给下一个管道处理。管道操作是可以重复的。
表达式:处理输入文档并输出。表达式是无状态的,只能用于计算当前聚合管道的文档,不能处理其它的文档。
这里我们介绍一下聚合框架中常用的几个操作:
- $project:修改输入文档的结构。可以用来重命名、增加或删除域,也可以用于创建计算结果以及嵌套文档。
- $match:用于过滤数据,只输出符合条件的文档。$match使用MongoDB的标准查询操作。
- $limit:用来限制MongoDB聚合管道返回的文档数。
- $skip:在聚合管道中跳过指定数量的文档,并返回余下的文档。
- $unwind:将文档中的某一个数组类型字段拆分成多条,每条包含数组中的一个值。
- $group:将集合中的文档分组,可用于统计结果。
- $sort:将输入文档排序后输出。
- $geoNear:输出接近某一地理位置的有序文档。
4.4.10.1 $project
用来控制显示的字段
// 下面的两个查询结果是相等的,都只查询出那么和age字段,其他的字段不会查询
db.test.aggregate({$project:{name:1,age:1}})
db.test.find({},{like:1,age:1})
4.4.10.11 $match
// 查询like大于100且小于200的数据
db.test.aggregate({$match:{like:{$gt:100,$lt:200}}})
// 查询like大于100且小于200的数据,后将符合条件的记录送到下一阶段$group管道操作符进行处理(将查询到的数据不分组,显示总数)
db.test.aggregate([{$match:{like:{$gt:100,$lt:300}}}, {$group:{_id:null,count:{$sum:1}} }])
//log
{ "_id" : null, "count" : 3 }
// 查询like大于100且小于200的数据,后将符合条件的记录送到下一阶段$group管道操作符进行处理(将查询到的数据按姓名分组,显示每组总数)
db.test.aggregate([{$match:{like:{$gt:100,$lt:300}}}, {$group:{_id:null,count:{$sum:1}} }])
//log
{ "_id" : "赵六", "count" : 1 }
{ "_id" : "王五", "count" : 1 }
{ "_id" : "李四", "count" : 1 }
4.4.10.12 $skip
db.test.aggregate({ $skip : 5 });
五、DB 复制(副本集)
MongoDB复制是将数据同步在多个服务器的过程。
复制提供了数据的冗余备份,并在多个服务器上存储数据副本,提高了数据的可用性, 并可以保证数据的安全性。
复制还允许您从硬件故障和服务中断中恢复数据。
在本教程中我们使用同一个MongoDB来做MongoDB主从的实验, 操作步骤如下:
1、关闭正在运行的MongoDB服务器。
现在我们通过指定 --replSet 选项来启动mongoDB。–replSet 基本语法格式如下:
mongod --port "PORT" --dbpath "YOUR_DB_DATA_PATH" --replSet "REPLICA_SET_INSTANCE_NAME"
实例
mongod --port 27017 --dbpath "D:\set up\mongodb\data" --replSet rs0
以上实例会启动一个名为rs0的MongoDB实例,其端口号为27017。
启动后打开命令提示框并连接上mongoDB服务。
在Mongo客户端使用命令rs.initiate()来启动一个新的副本集。
我们可以使用rs.conf()来查看副本集的配置
查看副本集状态使用 rs.status() 命令
副本集添加成员
添加副本集的成员,我们需要使用多台服务器来启动mongo服务。进入Mongo客户端,并使用rs.add()方法来添加副本集的成员。
语法
rs.add() 命令基本语法格式如下:
>rs.add(HOST_NAME:PORT)
实例
假设你已经启动了一个名为mongod1.net,端口号为27017的Mongo服务。 在客户端命令窗口使用rs.add() 命令将其添加到副本集中,命令如下所示:
>rs.add("mongod1.net:27017")
>
MongoDB中你只能通过主节点将Mongo服务添加到副本集中, 判断当前运行的Mongo服务是否为主节点可以使用命令db.isMaster() 。
MongoDB的副本集与我们常见的主从有所不同,主从在主机宕机后所有服务将停止,而副本集在主机宕机后,副本会接管主节点成为主节点,不会出现宕机的情况。
六、分片
分片
在Mongodb里面存在另一种集群,就是分片技术,可以满足MongoDB数据量大量增长的需求。
当MongoDB存储海量的数据时,一台机器可能不足以存储数据,也可能不足以提供可接受的读写吞吐量。这时,我们就可以通过在多台机器上分割数据,使得数据库系统能存储和处理更多的数据。
为什么使用分片
- 复制所有的写入操作到主节点
- 延迟的敏感数据会在主节点查询
- 单个副本集限制在12个节点
- 当请求量巨大时会出现内存不足。
- 本地磁盘不足
- 垂直扩展价格昂贵
MongoDB分片
下图展示了在MongoDB中使用分片集群结构分布:
上图中主要有如下所述三个主要组件:
-
Shard:
用于存储实际的数据块,实际生产环境中一个shard server角色可由几台机器组个一个replica set承担,防止主机单点故障
-
Config Server:
mongod实例,存储了整个 ClusterMetadata,其中包括 chunk信息。
-
Query Routers:
前端路由,客户端由此接入,且让整个集群看上去像单一数据库,前端应用可以透明使用。