MongoDB数据库学习总结

NoSQL 简介

NoSQL(NoSQL = Not Only SQL ),意即"不仅仅是SQL"。

在现代的计算系统上每天网络上都会产生庞大的数据量。

这些数据有很大一部分是由关系数据库管理系统(RDBMS)来处理。 1970年 E.F.Codd's提出的关系模型的论文 "A relational model of data for large shared data banks",这使得数据建模和应用程序编程更加简单。

通过应用实践证明,关系模型是非常适合于客户服务器编程,远远超出预期的利益,今天它是结构化数据存储在网络和商务应用的主导技术。

NoSQL 是一项全新的数据库革命性运动,早期就有人提出,发展至2009年趋势越发高涨。NoSQL的拥护者们提倡运用非关系型的数据存储,相对于铺天盖地的关系型数据库运用,这一概念无疑是一种全新的思维的注入。

关系型数据库遵循ACID规则

事务在英文中是transaction,和现实世界中的交易很类似,它有如下四个特性:

1、A (Atomicity) 原子性

原子性很容易理解,也就是说事务里的所有操作要么全部做完,要么都不做,事务成功的条件是事务里的所有操作都成功,只要有一个操作失败,整个事务就失败,需要回滚。

比如银行转账,从A账户转100元至B账户,分为两个步骤:1)从A账户取100元;2)存入100元至B账户。这两步要么一起完成,要么一起不完成,如果只完成第一步,第二步失败,钱会莫名其妙少了100元。

2、C (Consistency) 一致性

一致性也比较容易理解,也就是说数据库要一直处于一致的状态,事务的运行不会改变数据库原本的一致性约束。

例如现有完整性约束a+b=10,如果一个事务改变了a,那么必须得改变b,使得事务结束后依然满足a+b=10,否则事务失败。

3、I (Isolation) 独立性

所谓的独立性是指并发的事务之间不会互相影响,如果一个事务要访问的数据正在被另外一个事务修改,只要另外一个事务未提交,它所访问的数据就不受未提交事务的影响。

比如现在有个交易是从A账户转100元至B账户,在这个交易还未完成的情况下,如果此时B查询自己的账户,是看不到新增加的100元的。

4、D (Durability) 持久性

持久性是指一旦事务提交后,它所做的修改将会永久的保存在数据库上,即使出现宕机也不会丢失。

分布式系统

分布式系统(distributed system)由多台计算机和通信的软件组件通过计算机网络连接(本地网络或广域网)组成。

分布式系统是建立在网络之上的软件系统。正是因为软件的特性,所以分布式系统具有高度的内聚性和透明性。

因此,网络和分布式系统之间的区别更多的在于高层软件(特别是操作系统),而不是硬件。

分布式系统可以应用在不同的平台上如:Pc、工作站、局域网和广域网上等。

分布式计算的优点

可靠性(容错) :

分布式计算系统中的一个重要的优点是可靠性。一台服务器的系统崩溃并不影响到其余的服务器。

可扩展性:

在分布式计算系统可以根据需要增加更多的机器。

资源共享:

共享数据是必不可少的应用,如银行,预订系统。

灵活性:

由于该系统是非常灵活的,它很容易安装,实施和调试新的服务。

更快的速度:

分布式计算系统可以有多台计算机的计算能力,使得它比其他系统有更快的处理速度。

开放系统:

由于它是开放的系统,本地或者远程都可以访问到该服务。

更高的性能:

相较于集中式计算机网络集群可以提供更高的性能(及更好的性价比)。

分布式计算的缺点

故障排除:

故障排除和诊断问题。

软件:

更少的软件支持是分布式计算系统的主要缺点。

网络:

网络基础设施的问题,包括:传输问题,高负载,信息丢失等。

安全性:

开放系统的特性让分布式计算系统存在着数据的安全性和共享的风险等问题。

什么是NoSQL?

NoSQL,指的是非关系型的数据库。NoSQL有时也称作Not Only SQL的缩写,是对不同于传统的关系型数据库的数据库管理系统的统称。

NoSQL用于超大规模数据的存储。(例如谷歌或Facebook每天为他们的用户收集万亿比特的数据)。这些类型的数据存储不需要固定的模式,无需多余操作就可以横向扩展。

为什么使用NoSQL ?

今天我们可以通过第三方平台(如:Google,Facebook等)可以很容易的访问和抓取数据。用户的个人信息,社交网络,地理位置,用户生成的数据和用户操作日志已经成倍的增加。我们如果要对这些用户数据进行挖掘,那SQL数据库已经不适合这些应用了, NoSQL 数据库的发展却能很好的处理这些大的数据。

img

实例

社会化关系网:

Each record: UserID1, UserID2
Separate records: UserID, first_name,last_name, age, gender,...
Task: Find all friends of friends of friends of ... friends of a given user.

Wikipedia 页面 :

Large collection of documents
Combination of structured and unstructured data
Task: Retrieve all pages regarding athletics of Summer Olympic before 1950.

RDBMS vs NoSQL

RDBMS
- 高度组织化结构化数据
- 结构化查询语言(SQL) (SQL)
- 数据和关系都存储在单独的表中。
- 数据操纵语言,数据定义语言
- 严格的一致性
- 基础事务

NoSQL
- 代表着不仅仅是SQL
- 没有声明性查询语言
- 没有预定义的模式
-键 - 值对存储,列存储,文档存储,图形数据库
- 最终一致性,而非ACID属性
- 非结构化和不可预知的数据
- CAP定理
- 高性能,高可用性和可伸缩性

img

NoSQL 简史

NoSQL一词最早出现于1998年,是Carlo Strozzi开发的一个轻量、开源、不提供SQL功能的关系数据库。

2009年,Last.fm的Johan Oskarsson发起了一次关于分布式开源数据库的讨论[2],来自Rackspace的Eric Evans再次提出了NoSQL的概念,这时的NoSQL主要指非关系型、分布式、不提供ACID的数据库设计模式。

2009年在亚特兰大举行的"no:sql(east)"讨论会是一个里程碑,其口号是"select fun, profit from real_world where relational=false;"。因此,对NoSQL最普遍的解释是"非关联型的",强调Key-Value Stores和文档数据库的优点,而不是单纯的反对RDBMS。

CAP定理(CAP theorem)

在计算机科学中, CAP定理(CAP theorem), 又被称作 布鲁尔定理(Brewer's theorem), 它指出对于一个分布式计算系统来说,不可能同时满足以下三点:

  • 一致性(Consistency) (所有节点在同一时间具有相同的数据)
  • 可用性(Availability) (保证每个请求不管成功或者失败都有响应)
  • 分隔容忍(Partition tolerance) (系统中任意信息的丢失或失败不会影响系统的继续运作)

CAP理论的核心是:一个分布式系统不可能同时很好的满足一致性,可用性和分区容错性这三个需求,最多只能同时较好的满足两个。

因此,根据 CAP 原理将 NoSQL 数据库分成了满足 CA 原则、满足 CP 原则和满足 AP 原则三 大类:

  • CA - 单点集群,满足一致性,可用性的系统,通常在可扩展性上不太强大。
  • CP - 满足一致性,分区容忍性的系统,通常性能不是特别高。
  • AP - 满足可用性,分区容忍性的系统,通常可能对一致性要求低一些。

img

NoSQL的优点/缺点

优点:

  • - 高可扩展性
  • - 分布式计算
  • - 低成本
  • - 架构的灵活性,半结构化数据
  • - 没有复杂的关系

缺点:

  • - 没有标准化
  • - 有限的查询功能(到目前为止)
  • - 最终一致是不直观的程序

BASE

BASE:Basically Available, Soft-state, Eventually Consistent。 由 Eric Brewer 定义。

CAP理论的核心是:一个分布式系统不可能同时很好的满足一致性,可用性和分区容错性这三个需求,最多只能同时较好的满足两个。

BASE是NoSQL数据库通常对可用性及一致性的弱要求原则:

  • Basically Availble --基本可用
  • Soft-state --软状态/柔性事务。 "Soft state" 可以理解为"无连接"的, 而 "Hard state" 是"面向连接"的
  • Eventual Consistency -- 最终一致性, 也是 ACID 的最终目的。

ACID vs BASE

ACID BASE
原子性(Atomicity) 基本可用(Basically Available)
一致性(Consistency) 软状态/柔性事务(Soft state)
隔离性(Isolation) 最终一致性 (Eventual consistency)
持久性 (Durable)

NoSQL 数据库分类

类型 部分代表 特点
列存储 HbaseCassandraHypertable 顾名思义,是按列存储数据的。最大的特点是方便存储结构化和半结构化数据,方便做数据压缩,对针对某一列或者某几列的查询有非常大的IO优势。
文档存储 MongoDBCouchDB 文档存储一般用类似json的格式存储,存储的内容是文档型的。这样也就有机会对某些字段建立索引,实现关系数据库的某些功能。
key-value存储 Tokyo Cabinet / TyrantBerkeley DBMemcacheDBRedis 可以通过key快速查询到其value。一般来说,存储不管value的格式,照单全收。(Redis包含了其他功能)
图存储 Neo4JFlockDB 图形关系的最佳存储。使用传统关系数据库来解决的话性能低下,而且设计使用不方便。
对象存储 db4oVersant 通过类似面向对象语言的语法操作数据库,通过对象的方式存取数据。
xml数据库 Berkeley DB XMLBaseX 高效的存储XML数据,并支持XML的内部查询语法,比如XQuery,Xpath。

谁在使用

现在已经有很多公司使用了 NoSQL:

  • Google
  • Facebook
  • Mozilla
  • Adobe
  • Foursquare
  • LinkedIn
  • Digg
  • McGraw-Hill Education
  • Vermont Public Radio

MongoDB 概念解析

MongoDB是一个基于分布式文件存储的数据库。由C++语言编写。旨在为WEB应用提供可扩展的高性能数据存储解决方案。

MongoDB是一个介于关系型数据库和非关系型数据库之间的产品,是非关系型数据库中功能最丰富,最像关系型数据库的。

不管我们学习什么数据库都应该学习其中的基础概念,在mongodb中基本的概念是文档、集合、数据库,下面我们挨个介绍。

下表将帮助您更容易理解Mongo中的一些概念:

SQL术语/概念 MongoDB术语/概念 解释/说明
database database 数据库
table collection 数据库表/集合
row document 数据记录行/文档
column field 数据字段/域
index index 索引
table joins 表连接,MongoDB不支持
primary key primary key 主键,MongoDB自动将_id字段设置为主键

通过下图实例,我们也可以更直观的了解Mongo中的一些概念:

img


数据库

一个mongodb中可以建立多个数据库。

MongoDB的默认数据库为"db",该数据库存储在data目录中。

MongoDB的单个实例可以容纳多个独立的数据库,每一个都有自己的集合和权限,不同的数据库也放置在不同的文件中。

"show dbs" 命令可以显示所有数据的列表。

$ ./mongo
MongoDB shell version: 3.0.6
connecting to: test
> show dbs
local  0.078GB
test   0.078GB
> 

执行 "db" 命令可以显示当前数据库对象或集合。

$ ./mongo
MongoDB shell version: 3.0.6
connecting to: test
> db
test
> 

运行"use"命令,可以连接到一个指定的数据库。

> use local
switched to db local
> db
local
> 

以上实例命令中,"local" 是你要链接的数据库。

在下一个章节我们将详细讲解MongoDB中命令的使用。

数据库也通过名字来标识。数据库名可以是满足以下条件的任意UTF-8字符串。

  • 不能是空字符串("")。
  • 不得含有' '(空格)、.、$、/、\和\0 (空字符)。
  • 应全部小写。
  • 最多64字节。

有一些数据库名是保留的,可以直接访问这些有特殊作用的数据库。

  • admin: 从权限的角度来看,这是"root"数据库。要是将一个用户添加到这个数据库,这个用户自动继承所有数据库的权限。一些特定的服务器端命令也只能从这个数据库运行,比如列出所有的数据库或者关闭服务器。
  • local: 这个数据永远不会被复制,可以用来存储限于本地单台服务器的任意集合
  • config: 当Mongo用于分片设置时,config数据库在内部使用,用于保存分片的相关信息。

文档(Document)

文档是一组键值(key-value)对(即 BSON)。MongoDB 的文档不需要设置相同的字段,并且相同的字段不需要相同的数据类型,这与关系型数据库有很大的区别,也是 MongoDB 非常突出的特点。

一个简单的文档例子如下:

{"site":"www.runoob.com", "name":"菜鸟教程"}

下表列出了 RDBMS 与 MongoDB 对应的术语:

RDBMS MongoDB
数据库 数据库
表格 集合
文档
字段
表联合 嵌入文档
主键 主键 (MongoDB 提供了 key 为 _id )
数据库服务和客户端
Mysqld/Oracle mongod
mysql/sqlplus mongo

需要注意的是:

  1. 文档中的键/值对是有序的。
  2. 文档中的值不仅可以是在双引号里面的字符串,还可以是其他几种数据类型(甚至可以是整个嵌入的文档)。
  3. MongoDB区分类型和大小写。
  4. MongoDB的文档不能有重复的键。
  5. 文档的键是字符串。除了少数例外情况,键可以使用任意UTF-8字符。

文档键命名规范:

  • 键不能含有\0 (空字符)。这个字符用来表示键的结尾。
  • .和$有特别的意义,只有在特定环境下才能使用。
  • 以下划线"_"开头的键是保留的(不是严格要求的)。

集合

集合就是 MongoDB 文档组,类似于 RDBMS (关系数据库管理系统:Relational Database Management System)中的表格。

集合存在于数据库中,集合没有固定的结构,这意味着你在对集合可以插入不同格式和类型的数据,但通常情况下我们插入集合的数据都会有一定的关联性。

比如,我们可以将以下不同数据结构的文档插入到集合中:

{"site":"www.baidu.com"}
{"site":"www.google.com","name":"Google"}
{"site":"www.runoob.com","name":"菜鸟教程","num":5}

当第一个文档插入时,集合就会被创建。

合法的集合名

  • 集合名不能是空字符串""。
  • 集合名不能含有\0字符(空字符),这个字符表示集合名的结尾。
  • 集合名不能以"system."开头,这是为系统集合保留的前缀。
  • 用户创建的集合名字不能含有保留字符。有些驱动程序的确支持在集合名里面包含,这是因为某些系统生成的集合中包含该字符。除非你要访问这种系统创建的集合,否则千万不要在名字里出现$。 

如下实例:

db.col.findOne()

capped collections

Capped collections 就是固定大小的collection。

它有很高的性能以及队列过期的特性(过期按照插入的顺序). 有点和 "RRD" 概念类似。

Capped collections 是高性能自动的维护对象的插入顺序。它非常适合类似记录日志的功能和标准的 collection 不同,你必须要显式的创建一个capped collection,指定一个 collection 的大小,单位是字节。collection 的数据存储空间值提前分配的。

Capped collections 可以按照文档的插入顺序保存到集合中,而且这些文档在磁盘上存放位置也是按照插入顺序来保存的,所以当我们更新Capped collections 中文档的时候,更新后的文档不可以超过之前文档的大小,这样话就可以确保所有文档在磁盘上的位置一直保持不变。

由于 Capped collection 是按照文档的插入顺序而不是使用索引确定插入位置,这样的话可以提高增添数据的效率。MongoDB 的操作日志文件 oplog.rs 就是利用 Capped Collection 来实现的。

要注意的是指定的存储大小包含了数据库的头信息。

db.createCollection("mycoll", {capped:true, size:100000})
  • 在 capped collection 中,你能添加新的对象。
  • 能进行更新,然而,对象不会增加存储空间。如果增加,更新就会失败 。
  • 使用 Capped Collection 不能删除一个文档,可以使用 drop() 方法删除 collection 所有的行。
  • 删除之后,你必须显式的重新创建这个 collection。
  • 在32bit机器中,capped collection 最大存储为 1e9( 1X109)个字节。

元数据

数据库的信息是存储在集合中。它们使用了系统的命名空间:

dbname.system.*

在MongoDB数据库中名字空间 .system.* 是包含多种系统信息的特殊集合(Collection),如下:

集合命名空间 描述
dbname.system.namespaces 列出所有名字空间。
dbname.system.indexes 列出所有索引。
dbname.system.profile 包含数据库概要(profile)信息。
dbname.system.users 列出所有可访问数据库的用户。
dbname.local.sources 包含复制对端(slave)的服务器信息和状态。

对于修改系统集合中的对象有如下限制。

在{{system.indexes}}插入数据,可以创建索引。但除此之外该表信息是不可变的(特殊的drop index命令将自动更新相关信息)。

{{system.users}}是可修改的。 {{system.profile}}是可删除的。


MongoDB 数据类型

下表为MongoDB中常用的几种数据类型。

数据类型 描述
String 字符串。存储数据常用的数据类型。在 MongoDB 中,UTF-8 编码的字符串才是合法的。
Integer 整型数值。用于存储数值。根据你所采用的服务器,可分为 32 位或 64 位。
Boolean 布尔值。用于存储布尔值(真/假)。
Double 双精度浮点值。用于存储浮点值。
Min/Max keys 将一个值与 BSON(二进制的 JSON)元素的最低值和最高值相对比。
Array 用于将数组或列表或多个值存储为一个键。
Timestamp 时间戳。记录文档修改或添加的具体时间。
Object 用于内嵌文档。
Null 用于创建空值。
Symbol 符号。该数据类型基本上等同于字符串类型,但不同的是,它一般用于采用特殊符号类型的语言。
Date 日期时间。用 UNIX 时间格式来存储当前日期或时间。你可以指定自己的日期时间:创建 Date 对象,传入年月日信息。
Object ID 对象 ID。用于创建文档的 ID。
Binary Data 二进制数据。用于存储二进制数据。
Code 代码类型。用于在文档中存储 JavaScript 代码。
Regular expression 正则表达式类型。用于存储正则表达式。

下面说明下几种重要的数据类型。

ObjectId

ObjectId 类似唯一主键,可以很快的去生成和排序,包含 12 bytes,含义是:

  • 前 4 个字节表示创建 unix 时间戳,格林尼治时间 UTC 时间,比北京时间晚了 8 个小时
  • 接下来的 3 个字节是机器标识码
  • 紧接的两个字节由进程 id 组成 PID
  • 最后三个字节是随机数

img

MongoDB 中存储的文档必须有一个 _id 键。这个键的值可以是任何类型的,默认是个 ObjectId 对象

由于 ObjectId 中保存了创建的时间戳,所以你不需要为你的文档保存时间戳字段,你可以通过 getTimestamp 函数来获取文档的创建时间:

> var newObject = ObjectId()
> newObject.getTimestamp()
ISODate("2017-11-25T07:21:10Z")

ObjectId 转为字符串

> newObject.str
5a1919e63df83ce79df8b38f

字符串

BSON 字符串都是 UTF-8 编码。

时间戳

BSON 有一个特殊的时间戳类型用于 MongoDB 内部使用,与普通的 日期 类型不相关。 时间戳值是一个 64 位的值。其中:

  • 前32位是一个 time_t 值(与Unix新纪元相差的秒数)
  • 后32位是在某秒中操作的一个递增的序数

在单个 mongod 实例中,时间戳值通常是唯一的。

在复制集中, oplog 有一个 ts 字段。这个字段中的值使用BSON时间戳表示了操作时间。

BSON 时间戳类型主要用于 MongoDB 内部使用。在大多数情况下的应用开发中,你可以使用 BSON 日期类型。

日期

表示当前距离 Unix新纪元(1970年1月1日)的毫秒数。日期类型是有符号的, 负数表示 1970 年之前的日期。

> var mydate1 = new Date()     //格林尼治时间
> mydate1
ISODate("2018-03-04T14:58:51.233Z")
> typeof mydate1
object
> var mydate2 = ISODate() //格林尼治时间
> mydate2
ISODate("2018-03-04T15:00:45.479Z")
> typeof mydate2
object

这样创建的时间是日期类型,可以使用 JS 中的 Date 类型的方法。

返回一个时间类型的字符串:

> var mydate1str = mydate1.toString()
> mydate1str
Sun Mar 04 2018 14:58:51 GMT+0000 (UTC) 
> typeof mydate1str
string

或者

> Date()
Sun Mar 04 2018 15:02:59 GMT+0000 (UTC)  

下载安装

基本操作

1.启动服务端
mongod.exe --dbpath "C:\\MongoDB\data\db" --logpath "C:\\MongoDB\data\log\mongo.log"

2.启动客户端
mongo.exe --port 27017
# 默认端口:27017 可以自行指定端口启动

3.直接创建库
use students

4.自主创建student表,在mongodb中称为集合。写入数据
db.student.save({name:"aliang", age:18})

5.插入数据
db.student.insert({name:"aliang", age:20, city:"shenzhen"})
# 注意:无则添加,有则修改

6.查看数据
db.student.find()
# 查找全部数据

7.按条件查找
db.student.find({age:20})

8.统计数据
db.student.count()

9.按查找条件统计数据
db.student.find({name:"aliang"}).count()

10.按查找出来的某一字段的大小排序
db.student.find().sort({age:1})
# 1 即表示从小到大顺序排序

db.student.find().sort({age:-1})
# -1 即表示从大到小倒序排序

11.美化查询结果
db.student.find().sort({age:-1}).pretty(0)

BSON

BSON是一种计算机的数据交换格式

主要被用作MongoDB数据库中的数据存储和网络传输格式

是一种二进制表示形式,能用来表示简单的数据结构、关系数组(MongoDB中被称为“对象”或者“文档”)以及MongoDB中的各种数据类型

Binary JSON——>二进制的JSON

MongoDB GridFS

GridFS 用于存储和恢复那些超过16M(BSON文件限制)的文件(如:图片、音频、视频等)。

GridFS 也是文件存储的一种方式,但是它是存储在MonoDB的集合中。

GridFS 可以更好的存储大于16M的文件。

GridFS 会将大文件对象分割成多个小的chunk(文件片段),一般为256k/个,每个chunk将作为MongoDB的一个文档(document)被存储在chunks集合中。

GridFS 用两个集合来存储一个文件:fs.files与fs.chunks。

每个文件的实际内容被存在chunks(二进制数据)中,和文件有关的meta数据(filename,content_type,还有用户自定义的属性)将会被存在files集合中。

以下是简单的 fs.files 集合文档:

{
   "filename": "test.txt",
   "chunkSize": NumberInt(261120),
   "uploadDate": ISODate("2014-04-13T11:32:33.557Z"),
   "md5": "7b762939321e146569b07f72c62cca4f",
   "length": NumberInt(646)
}

以下是简单的 fs.chunks 集合文档:

{
   "files_id": ObjectId("534a75d19f54bfec8a2fe44b"),
   "n": NumberInt(0),
   "data": "Mongo Binary Data"
}

GridFS 添加文件

现在我们使用 GridFS 的 put 命令来存储 mp3 文件。 调用 MongoDB 安装目录下bin的 mongofiles.exe工具。

打开命令提示符,进入到MongoDB的安装目录的bin目录中,找到mongofiles.exe,并输入下面的代码:

>mongofiles.exe -d gridfs put song.mp3

-d gridfs 指定存储文件的数据库名称,如果不存在该数据库,MongoDB会自动创建。如果不存在该数据库,MongoDB会自动创建。Song.mp3 是音频文件名。

使用以下命令来查看数据库中文件的文档:

>db.fs.files.find()

以上命令执行后返回以下文档数据:

{
   _id: ObjectId('534a811bf8b4aa4d33fdf94d'), 
   filename: "song.mp3", 
   chunkSize: 261120, 
   uploadDate: new Date(1397391643474), md5: "e4f53379c909f7bed2e9d631e15c1c41",
   length: 10401959 
}

我们可以看到 fs.chunks 集合中所有的区块,以下我们得到了文件的 _id 值,我们可以根据这个 _id 获取区块(chunk)的数据:

>db.fs.chunks.find({files_id:ObjectId('534a811bf8b4aa4d33fdf94d')})

以上实例中,查询返回了 40 个文档的数据,意味着mp3文件被存储在40个区块中。

Python MongoDB 查询文档(集合collection)

MongoDB 中使用了 find 和 find_one 方法来查询集合中的数据,它类似于 SQL 中的 SELECT 语句。

本文使用的测试数据如下:内容显示是按照年龄大小倒序排列之后美化展示的效果

> show dbs
admin    0.000GB
config   0.000GB
local    0.000GB
student  0.000GB
> use student
switched to db student
> show tables
student
> db.student.find().sort({age:-1}).pretty(0)
{
        "_id" : ObjectId("5f805008cda441be27325c00"),
        "name" : "frank",
        "age" : 23
}
{
        "_id" : ObjectId("5f804dd4cda441be27325bfd"),
        "name" : "aliang",
        "age" : 18
}
{
        "_id" : ObjectId("5f804e32cda441be27325bfe"),
        "name" : "yexiang",
        "age" : 18,
        "city" : "shanghai"
}
{
        "_id" : ObjectId("5f804e53cda441be27325bff"),
        "name" : "yexiang",
        "age" : 18,
        "city" : "shanghai",
        "mobile" : "17361537209"
}
{ "_id" : ObjectId("5f804d9acda441be27325bfc"), "name" : "frank" }

查询一条数据——>find_one()

我们可以使用 find_one() 方法来查询集合中的一条数据。

查询 student 文档(集合)中的第一条数据:

实例

import pymongo

# 连接本地数据库,默认端口是27017
myclient = pymongo.MongoClient("mongodb://localhost:27017/")
# myclient即连接对象,使用连接对象选择要操作的数据库 "student"
mydb = myclient["student"]
# mydb 即选择的数据库的实例化对象,使用此库对象选择要操作的表即集合 "student"
mycollection = mydb["student"]
# mycollection 即选择的数据集合的实例化对象
x = mycollection.find_one()
print(x)

输出结果为:

{
'_id': ObjectId('5f804d9acda441be27325bfc'), 
'name': 'frank'
}

查询集合中所有数据——>find()

find() 方法可以查询集合中的所有数据,类似 SQL 中的 SELECT * 操作。

以下实例查找 student 集合中的所有数据:

实例

import pymongo

# 连接本地数据库,默认端口是27017
myclient = pymongo.MongoClient("mongodb://localhost:27017/")
# myclient即连接对象,使用连接对象选择要操作的数据库 "student"
mydb = myclient["student"]
# mydb 即选择的数据库的实例化对象,使用此库对象选择要操作的表 "student"
mycollection = mydb["student"]
# mycollection 即选择的数据表的实例化对象
x = mycollection.find()
print(x)
for i in x:
    print(i)

输出结果为:

<pymongo.cursor.Cursor object at 0x000002374A811248>
{'_id': ObjectId('5f804d9acda441be27325bfc'), 'name': 'frank'}
{'_id': ObjectId('5f804dd4cda441be27325bfd'), 'name': 'aliang', 'age': 18.0}
{'_id': ObjectId('5f804e32cda441be27325bfe'), 'name': 'yexiang', 'age': 18.0, 'city': 'shanghai'}
{'_id': ObjectId('5f804e53cda441be27325bff'), 'name': 'yexiang', 'age': 18.0, 'city': 'shanghai', 'mobile': '17361537209'}
{'_id': ObjectId('5f805008cda441be27325c00'), 'name': 'frank', 'age': 23.0}

查询指定字段的数据——>find({}, {...})

我们可以使用 find() 方法来查询指定字段的数据。

mycollection.find({}, {'_id': 0, 'name': 1, 'age': 1})

# 即表示'_id'不需要查询,仅查询'name'以及'age'对应的数据,其他字段的数据信息没有标明则表示不需要查询!

实例

import pymongo

# 连接本地数据库,默认端口是27017
myclient = pymongo.MongoClient("mongodb://localhost:27017/")
# myclient即连接对象,使用连接对象选择要操作的数据库 "student"
mydb = myclient["student"]
# mydb 即选择的数据库的实例化对象,使用此库对象选择要操作的表 "student"
mycollection = mydb["student"]
# mycollection 即选择的数据表的实例化对象

# 该注意的是:一条按需查找的语句中,只能设置'_id'这个字段和其他字段的值不同,比如以下查询语句。
# 除过'_id'字段,其他字段如果需要查询就设置成1,如果不需要查询就干脆不要写,如果写成{'name': 0, 'age': 1},会报错的。
x = mycollection.find({}, {'_id': 0, 'name': 1, 'age': 1})
for i in x:
    print(i)

输出结果为:

{'name': 'frank'}
{'name': 'aliang', 'age': 18.0}
{'name': 'yexiang', 'age': 18.0}
{'name': 'yexiang', 'age': 18.0}
{'name': 'frank', 'age': 23.0}

以下实例除了city字段外,其他都返回:

实例

import pymongo

# 连接本地数据库,默认端口是27017
myclient = pymongo.MongoClient("mongodb://localhost:27017/")
# myclient即连接对象,使用连接对象选择要操作的数据库 "student"
mydb = myclient["student"]
# mydb 即选择的数据库的实例化对象,使用此库对象选择要操作的表 "student"
mycollection = mydb["student"]
# mycollection 即选择的数据表的实例化对象

x = mycollection.find({}, {'city': 0})
for i in x:
    print(i)

输出结果为:

{'_id': ObjectId('5f804d9acda441be27325bfc'), 'name': 'frank'}
{'_id': ObjectId('5f804dd4cda441be27325bfd'), 'name': 'aliang', 'age': 18.0}
{'_id': ObjectId('5f804e32cda441be27325bfe'), 'name': 'yexiang', 'age': 18.0}
{'_id': ObjectId('5f804e53cda441be27325bff'), 'name': 'yexiang', 'age': 18.0, 'mobile': '17361537209'}
{'_id': ObjectId('5f805008cda441be27325c00'), 'name': 'frank', 'age': 23.0}

以下代码同时指定了 0 和 1 则会报错

import pymongo

# 连接本地数据库,默认端口是27017
myclient = pymongo.MongoClient("mongodb://localhost:27017/")
# myclient即连接对象,使用连接对象选择要操作的数据库 "student"
mydb = myclient["student"]
# mydb 即选择的数据库的实例化对象,使用此库对象选择要操作的表 "student"
mycollection = mydb["student"]
# mycollection 即选择的数据表的实例化对象
x = mycollection.find({}, {'name': 1, 'city': 0})
for i in x:
    print(i)

错误内容大概如下:

pymongo.errors.OperationFailure: Projection cannot have a mix of inclusion and exclusion., full error: {'ok': 0.0, 'errmsg': 'Projection cannot have a mix of inclusion and exclusion.', 'code': 2, 'codeName': 'BadValue'}

根据指定条件查询——>find({'age': 18})

我们可以在 find() 中设置参数来过滤数据。

以下实例查找 age 字段为 18 的数据:

实例

import pymongo

# 连接本地数据库,默认端口是27017
myclient = pymongo.MongoClient("mongodb://localhost:27017/")
# myclient即连接对象,使用连接对象选择要操作的数据库 "student"
mydb = myclient["student"]
# mydb 即选择的数据库的实例化对象,使用此库对象选择要操作的表 "student"
mycollection = mydb["student"]
# mycollection 即选择的数据表的实例化对象

x = mycollection.find({'age': 18})
for i in x:
    print(i)

输出结果为:

{'_id': ObjectId('5f804dd4cda441be27325bfd'), 'name': 'aliang', 'age': 18.0}
{'_id': ObjectId('5f804e32cda441be27325bfe'), 'name': 'yexiang', 'age': 18.0, 'city': 'shanghai'}
{'_id': ObjectId('5f804e53cda441be27325bff'), 'name': 'yexiang', 'age': 18.0, 'city': 'shanghai', 'mobile': '17361537209'}

高级查询

查询的条件语句中,我们还可以使用修饰符($gt:大于, $lt:小于)

以下实例用于读取 age 字段大于18的数据信息,大于的修饰符条件为 {"$gt": 18} :

实例

import pymongo

# 连接本地数据库,默认端口是27017
myclient = pymongo.MongoClient("mongodb://localhost:27017/")
# myclient即连接对象,使用连接对象选择要操作的数据库 "student"
mydb = myclient["student"]
# mydb 即选择的数据库的实例化对象,使用此库对象选择要操作的表 "student"
mycollection = mydb["student"]
# mycollection 即选择的数据表的实例化对象

x = mycollection.find({'age': {'$gt': 18}})
for i in x:
    print(i)

输出结果为:

{'_id': ObjectId('5f805008cda441be27325c00'), 'name': 'frank', 'age': 23.0}

使用正则表达式查询

我们还可以使用正则表达式作为修饰符。

正则表达式修饰符只用于搜索字符串的字段。

以下实例用于读取 name 字段中第一个字母为 "a" 的数据,正则表达式修饰符条件为 {"$regex": "^a"} :

实例

import pymongo

# 连接本地数据库,默认端口是27017
myclient = pymongo.MongoClient("mongodb://localhost:27017/")
# myclient即连接对象,使用连接对象选择要操作的数据库 "student"
mydb = myclient["student"]
# mydb 即选择的数据库的实例化对象,使用此库对象选择要操作的表 "student"
mycollection = mydb["student"]
# mycollection 即选择的数据表的实例化对象

x = mycollection.find({'name': {'$regex': '^a'}})
for i in x:
    print(i)

输出结果为:

{'_id': ObjectId('5f804dd4cda441be27325bfd'), 'name': 'aliang', 'age': 18.0}

返回指定条数记录

如果我们要对查询结果设置指定条数的记录可以使用 limit() 方法,该方法只接受一个数字参数。

以下实例返回 2 条文档记录:

实例

import pymongo

# 连接本地数据库,默认端口是27017
myclient = pymongo.MongoClient("mongodb://localhost:27017/")
# myclient即连接对象,使用连接对象选择要操作的数据库 "student"
mydb = myclient["student"]
# mydb 即选择的数据库的实例化对象,使用此库对象选择要操作的表 "student"
mycollection = mydb["student"]
# mycollection 即选择的数据表的实例化对象

x = mycollection.find().limit(2)
for i in x:
    print(i)

输出结果为:

{'_id': ObjectId('5f804d9acda441be27325bfc'), 'name': 'frank'}
{'_id': ObjectId('5f804dd4cda441be27325bfd'), 'name': 'aliang', 'age': 18.0}

Python MongoDB 删除数据

删除一个文档

我们可以使用 delete_one() 方法来删除一个文档,

该方法第一个参数为查询对象,指定要删除哪些数据。

以下实例删除 name 字段值为 "aliang" 的文档:

实例

import pymongo

# 连接本地数据库,默认端口是27017
myclient = pymongo.MongoClient("mongodb://localhost:27017/")
# myclient即连接对象,使用连接对象选择要操作的数据库 "student"
mydb = myclient["student"]
# mydb 即选择的数据库的实例化对象,使用此库对象选择要操作的表 "student"
mycollection = mydb["student"]
# mycollection 即选择的数据表的实例化对象

# 只删除第一个出现的'yexiang'
mycollection.delete_one({'name': 'yexiang'})

# 删除后输出:
for i in mycollection.find():
    print(i)

输出结果为:

{'_id': ObjectId('5f804d9acda441be27325bfc'), 'name': 'frank'}
{'_id': ObjectId('5f804dd4cda441be27325bfd'), 'name': 'aliang', 'age': 18.0}
{'_id': ObjectId('5f804e53cda441be27325bff'), 'name': 'yexiang', 'age': 18.0, 'city': 'shanghai', 'mobile': '17361537209'}
{'_id': ObjectId('5f805008cda441be27325c00'), 'name': 'frank', 'age': 23.0}

删除多个文档

我们可以使用 delete_many() 方法来删除多个文档,该方法第一个参数为查询对象,指定要删除哪些数据。

删除所有 name 字段中以 f 开头的文档:

实例

import pymongo

# 连接本地数据库,默认端口是27017
myclient = pymongo.MongoClient("mongodb://localhost:27017/")
# myclient即连接对象,使用连接对象选择要操作的数据库 "student"
mydb = myclient["student"]
# mydb 即选择的数据库的实例化对象,使用此库对象选择要操作的表 "student"
mycollection = mydb["student"]
# mycollection 即选择的数据表的实例化对象

x = mycollection.delete_many({'name': {'$regex': '^f'}})
print(x.deleted_count, "个文档已经删除!")

输出结果为:

2 个文档已删除

删除集合中的所有文档

delete_many() 方法如果传入的是一个空的查询对象,则会删除集合中的所有文档:

实例

import pymongo

# 连接本地数据库,默认端口是27017
myclient = pymongo.MongoClient("mongodb://localhost:27017/")
# myclient即连接对象,使用连接对象选择要操作的数据库 "student"
mydb = myclient["student"]
# mydb 即选择的数据库的实例化对象,使用此库对象选择要操作的表 "student"
mycollection = mydb["student"]
# mycollection 即选择的数据表的实例化对象

# 集合中的所有文档全部被删除
mycollection.delete_many({})

删除集合

我们可以使用 drop() 方法来删除一个集合。

以下实例删除了 student 集合:

实例

import pymongo

# 连接本地数据库,默认端口是27017
myclient = pymongo.MongoClient("mongodb://localhost:27017/")
# myclient即连接对象,使用连接对象选择要操作的数据库 "student"
mydb = myclient["student"]
# mydb 即选择的数据库的实例化对象,使用此库对象选择要操作的表 "student"
mycollection = mydb["student"]
# mycollection 即选择的数据表的实例化对象

# 删除student集合
mycollection.drop()

如果删除成功 drop() 返回 true,如果删除失败(集合不存在)则返回 false。

删除数据记录中的某一个字段数据

db.getCollection("TempSample").update({},{$unset:{'tag_id_list':''}},false, true)

排序

sort() 方法可以指定升序或降序排序。

sort() 方法第一个参数为要排序的字段,第二个字段指定排序规则,1 为升序,-1 为降序,默认为升序。

Python MongoDB 修改文档

修改一个文档

我们可以在 MongoDB 中使用 update_one() 方法修改文档中的记录。

该方法第一个参数为查询的条件,第二个参数为要修改的字段。

如果查找到的匹配数据多于一条,则只会修改第一条。

实例

import pymongo

# 连接本地数据库,默认端口是27017
myclient = pymongo.MongoClient("mongodb://localhost:27017/")
# myclient即连接对象,使用连接对象选择要操作的数据库 "student"
mydb = myclient["student"]
# mydb 即选择的数据库的实例化对象,使用此库对象选择要操作的表 "user"
mycollection = mydb["user"]
# mycollection 即选择的数据表的实例化对象

mycollection.update_one({"age": 18}, {"$set": {"age": 24}})

for i in mycollection.find():
    print(i)

执行输出结果为:

{'_id': ObjectId('5f856c81878d502dbe841498'), 'name': 'aliang', 'age': 24}

修改匹配到的所有数据信息

update_one() 方法只能修匹配到的第一条记录,如果要修改所有匹配到的记录,可以使用 update_many()

以下实例将查找所有以 a 开头的 name 字段,并将匹配到所有记录的 age 字段修改为 18

实例

import pymongo

# 连接本地数据库,默认端口是27017
myclient = pymongo.MongoClient("mongodb://localhost:27017/")
# myclient即连接对象,使用连接对象选择要操作的数据库 "student"
mydb = myclient["student"]
# mydb 即选择的数据库的实例化对象,使用此库对象选择要操作的表 "user"
mycollection = mydb["user"]
# mycollection 即选择的数据表的实例化对象

x = mycollection.update_many({"name": {"$regex": "^a"}}, {"$set": {"age": 18}})
print(x.modified_count, "个文档被修改!")

for i in mycollection.find():
   print(i)

输出结果为:

2 个文档被修改!
{'_id': ObjectId('5f856c81878d502dbe841498'), 'name': 'aliang', 'age': 18}
{'_id': ObjectId('5f856ecc878d502dbe841499'), 'name': 'ahuang', 'age': 18}

Python MongoDB 插入文档

MongoDB 中的一个文档类似 SQL 表中的一条记录。

插入集合

集合中插入文档使用 insert_one() 方法,该方法的第一参数是字典 name: value 对。

以下实例向 user 集合中插入文档:

实例

import pymongo

# 连接本地数据库,默认端口是27017
myclient = pymongo.MongoClient("mongodb://localhost:27017/")
# myclient即连接对象,使用连接对象选择要操作的数据库 "student"
mydb = myclient["student"]
# mydb 即选择的数据库的实例化对象,使用此库对象选择要操作的表 "user"
mycollection = mydb["user"]
# mycollection 即选择的数据表的实例化对象

mycollection.insert_one({'name': 'huayignxiong', 'age': 180})

for i in mycollection.find():
    print(i)

执行输出结果为:

{'_id': ObjectId('5f856c81878d502dbe841498'), 'name': 'aliang', 'age': 10}
{'_id': ObjectId('5f856ecc878d502dbe841499'), 'name': 'ahuang', 'age': 10}
{'_id': ObjectId('5f8572fcc9ed7fc1e0eab03f'), 'name': 'huayignxiong', 'age': 180}

返回 _id 字段

insert_one() 方法返回 InsertOneResult 对象,该对象包含 inserted_id 属性,它是插入文档的 id 值。

实例

import pymongo

# 连接本地数据库,默认端口是27017
myclient = pymongo.MongoClient("mongodb://localhost:27017/")
# myclient即连接对象,使用连接对象选择要操作的数据库 "student"
mydb = myclient["student"]
# mydb 即选择的数据库的实例化对象,使用此库对象选择要操作的表 "user"
mycollection = mydb["user"]
# mycollection 即选择的数据表的实例化对象

x = mycollection.insert_one({'name': '狗哥', 'age': 0})
print(x.inserted_id)

执行输出结果为:

5f85740edca4eb3a6e781c29

如果我们在插入文档时没有指定 _id,MongoDB 会为每个文档添加一个唯一的 id

插入多个文档

集合中插入多个文档使用 insert_many() 方法,该方法的第一参数是字典列表。

实例

import pymongo

# 连接本地数据库,默认端口是27017
myclient = pymongo.MongoClient("mongodb://localhost:27017/")
# myclient即连接对象,使用连接对象选择要操作的数据库 "student"
mydb = myclient["student"]
# mydb 即选择的数据库的实例化对象,使用此库对象选择要操作的表 "user"
mycollection = mydb["user"]
# mycollection 即选择的数据表的实例化对象

x = mycollection.insert_many([{'name': '天行', 'age': 10}, {'name': '阿良', 'age': 20}])
print(x.inserted_ids)

for i in mycollection.find():
    print(i)

输出结果类似如下:

[ObjectId('5f857538711bf2a6c03f2a9e'), ObjectId('5f857538711bf2a6c03f2a9f')]
{'_id': ObjectId('5f856c81878d502dbe841498'), 'name': 'aliang', 'age': 10}
{'_id': ObjectId('5f856ecc878d502dbe841499'), 'name': 'ahuang', 'age': 10}
{'_id': ObjectId('5f8572fcc9ed7fc1e0eab03f'), 'name': 'huayignxiong', 'age': 180}
{'_id': ObjectId('5f85740edca4eb3a6e781c29'), 'name': '狗哥', 'age': 0}
{'_id': ObjectId('5f857538711bf2a6c03f2a9e'), 'name': '天行', 'age': 10}
{'_id': ObjectId('5f857538711bf2a6c03f2a9f'), 'name': '阿良', 'age': 20}

insert_many() 方法返回 InsertManyResult 对象,该对象包含 inserted_ids 属性,该属性保存着所有插入文档的 id 值。

插入指定 _id 的多个文档

我们也可以自己指定 id,插入,以下实例我们在 user 集合中插入数据,_id 为我们指定的:

实例

import pymongo

# 连接本地数据库,默认端口是27017
myclient = pymongo.MongoClient("mongodb://localhost:27017/")
# myclient即连接对象,使用连接对象选择要操作的数据库 "student"
mydb = myclient["student"]
# mydb 即选择的数据库的实例化对象,使用此库对象选择要操作的表 "user"
mycollection = mydb["user"]
# mycollection 即选择的数据表的实例化对象

x = mycollection.insert_many([{'id': 1, 'name': '嗨', 'age': 100}, {'id': 2, 'name': 'man', 'age': 200}])
print(x.inserted_ids)

for i in mycollection.find():
    print(i)

输出结果为:

[ObjectId('5f8576fae5b453fa5cbe98b8'), ObjectId('5f8576fae5b453fa5cbe98b9')]
{'_id': ObjectId('5f856c81878d502dbe841498'), 'name': 'aliang', 'age': 10}
{'_id': ObjectId('5f856ecc878d502dbe841499'), 'name': 'ahuang', 'age': 10}
{'_id': ObjectId('5f8572fcc9ed7fc1e0eab03f'), 'name': 'huayignxiong', 'age': 180}
{'_id': ObjectId('5f85740edca4eb3a6e781c29'), 'name': '狗哥', 'age': 0}
{'_id': ObjectId('5f857538711bf2a6c03f2a9e'), 'name': '天行', 'age': 10}
{'_id': ObjectId('5f857538711bf2a6c03f2a9f'), 'name': '阿良', 'age': 20}
{'_id': ObjectId('5f8576fae5b453fa5cbe98b8'), 'id': 1, 'name': '嗨', 'age': 100}
{'_id': ObjectId('5f8576fae5b453fa5cbe98b9'), 'id': 2, 'name': 'man', 'age': 200}

MongoDB集群搭建之Replica-Set方式

mongodb集群搭建有三种方式。
1、Master-Slave模式
2、Replica-Set方式
3、Sharding方式
其中,第一种方式基本没什么意义,官方也不推荐这种方式搭建。另外两种分别就是副本集和分片的方式。今天介绍副本集的方式搭建mongodb高可用集群。	
副本集的方式也很容易理解,这里需要一个主节点,一个备节点,如果主节点发生故障,那么会启用备节点,当主节点修复之后,主节点再次恢复为主节点,备节点不再是主节点的角色。副本集的方式还需要一个角色,那就是仲裁节点,它不存储数据,他的作用就是当主节点出现故障,选举出备节点作为主节点,继续保证集群可用。客户端连接时只连接主节点或者备节点,不用连接仲裁节点。
修改配置文件
master.conf
dbpath=/data/mongodb/master
logpath=/data/mongodb/master.log
pidfilepath=/data/mongodb/master.pid
directoryperdb=true
logappend=true
replSet=testrs
bind_ip=192.168.61.21
port=27017
oplogSize=10000
fork=true
noprealloc=true
slave.conf
dbpath=/data/mongodb/slave
logpath=/data/mongodb/slave.log
pidfilepath=/data/mongodb/slave.pid
directoryperdb=true
logappend=true
replSet=testrs
bind_ip=192.168.61.22
port=27017
oplogSize=10000
fork=true
noprealloc=true
arbiter.conf
dbpath=/data/mongodb/arbiter
logpath=/data/mongodb/arbiter.log
pidfilepath=/data/mongodb/arbiter.pid
directoryperdb=true
logappend=true
replSet=testrs
bind_ip=192.168.61.23
port=27017
oplogSize=10000
fork=true
noprealloc=true
启动节点,保证每个节点上存储数据的文件夹都存在/data/mongodb/master,/data/mongodb/slave,/data/mongodb/arbiter,然后依次启动每个节点。
mongod -f master.conf
mongod -f slave.conf
mongod -f arbiter.conf
创建集群
[root@linux-node1 mongodb]# mongo 192.168.61.21:27017
MongoDB shell version: 3.2.9
connecting to: 192.168.61.21:27017/test
Server has startup warnings: 
2017-11-16T22:44:06.170+0800 I CONTROL  [initandlisten] ** WARNING: You are running this process as the root user, which is not recommended.
2017-11-16T22:44:06.170+0800 I CONTROL  [initandlisten] 
2017-11-16T22:44:06.171+0800 I CONTROL  [initandlisten] 
2017-11-16T22:44:06.171+0800 I CONTROL  [initandlisten] ** WARNING: /sys/kernel/mm/transparent_hugepage/enabled is 'always'.
2017-11-16T22:44:06.171+0800 I CONTROL  [initandlisten] **        We suggest setting it to 'never'
2017-11-16T22:44:06.171+0800 I CONTROL  [initandlisten] 
2017-11-16T22:44:06.171+0800 I CONTROL  [initandlisten] ** WARNING: /sys/kernel/mm/transparent_hugepage/defrag is 'always'.
2017-11-16T22:44:06.171+0800 I CONTROL  [initandlisten] **        We suggest setting it to 'never'
2017-11-16T22:44:06.171+0800 I CONTROL  [initandlisten] 
> use admin
switched to db admin
> cfg={_id:"testrs",members:[{_id:0,host:"192.168.61.21:27017",priority:2},{_id:1,host:"192.168.61.22:27017",priority:1},{_id:2,host:"192.168.61.23:27017",arbiterOnly:true}]};
{
        "_id" : "testrs",
        "members" : [
                {
                        "_id" : 0,
                        "host" : "192.168.61.21:27017",
                        "priority" : 2
                },
                {
                        "_id" : 1,
                        "host" : "192.168.61.22:27017",
                        "priority" : 1
                },
                {
                        "_id" : 2,
                        "host" : "192.168.61.23:27017",
                        "arbiterOnly" : true
                }
        ]
}
> rs.initiate(cfg)
{ "ok" : 1 }
查看集群状态
testrs:OTHER> rs.status()
{
        "set" : "testrs",
        "date" : ISODate("2017-11-16T14:51:33.788Z"),
        "myState" : 1,
        "term" : NumberLong(1),
        "heartbeatIntervalMillis" : NumberLong(2000),
        "members" : [
                {
                        "_id" : 0,
                        "name" : "192.168.61.21:27017",
                        "health" : 1,
                        "state" : 1,
                        "stateStr" : "PRIMARY",
                        "uptime" : 447,
                        "optime" : {
                                "ts" : Timestamp(1510843888, 2),
                                "t" : NumberLong(1)
                        },
                        "optimeDate" : ISODate("2017-11-16T14:51:28Z"),
                        "infoMessage" : "could not find member to sync from",
                        "electionTime" : Timestamp(1510843888, 1),
                        "electionDate" : ISODate("2017-11-16T14:51:28Z"),
                        "configVersion" : 1,
                        "self" : true
                },
                {
                        "_id" : 1,
                        "name" : "192.168.61.22:27017",
                        "health" : 1,
                        "state" : 2,
                        "stateStr" : "SECONDARY",
                        "uptime" : 15,
                        "optime" : {
                                "ts" : Timestamp(1510843877, 1),
                                "t" : NumberLong(-1)
                        },
                        "optimeDate" : ISODate("2017-11-16T14:51:17Z"),
                        "lastHeartbeat" : ISODate("2017-11-16T14:51:32.718Z"),
                        "lastHeartbeatRecv" : ISODate("2017-11-16T14:51:31.944Z"),
                        "pingMs" : NumberLong(0),
                        "configVersion" : 1
                },
                {
                        "_id" : 2,
                        "name" : "192.168.61.23:27017",
                        "health" : 1,
                        "state" : 7,
                        "stateStr" : "ARBITER",
                        "uptime" : 15,
                        "lastHeartbeat" : ISODate("2017-11-16T14:51:32.718Z"),
                        "lastHeartbeatRecv" : ISODate("2017-11-16T14:51:29.987Z"),
                        "pingMs" : NumberLong(1),
                        "configVersion" : 1
                }
        ],
        "ok" : 1
}

数据测试:

主节点测试

img

从节点连接,查询数据库出错:not master and slaveOk=false,这个错误是正常的,备节点不允许读操作。	
 show dbs
2017-11-16T23:10:05.041+0800 E QUERY    [thread1] Error: listDatabases failed:{ "ok" : 0, "errmsg" : "not master and slaveOk=false", "code" : 13435 } 

可以利用rs.slaveOk()命令来解决这个问题,但是在集群环境中没有必要这么做。

故障测试:

[root@linux-node1 mongodb]# ps -ef |grep mongod
root      24676      1  1 22:44 ?        00:00:21 mongod -f master.conf
root      24856  11281  0 23:18 pts/0    00:00:00 grep --color=auto mongod
[root@linux-node1 mongodb]# kill -9 24676
主节点挂掉:在备节点上查看集群状态,一会会发现备节点成为主节点:primary。
testrs:SECONDARY> rs.status()
{
        "set" : "testrs",
        "date" : ISODate("2017-11-16T15:18:56.862Z"),
        "myState" : 1,
        "term" : NumberLong(2),
        "heartbeatIntervalMillis" : NumberLong(2000),
        "members" : [
                {
                        "_id" : 0,
                        "name" : "192.168.61.21:27017",
                        "health" : 0,
                        "state" : 8,
                        "stateStr" : "(not reachable/healthy)",
                        "uptime" : 0,
                        "optime" : {
                                "ts" : Timestamp(0, 0),
                                "t" : NumberLong(-1)
                        },
                        "optimeDate" : ISODate("1970-01-01T00:00:00Z"),
                        "lastHeartbeat" : ISODate("2017-11-16T15:18:55.651Z"),
                        "lastHeartbeatRecv" : ISODate("2017-11-16T15:18:31.988Z"),
                        "pingMs" : NumberLong(1),
                        "lastHeartbeatMessage" : "Connection refused",
                        "configVersion" : -1
                },
                {
                        "_id" : 1,
                        "name" : "192.168.61.22:27017",
                        "health" : 1,
                        "state" : 1,
                        "stateStr" : "PRIMARY",
                        "uptime" : 1986,
                        "optime" : {
                                "ts" : Timestamp(1510845524, 1),
                                "t" : NumberLong(2)
                        },
                        "optimeDate" : ISODate("2017-11-16T15:18:44Z"),
                        "infoMessage" : "could not find member to sync from",
                        "electionTime" : Timestamp(1510845523, 1),
                        "electionDate" : ISODate("2017-11-16T15:18:43Z"),
                        "configVersion" : 1,
                        "self" : true
                },
                {
                        "_id" : 2,
                        "name" : "192.168.61.23:27017",
                        "health" : 1,
                        "state" : 7,
                        "stateStr" : "ARBITER",
                        "uptime" : 1655,
                        "lastHeartbeat" : ISODate("2017-11-16T15:18:55.632Z"),
                        "lastHeartbeatRecv" : ISODate("2017-11-16T15:18:51.951Z"),
                        "pingMs" : NumberLong(1),
                        "configVersion" : 1
                }
        ],
        "ok" : 1
}
这里如果再次将挂掉的master节点启动,那么一会master节点会变为primary主节点,而不是由slave备节点继续充当主节点
posted @ 2020-10-13 17:48  嗨,阿良  阅读(252)  评论(0编辑  收藏  举报