MongoDB的基本使用

注意事项

  • 我们始终以下面的几条数据为测试用例:
"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中使用分片集群结构分布:

img

上图中主要有如下所述三个主要组件:

  • Shard:

    用于存储实际的数据块,实际生产环境中一个shard server角色可由几台机器组个一个replica set承担,防止主机单点故障

  • Config Server:

    mongod实例,存储了整个 ClusterMetadata,其中包括 chunk信息。

  • Query Routers:

    前端路由,客户端由此接入,且让整个集群看上去像单一数据库,前端应用可以透明使用。


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章