MongoDB是一个基于分布式文件存储的数据库。在高负载的情况下,添加更多的节点,可以保证服务器性能。由C++语言编写,旨在为web应用提供可扩展的高性能数据存储解决方案。
MongoDB是一个介于关系型和非关系数据库之间的产品,是非关系数据库当中功能最丰富、最像关系数据库的。MongoDB 将数据存储为一个文档,数据结构由键值(key=>value)对组成。MongoDB 文档类似于 JSON 对象。字段值可以包含其他文档,数组及文档数组。

事务在英文中是transaction,和现实世界中交易很类似,有如下四个特性:
A(Atomicity)原子性:原子性很容易理解,也就是说事务里的所有操作要么全部做完,要么都不做,事务成功的条件是事务里的所有操作都成功,只要有一个操作失败,整个事务就失败,需要回滚。比如银行转账,从A账户转100元至B账户,分为两个步骤:1)从A账户取100元;2)存入100元至B账户。这两步要么一起完成,要么一起不完成,如果只完成第一步,第二步失败,钱会莫名其妙少了100元。
C(Consistency)一致性:一致性也比较容易理解,也就是说数据库要一直处于一致的状态,事务的运行不会改变数据库原本的一致性约束。例如现有完整性约束a+b=10,如果一个事务改变了a,那么必须得改变b,使得事务结束后依然满足a+b=10,否则事务失败。
I(Isolation)独立性:所谓的独立性是指并发的事务之间不会互相影响,如果一个事务要访问的数据正在被另外一个事务修改,只要另外一个事务未提交,它所访问的数据就不受未提交事务的影响。比如现在有个交易是从A账户转100元至B账户,在这个交易还未完成的情况下,如果此时B查询自己的账户,是看不到新增加的100元的。
D(Durability)持久性:持久性是指一旦事务提交后,它所做的修改将会永久的保存在数据库上,即使出现宕机也不会丢失。
分布式系统(distributed system)由多台计算机和通信的软件组件通过计算机网络连接(本地网络或广域网)组成。分布式系统是建立在网络之上的软件系统。正是因为软件的特性,所以分布式系统具有高度的内聚性和透明性。
因此,网络和分布式系统之间的区别更多的在于高层软件(特别是操作系统),而不是硬件。分布式系统可以应用在不同的平台上如:Pc、工作站、局域网和广域网上等。
优点:
缺点:
NoSQL=Not Only SQL,即不仅仅是SQL。指的是菲关系型数据库,是对不同于传统的关系型数据库的统称,用于超大规模数据的存储,用户的个人信息,社交网络,地理位置,用户生成的数据和用户操作日志已经成倍的增加。我们如果要对这些用户数据进行挖掘,那SQL数据库已经不适合这些应用了, 这些类型的数据存储不需要固定的模式,无需多余操作就可以横向发展。
RDBMS(Relational Database Management System关系型数据库管理系统) :
NoSQL:
对于分布式计算系统来说,不可能同时满足以下三点,最对只能同时较好的满足两个:
根据 CAP 原理将 NoSQL 数据库分成了满足 CA 原则、满足 CP 原则和满足 AP 原则三 大类:
优点:
缺点:
BASE是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。 | 
第一步:创建数据目录,MongoDB将数据目录存储在 db 目录下。但是这个数据目录不会主动创建,我们在安装完成后需要创建它。
开启一个cmd,启动服务端,执行以下命令:
D:\>D:\ruanjian\bin\mongod --dbpath D:\ruanjian\DATA\db再启动一个cmd,启动客户端,执行以下命令:
D:\>D:\ruanjian\bin\mongo.exe可视化界面连接
Hostname:127.0.0.1
Port:27017
> 2+2
4
> db
test
> db.learn.insert({x:10})
WriteResult({ "nInserted" : 1 })
> db.learn.find()
{ "_id" : ObjectId("5da6559ec3d981f3a5500451"), "x" : 10 }
# 第一个命令将数字 10 插入到 learn 集合的 x 字段中。MongoDB Compass是MongoDB官网提供的一个集创建数据库、管理集合和文档、运行临时查询、评估和优化查询、性能图表、构建地理查询等功能为一体的MongoDB可视化管理工具。
创建数据库——连接MongDB数据库后,可以点击create database创建一个数据库。我这里创建了mongo_1数据库的同时也创建collection_1集合。
索引——在Indexes页签可以为集合创建索引及查看该集合下的索引详细信息。
| SQL术语/概念 | MongoDB术语/概念 | 解释/说明 | 
|---|---|---|
| database | database | 数据库 | 
| table | collection | 数据库表/集合 | 
| row | document | 数据记录行/文档 | 
| column | field | 数据字段/域 | 
| index | index | 索引 | 
| table joins | 表连接,MongoDB不支持 | |
| primary key | primary key | 主键,MongoDB自动将_id字段设置为主键 | 

一个MongoDB中可以建立多个数据库,默认数据库为"db",该数据库存储在data目录中,MongoDB的单个实例可以容纳多个独立的数据库,每一个都有自己的集合和权限,不同的数据库也放置在不同的文件中。
"show dbs"命令可以显示所有数据的列表
> show dbs
admin    0.000GB
config   0.000GB
local    0.000GB
mongo_1  0.000GB
test     0.000GB"db"命令可显示当前数据库对象或集合
> db
test"use"命令可连接到一个指定的数据库
> use local
switched to db local数据库命名规范:
特殊作用数据库:
文档是一组键值对(key-value)即BSON。MongoDB的文档不需要设置相同的字段,并且相同的字段不需要相同的数据类型,这与关系型数据库有很大的区别,也是MongoDB非常突出的特点。如:{"site":"www.baidu.com","name":"百度"}
| RDBMS | MongoDB | 
|---|---|
| 数据库 | 数据库 | 
| 表格 | 集合 | 
| 行 | 文档 | 
| 列 | 字段 | 
| 表联合 | 嵌入文档 | 
| 主键 | 主键 (MongoDB 提供了 key 为 _id ) | 
| 服务端 | 客户端 | 
|---|---|
| mongod | mongo | 
| mysqld | mysql | 
需要注意的是:
文档键命名规范:
集合就是MongoDB文档组,类似于RDBMS中的表格。集合存在于数据库中,集合没有固定的结构,意味着你在对集合插入不同格式和类型的数据,但通常情况下我们插入集合的数据都会有一定的关联性。
{"site":"www.baidu.com"}
{"site":"www.jd.com","name":"京东"}
{"site":"www.google.com","name":"谷歌","num":5}当第一个文档插入时,集合就会被创建。
合法的集合名:
capped collections就是固定大小的collection。由于 Capped collection 是按照文档的插入顺序而不是使用索引确定插入位置,这样的话可以提高增添数据的效率。MongoDB 的操作日志文件 oplog.rs 就是利用 Capped Collection 来实现的。
数据库的信息是存储在集合中,使用系统的命名空间:
| 集合命名空间 | 描述 | 
|---|---|
| dbname.system.namespaces | 列出所有名字空间。 | 
| dbname.system.indexes | 列出所有索引。 | 
| dbname.system.profile | 包含数据库概要(profile)信息。 | 
| dbname.system.users | 列出所有可访问数据库的用户。 | 
| dbname.local.sources | 包含复制对端(slave)的服务器信息和状态。 | 
| 数据类型 | 描述 | 
|---|---|
| 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 | 正则表达式类型。用于存储正则表达式。 | 
语法格式:use 数据库名,若不存在则创建,否则切换到指定数据库
> use db1 # 创建数据库
switched to db db1
> show dbs # 查看所有数据库
admin    0.000GB
config   0.000GB
local    0.000GB
mongo_1  0.000GB
test     0.000GB
# 在 MongoDB 中,集合只有在内容插入后才会创建! 就是说,创建集合(数据表)后要再插入一个文档(记录),集合才会真正创建。
> db.db1.insert({"name":"allen"}) #插入数据
WriteResult({ "nInserted" : 1 })
> show dbs
admin    0.000GB
config   0.000GB
db1      0.000GB
local    0.000GB
mongo_1  0.000GB
test     0.000GB
# MongoDB中默认数据库为test,若没创建新数据库,集合将存放在test数据库中语法格式:db.dropDatabase(),删除当前数据库,默认为test。以下为正常操作:
> use db1 #先切换
switched to db db1
> db.dropDatabase() #再删除
{ "dropped" : "db1", "ok" : 1 }
> show dbs
admin    0.000GB
config   0.000GB
local    0.000GB
mongo_1  0.000GB
test     0.000GB语法格式:db.createCollection(name,options),其中name:要创建的集合名称;options:可选参数,指定有关内存大小及索引的选项。参数如下:
| 字段 | 类型 | 描述 | 
|---|---|---|
| capped | 布尔 | (可选)如果为 true,则创建固定集合。固定集合是指有着固定大小的集合,当达到最大值时,它会自动覆盖最早的文档。 当该值为 true 时,必须指定 size 参数。 | 
| autoIndexId | 布尔 | (可选)如为 true,自动在 _id 字段创建索引。默认为 false。 | 
| size | 数值 | (可选)为固定集合指定一个最大值,以千字节计(KB)。 如果 capped 为 true,也需要指定该字段。 | 
| max | 数值 | (可选)指定固定集合中包含文档的最大数量。 | 
在插入文档时,MongoDB首先检查固定集合的size字段,然后检查max字段。
> use db1
switched to db db1
> db.createCollection("name")  #创建集合
{ "ok" : 1 }
> show collections #查看已有集合
name
# 创建固定集合 mycol,整个集合空间大小 6142800 KB, 文档最大个数为 10000 个。
> db.createCollection("mycol", { capped : true, autoIndexId : true, size : 6142800, max : 10000 } )
{ "ok" : 1 }语法格式:db.collection.drop(),若成功删除选定集合,返回true,否则返回false。
> use db1
switched to db db1
> show collections
mycol
name
> db.mycol.drop()
true
> show collections
name文档的数据结构和json基本一样,所有存储在集合的数据都是BSON格式,BSON是一种类似json的二进制形式的存储格式,是Binary JSON的简称。
# 将数据存储在 MongoDB 的 db1 数据库 的 col 集合中
>db.col.insert({title:"mongo教程",
              description:"一种NoSQL数据库",
              by:"互联网",
              url:"http://www.baidu.com",
              tags:["mongodb","database","NoSQL"],
              likes:100})
 WriteResult({ "nInserted" : 1 })
 > db.col.find() #查看已插入文档
 { "_id" : ObjectId("5da6dac77539bd49bdb9ab64"), 
     "title" : "mongo教程", 
     "description" : "一种NoSQL数据库", 
     "by" : "互联网", 
     "url" : "http://www.baidu.com", 
     "tags" : [ "mongodb", "database", "NoSQL" ], 
     "likes" : 100 }
# 如果将数据定义一个变量
> document=({title:"mongo教程",
...               description:"一种NoSQL数据库",
...               by:"互联网",
...               url:"http://www.baidu.com",
...               tags:["mongodb","database","NoSQL"],
...               likes:100});
# 执行后显示结果如下:
{
        "title" : "mongo教程",
        "description" : "一种NoSQL数据库",
        "by" : "互联网",
        "url" : "http://www.baidu.com",
        "tags" : [
                "mongodb",
                "database",
                "NoSQL"
        ],
        "likes" : 100
}
# 执行插入操作
> db.col.insert(document)
WriteResult({ "nInserted" : 1 })
# 插入文档你也可以使用 db.col.save(document) 命令。如果不指定 _id 字段 save() 方法类似于 insert() 方法。如果指定 _id 字段,则会更新该 _id 的数据。uodate()方法用于更新已存在的文档。
> db.col.update({"title":"mongo教程"},{$set:{"title":"mongo"}})
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 }) #输出信息
> db.col.find().pretty() #两种对比
{
        "_id" : ObjectId("5da6dac77539bd49bdb9ab64"),
        "title" : "mongo",
        "description" : "一种NoSQL数据库",
        "by" : "互联网",
        "url" : "http://www.baidu.com",
        "tags" : [
                "mongodb",
                "database",
                "NoSQL"
        ],
        "likes" : 100
}
{
        "_id" : ObjectId("5da6dcc87539bd49bdb9ab65"),
        "title" : "mongo教程",
        "description" : "一种NoSQL数据库",
        "by" : "互联网",
        "url" : "http://www.baidu.com",
        "tags" : [
                "mongodb",
                "database",
                "NoSQL"
        ],
        "likes" : 100
}
# 以上语句只会修改第一条发现的文档,如果你要修改多条相同的文档,则需要设置 multi 参数为 true。
>db.col.update({'title':'mongo教程'},{$set:{'title':'mongo'}},{multi:true})save()方法通过传入的文档来替换已有文档
>db.col.save({title:"mongo教程",
              description:"一种NoSQL数据库",
              by:"互联网",
              url:"http://www.baidu.com",
              tags:["mongodb","database"],
              likes:110})
>db.col.find().pretty()
{
        "_id" : ObjectId("5da6dfdb7539bd49bdb9ab66")
        "title" : "mongo教程",
        "description" : "一种NoSQL数据库",
        "by" : "互联网",
        "url" : "http://www.baidu.com",
        "tags" : [
                "mongodb",
                "database"
        ],
        "likes" : 110
}在执行remove()函数前先执行find()命令来判断执行的条件是否正确,这是一个比较好的习惯。
> db.col.find()
{ "_id" : ObjectId("5da6dac77539bd49bdb9ab64"), "title" : "mongo", "description": "一种NoSQL数据库", "by" : "互联网", "url" :"http://www.baidu.com", "tags" :[ "mongodb", "database", "NoSQL" ], "likes" : 100 }
{ "_id" : ObjectId("5da6dcc87539bd49bdb9ab65"), "title" : "mongo教程", "description" : "一种NoSQL数据库", "by" : "互联网", "url" : "http://www.baidu.com", "tags" : [ "mongodb", "database", "NoSQL" ], "likes" : 100 }
{ "_id" : ObjectId("5da6dfdb7539bd49bdb9ab66"), "title" : "mongo教程", "description" : "一种NoSQL数据库", "by" : "互联网", "url" : "http://www.baidu.com", "tags" : [ "mongodb", "database" ], "likes" : 110 }
> db.col.remove({"title":"mongo"}) #删除了一条数据
WriteResult({ "nRemoved" : 1 })
> db.col.find()
{ "_id" : ObjectId("5da6dcc87539bd49bdb9ab65"), "title" : "mongo教程", "description" : "一种NoSQL数据库", "by" : "互联网", "url" : "http://www.baidu.com", "tags" : [ "mongodb", "database", "NoSQL" ], "likes" : 100 }
{ "_id" : ObjectId("5da6dfdb7539bd49bdb9ab66"), "title" : "mongo教程", "description" : "一种NoSQL数据库", "by" : "互联网", "url" :"http://www.baidu.com", "tags" : [ "mongodb", "database" ], "likes" : 110 }
# 删除所有数据
>db.col.remove({})
>db.col.find()# 如果你需要以易读的方式来读取数据,可以使用 pretty() 方法
>db.col.find().pretty()
# pretty() 方法以格式化的方式来显示所有文档。
> db.col.find()
{ "_id" : ObjectId("5da6dcc87539bd49bdb9ab65"), "title" : "mongo教程", "description" : "一种NoSQL数据库", "by" : "互联网", "url" : "http://www.baidu.com", "tags" : [ "mongodb", "database", "NoSQL" ], "likes" : 100 }
{ "_id" : ObjectId("5da6dfdb7539bd49bdb9ab66"), "title" : "mongo教程", "description" : "一种NoSQL数据库", "by" : "互联网", "url" : "http://www.baidu.com", "tags" : [ "mongodb", "database" ], "likes" : 110 }| 操作 | 格式 | 范例 | 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 | 
# 使用<和>查询
> db.col.find({likes:{$lt:105,$gt:90}}).pretty()
{
        "_id" : ObjectId("5da6dcc87539bd49bdb9ab65"),
        "title" : "mongo教程",
        "description" : "一种NoSQL数据库",
        "by" : "互联网",
        "url" : "http://www.baidu.com",
        "tags" : [
                "mongodb",
                "database",
                "NoSQL"
        ],
        "likes" : 100MongoDB 的 find() 方法可以传入多个键(key),每个键(key)以逗号隔开,即常规 SQL 的 AND 条件。语法格式如下:
>db.col.find({key1:value1, key2:value2}).pretty()db.col.find({"by":"互联网","title":"mongo教程"}).pretty() #两条语句全部查出来MongoDB OR 条件语句使用了关键字 $or,语法格式如下:
>db.col.find(
   {
      $or: [
         {key1: value1}, {key2:value2}
      ]
   }
).pretty()db.col.find({$or:[{"likes":100},{"likes":110}]}).pretty() #两条语句全部查出来类似常规 SQL 语句为: where likes>50 AND (by = ‘互联网‘ OR title = ‘mongodb 教程‘)
db.col.find({"likes":{$gt:50},$or:[{"by":"互联网"},{"title":"mongo教程"}]}).pretty() #两条语句全部查出来$type操作符是基于BSON类型来检索集合中匹配的数据类型,并返回结果。
| 类型 | 数字 | 备注 | 
|---|---|---|
| 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 | 
# 如果想获取 "col" 集合中 title 为 String 的数据,你可以使用以下命令:
>db.col.find({"title":{$type:2}})
>db.col.find({"title":{$type:"string"}})如果你需要在MongoDB中读取指定数量的数据记录,可以使用MongoDB的Limit方法,limit()方法接受一个数字参数,该参数指定从MongoDB中读取的记录条数。
db.col.find().limit(1).pretty() #只显示一条数据我们除了可以使用limit()方法来读取指定数量的数据外,还可以使用skip()方法来跳过指定数量的数据,skip方法同样接受一个数字参数作为跳过的记录条数。
db.col.find().limit(1).skip(1).pretty() #只显示一条数据,skip()方法默认参数为 0 。在 MongoDB 中使用 sort() 方法对数据进行排序,sort() 方法可以通过参数指定排序的字段,并使用 1 和 -1 来指定排序的方式,其中 1 为升序排列,而 -1 是用于降序排列。
skip(), limilt(), sort()三个放在一起执行的时候,执行的顺序是先 sort(), 然后是 skip(),最后是显示的 limit()。
db.col.find().sort({"likes":-1}).pretty() #col 集合中的数据按字段 likes 的降序排列索引通常能够极大的提高查询的效率,如果没有索引,MongoDB在读取数据时必须扫描集合中的每个文件并选取那些符合查询条件的记录。
这种扫描全集合的查询效率是非常低的,特别在处理大量的数据时,查询可以要花费几十秒甚至几分钟,这对网站的性能是非常致命的。
索引是特殊的数据结构,索引存储在一个易于遍历读取的数据集合中,索引是对数据库表中一列或多列的值进行排序的一种结构
MongoDB使用createIndex()方法来创建索引,语法格式如下:
>db.collection.createIndex(keys, options)
# 语法中 Key 值为你要创建的索引字段,1 为指定按升序创建索引,如果你想按降序来创建索引指定为 -1 即可。
>db.col.createIndex({"title":1})
# createIndex()方法可设置使用多个字段创建索引(关系型数据库中称作复合索引)
>db.col.createIndex({"title":1,"description":-1})MongoDB中的聚合(aggregate)主要用于处理数据(诸如统计平均值、求和等),并返回计算后的数据结果。有点类似sql语句中的"count(*)"
| 表达式 | 描述 | 实例 | 
|---|---|---|
| \(sum | 计算总和。 | db.mycol.aggregate([{\)group : {_id : "\(by_user", num_tutorial : {\)sum : "$likes"}}}]) | ||
| \(avg | 计算平均值 | db.mycol.aggregate([{\)group : {_id : "\(by_user", num_tutorial : {\)avg : "$likes"}}}]) | ||
| \(min | 获取集合中所有文档对应值得最小值。 | db.mycol.aggregate([{\)group : {_id : "\(by_user", num_tutorial : {\)min : "$likes"}}}]) | ||
| \(max | 获取集合中所有文档对应值得最大值。 | db.mycol.aggregate([{\)group : {_id : "\(by_user", num_tutorial : {\)max : "$likes"}}}]) | ||
| \(push | 在结果文档中插入值到一个数组中。 | db.mycol.aggregate([{\)group : {_id : "\(by_user", url : {\)push: "$url"}}}]) | ||
| \(addToSet | 在结果文档中插入值到一个数组中,但不创建副本。 | db.mycol.aggregate([{\)group : {_id : "\(by_user", url : {\)addToSet : "$url"}}}]) | ||
| \(first | 根据资源文档的排序获取第一个文档数据。 | db.mycol.aggregate([{\)group : {_id : "\(by_user", first_url : {\)first : "$url"}}}]) | ||
| \(last | 根据资源文档的排序获取最后一个文档数据 | db.mycol.aggregate([{\)group : {_id : "\(by_user", last_url : {\)last : "$url"}}}]) | 
# 先插入三条数据
>db.mycol.insert({
   title: 'MongoDB Overview', 
   description: 'MongoDB is no sql database',
   by_user: 'baidu.com',
   url: 'http://www.baidu.com',
   tags: ['mongodb', 'database', 'NoSQL'],
   likes: 100
})
>db.mycol.insert({
   title: 'NoSQL Overview', 
   description: 'No sql database is very fast',
   by_user: 'baidu.com',
   url: 'http://www.baidu.com',
   tags: ['mongodb', 'database', 'NoSQL'],
   likes: 10
})
>db.mycol.insert({
   title: 'Neo4j Overview', 
   description: 'Neo4j is no sql database',
   by_user: 'eo4j',
   url: 'http://www.eo4j.com',
   tags: ['neo4j', 'database', 'NoSQL'],
   likes: 750
})
# 查看数据是否插入
> db.mycol.find().pretty()
# 通过以上集合计算每个作者所写的文章数
> db.mycol.aggregate([{$group:{_id:"$by_user",num_tutorial:{$sum:1}}}])
{ "_id" : "eo4j", "num_tutorial" : 1 }
{ "_id" : "baidu.com", "num_tutorial" : 2 }
#以上语句类似SQL语句:select by_user, count(*) from mycol group by by_user,我们通过字段 by_user 字段对数据进行分组,并计算 by_user 字段相同值的总和。MongoDB的聚合管道将MongoDB文档在一个管道处理完毕后将结果传递给下一个管道处理。管道操作是可以重复的。
表达式:处理输入文档并输出。表达式是无状态的,只能用于计算当前聚合管道的文档,不能处理其它的文档。
这里我们介绍一下聚合框架中常用的几个操作:
$project:修改输入文档的结构。可以用来重命名、增加或删除域,也可以用于创建计算结果以及嵌套文档。
db.article.aggregate(
    { $project : {
        title : 1 ,
        author : 1 ,
    }}
 );
# 这样的话结果中就只还有_id,tilte和author三个字段了,默认情况下_id字段是被包含的,如果要想不包含_id话可以这样
db.article.aggregate(
    { $project : {
        _id : 0 ,
        title : 1 ,
        author : 1
    }});$match:用于过滤数据,只输出符合条件的文档。?match使用MongoDB的标准查询操作。
db.articles.aggregate( [
                 { $match : { score : { $gt : 70, $lte : 90 } } },
                  { $group: { _id: null, count: { $sum: 1 } } }] );
# $match用于获取分数大于70小于或等于90记录,然后将符合条件的记录送到下一阶段$group管道操作符进行处理。$limit:用来限制MongoDB聚合管道返回的文档数。
$skip:在聚合管道中跳过指定数量的文档,并返回余下的文档。
db.article.aggregate(
    { $skip : 5 });
# 经过$skip管道操作符处理后,前五个文档被"过滤"掉。$unwind:将文档中的某一个数组类型字段拆分成多条,每条包含数组中的一个值。
$group:将集合中的文档分组,可用于统计结果。
$sort:将输入文档排序后输出。
$geoNear:输出接近某一地理位置的有序文档。
mongodb的复制至少需要两个节点。其中一个是主节点,负责处理客户端请求,其余的都是从节点,负责复制主节点上的数据。
mongodb各个节点常见的搭配方式为:一主一从、一主多从。
主节点记录在其上的所有操作oplog,从节点定期轮询主节点获取这些操作,然后对自己的数据副本执行这些操作,从而保证从节点的数据与主节点一致。
MongoDB复制结构图如下所示:

以上结构图中,客户端从主节点读取数据,在客户端写入数据到主节点时, 主节点与从节点进行数据交互保障数据的一致性。
在Mongodb里面存在另一种集群,就是分片技术,可以满足MongoDB数据量大量增长的需求。
当MongoDB存储海量的数据时,一台机器可能不足以存储数据,也可能不足以提供可接受的读写吞吐量。这时,我们就可以通过在多台机器上分割数据,使得数据库系统能存储和处理更多的数据。
下图展示了在MongoDB中使用分片集群结构分布:

上图中主要有如下所述三个主要组件:
Shard:
用于存储实际的数据块,实际生产环境中一个shard server角色可由几台机器组个一个replica set承担,防止主机单点故障
Config Server:
mongod实例,存储了整个 ClusterMetadata,其中包括 chunk信息。
Query Routers:
前端路由,客户端由此接入,且让整个集群看上去像单一数据库,前端应用可以透明使用。
在Mongodb中我们使用mongodump命令来备份MongoDB数据。该命令可以导出所有数据到指定目录中。mongodump命令可以通过参数指定导出的数据量级转存的服务器。
>mongodump -h dbhost -d dbname -o dbdirectory
# -h:MongDB所在服务器地址,例如:127.0.0.1,当然也可以指定端口号:127.0.0.1:27017
# -d:需要备份的数据库实例,例如:test
# -o:备份的数据存放位置,例如:c:\data\dump,当然该目录需要提前建立,在备份完成后,系统自动在dump目录下建立一个test目录,这个目录里面存放该数据库实例的备份数据
# 在本地使用 27017 启动你的mongod服务。打开命令提示符窗口,进入MongoDB安装目录的bin目录输入命令mongodump,执行以上命令后,客户端会连接到ip为 127.0.0.1 端口号为 27017 的MongoDB服务上,并备份所有数据到 bin/dump/ 目录中。mongodb使用 mongorestore 命令来恢复备份的数据。
>mongorestore -h <hostname><:port> -d dbname <path>
# --host <:port>, -h <:port>:MongoDB所在服务器地址,默认为: localhost:27017
# --db , -d :需要恢复的数据库实例,例如:test,当然这个名称也可以和备份时候的不一样,比如test2
# --drop:恢复的时候,先删除当前数据,然后恢复备份的数据。就是说,恢复后,备份后添加修改的数据都会被删除,慎用。
# <path>:mongorestore 最后的一个参数,设置备份数据所在位置,例如:c:\data\dump\test。你不能同时指定 <path> 和 --dir 选项,--dir也可以设置备份目录。
# --dir:指定备份的目录,你不能同时指定 <path> 和 --dir 选项。在你已经安装部署并允许MongoDB服务后,你必须要了解MongoDB的运行情况,并查看MongoDB的性能。这样在大流量得情况下可以很好的应对并保证MongoDB正常运作。MongoDB中提供了mongostat 和 mongotop 两个命令来监控MongoDB的运行情况。
mongostat是mongodb自带的状态检测工具,在命令行下使用。它会间隔固定时间获取mongodb的当前运行状态,并输出。如果你发现数据库突然变慢或者有其他问题的话,你第一手的操作就考虑采用mongostat来查看mongo的状态。
启动你的Mongod服务,进入到你安装的MongoDB目录下的bin目录, 然后输入mongostat命令
mongotop也是mongodb下的一个内置工具,mongotop提供了一个方法,用来跟踪一个MongoDB的实例,查看哪些大量的时间花费在读取和写入数据。 mongotop提供每个集合的水平的统计数据。默认情况下,mongotop返回值的每一秒。
启动你的Mongod服务,进入到你安装的MongoDB目录下的bin目录, 然后输入mongotop命令
MongoDB的关系表示多个文档之间在逻辑上的相互关系。文件间可通过嵌入和引用来建立关系。
MongoDB中的关系可以是:
举例:一个用户可以有多个地址,故是一对多关系。
# 嵌入式关系
db.mytest.insert({
   "name": "Tom Hanks",
   "contact": "987654321",
   "dob": "01-01-1991",
   "address":[
       {
         "building": "22 A, Indiana Apt",
         "pincode": 123456,
         "city": "Los Angeles",
         "state": "California"
      },
       {
         "building": "170 A, Acropolis Apt",
         "pincode": 456789,
         "city": "Chicago",
         "state": "Illinois"
      }]})
# 以上数据保存在单一的文档中,可以比较容易的获取和维护数据。 你可以这样查询用户的地址
>db.mytest.findOne({"name":"Tom Hanks"},{"address":1})
# 缺点:如果用户和地址不断增加,数据量不断变大,会影响读写性能# 引用式关系:设计数据库时经常用到的方法,这种方法把用户数据文档和用户地址数据文档分开,通过引用文档的 id 字段来建立关系。
>db.address.insert({
         "building": "22 A, Indiana Apt",
         "pincode": 123456,
         "city": "Los Angeles",
         "state": "California"
      })
>db.address.insert({
         "building": "170 A, Acropolis Apt",
         "pincode": 456789,
         "city": "Chicago",
         "state": "Illinois"
      })
>db.address.find()
>db.users.insert({
   "name": "Tom Hanks",
   "contact": "987654321",
   "dob": "01-01-1991",
   "address_ids":[
       ObjectId("5da707c17539bd49bdb9ab6d"),
       ObjectId("5da707c97539bd49bdb9ab6e")]})
# 用户文档的 address_ids 字段包含用户地址的对象id(ObjectId)数组。我们可以读取这些用户地址的对象id(ObjectId)来获取用户的详细地址信息。这种方法需要两次查询,第一次查询用户地址的对象id(ObjectId),第二次通过查询的id获取用户的详细地址信息。
>var result=db.users.findOne({"name":"Tom Hanks"},{"address_ids":1})
>result
{
        "_id" : ObjectId("5da70b1a7539bd49bdb9ab71"),
        "address_ids" : [
                ObjectId("5da707c17539bd49bdb9ab6d"),
                ObjectId("5da707c97539bd49bdb9ab6e")
        ]
}
>var addresses=db.address.find({"_id":{"$in":result["address_ids"]}})
>addresses
{ "_id" : ObjectId("5da707c17539bd49bdb9ab6d"), "building" : "22 A, Indiana Apt", "pincode" : 123456, "city" : "Los Angeles", "state" : "California" }
{ "_id" : ObjectId("5da707c97539bd49bdb9ab6e"), "building" : "170 A, Acropolis Apt", "pincode" : 456789, "city" : "Chicago", "state" : "Illinois" }考虑这样的一个场景,我们在不同的集合中 (address_home, address_office, address_mailing, 等)存储不同的地址(住址,办公室地址,邮件地址等)。这样,我们在调用不同地址时,也需要指定集合,一个文档从多个集合引用文档,我们应该使用 DBRefs。
DBRefs的形式:
{$ref:,$id:,$db:}
# $ref:集合名称
# $id:引用的id
# $db:数据库名称,可选参数# 对用户数据使用DBRefs,字段address
{
   "_id":ObjectId("53402597d852426020000002"),
   "address": {
   "$ref": "address_home",
   "$id": ObjectId("534009e4d852427820000002"),
   "$db": "runoob"},
   "contact": "987654321",
   "dob": "01-01-1991",
   "name": "Tom Benzamin"
}
# 查找集合中指定id的用户地址信息
>var user = db.users.findOne({"name":"Tom Benzamin"})
>var dbRef = user.address
>db[dbRef.$ref].findOne({"_id":(dbRef.$id)})由于所有出现在查询中的字段是索引的一部分, MongoDB 无需在整个数据文档中检索匹配查询条件和返回使用相同索引的查询结果。因为索引存在于RAM中,从索引中获取数据比通过扫描文档读取数据要快得多。
>db.mytest02.insert({
   "contact": "987654321",
   "dob": "01-01-1991",
   "gender": "M",
   "name": "Tom Benzamin",
   "user_name": "tombenzamin"
})
# 在集合中创建联合索引,字段为gender和user_name
>db.mytest02.ensureIndex({gender:1,user_name:1})
{
        "createdCollectionAutomatically" : false,
        "numIndexesBefore" : 1,
        "numIndexesAfter" : 2,
        "ok" : 1
}
>db.mytest02.find({gender:"M"},{user_name:1,_id:0})
{ "user_name" : "tombenzamin" }
# 对于上述查询,MongoDB的不会去数据库文件中查找。相反,它会从索引中提取数据,这是非常快速的数据查询。由于我们的索引中不包括 _id 字段,_id在查询中会默认返回,我们可以在MongoDB的查询结果集中排除它。
# 下面的实例没有排除_id,查询就不会被覆盖:
>db.mytest02.find({gender:"M"},{user_name:1}) 
{ "_id" : ObjectId("5da7194d7539bd49bdb9ab72"), "user_name" : "tombenzamin" }最后,如果是以下的查询,不能使用覆盖索引查询:所有索引字段是一个数组,所有索引字段是一个子文档
MongoDB 查询分析可以确保我们所建立的索引是否有效,是查询语句性能分析的重要工具。MongoDB 查询分析常用函数有:explain() 和 hint()。
explain 操作提供了查询信息,使用索引及查询统计等。有利于我们对索引的优化。
>db.mytest02.find({gender:"M"},{user_name:1,_id:0}).explain()
{
   "cursor" : "BtreeCursor gender_1_user_name_1",
   "isMultiKey" : false,
   "n" : 1,
   "nscannedObjects" : 0,
   "nscanned" : 1,
   "nscannedObjectsAllPlans" : 0,
   "nscannedAllPlans" : 1,
   "scanAndOrder" : false,
   "indexOnly" : true,
   "nYields" : 0,
   "nChunkSkips" : 0,
   "millis" : 0,
   "indexBounds" : {
      "gender" : [
         [
            "M",
            "M"
         ]
      ],
      "user_name" : [
         [
            {
               "$minElement" : 1
            },
            {
               "$maxElement" : 1
            }
         ]
      ]
   }
}
# indexOnly: 字段为 true ,表示我们使用了索引。
# cursor:因为这个查询使用了索引,MongoDB 中索引存储在B树结构中,所以这是也使用了 BtreeCursor 类型的游标。如果没有使用索引,游标的类型是 BasicCursor。这个键还会给出你所使用的索引的名称,你通过这个名称可以查看当前数据库下的system.indexes集合(系统自动创建,由于存储索引信息,这个稍微会提到)来得到索引的详细信息。
# n:当前查询返回的文档数量
# nscanned/nscannedObjects:表明当前这次查询一共扫描了集合中多少个文档,我们的目的是,让这个数值和返回文档的数量越接近越好。
# millis:当前查询所需时间,毫秒数。
# indexBounds:当前查询具体使用的索引。虽然MongoDB查询优化器一般工作的很不错,但是也可以使用 hint 来强制 MongoDB 使用一个指定的索引。这种方法某些情形下会提升性能。 一个有索引的 collection 并且执行一个多字段的查询(一些字段已经索引了)。
如下查询实例指定了使用 gender 和 user_name 索引字段来查询:
>db.users.find({gender:"M"},{user_name:1,_id:0}).hint({gender:1,user_name:1})可以使用 explain() 函数来分析以上查询:
>db.users.find({gender:"M"},{user_name:1,_id:0}).hint({gender:1,user_name:1}).explain()mongodb不支持事务,所以,在你的项目中应用时,要注意这点。无论什么设计,都不要要求mongodb保证数据的完整性。但是mongodb提供了许多原子操作,比如文档的保存,修改,删除等,都是原子操作。所谓原子操作就是要么这个文档保存到Mongodb,要么没有保存到Mongodb,不会出现查询到的文档没有保存完整的情况。
# 例子:图书馆的书籍及结账信息。说明了在一个相同的文档中如何确保嵌入字段关联原子操作(update:更新)的字段是同步的。
book = {
          _id: 123456789,
          title: "MongoDB: The Definitive Guide",
          author: [ "Kristina Chodorow", "Mike Dirolf" ],
          published_date: ISODate("2010-09-24"),
          pages: 216,
          language: "English",
          publisher_id: "oreilly",
          available: 3,
          checkout: [ { by: "joe", date: ISODate("2012-10-15") } ]
        }
# 你可以使用 db.collection.findAndModify() 方法来判断书籍是否可结算并更新新的结算信息。在同一个文档中嵌入的 available 和 checkout 字段来确保这些字段是同步更新的:
db.books.findAndModify ( {
   query: {
            _id: 123456789,
            available: { $gt: 0 }
          },
   update: {
             $inc: { available: -1 },
             $push: { checkout: { by: "abc", date: new Date() } }
           }
} )用来指定一个键并更新键值,若键不存在并创建。
{ $set : { field : value } }用来删除一个键。
{ $unset : { field : 1} }$inc可以对文档的某个值为数字型(只能为满足要求的数字)的键进行增减的操作。
{ $inc : { field : value } }用法:
{ $push : { field : value } }把value追加到field里面去,field一定要是数组类型才行,如果field不存在,会新增一个数组类型加进去。
同$push,只是一次可以追加多个值到一个数组字段内。
{ $pushAll : { field : value_array } }从数组field内删除一个等于value值。
{ $pull : { field : _value } }增加一个值到数组内,而且只有当这个值不在数组内才增加。
删除数组的第一个或最后一个元素
{ $pop : { field : 1 } }修改字段名称
{ $rename : { old_field_name : new_field_name } }位操作,integer类型
{$bit : { field : {and : 5}}}> t.find() { "_id" : ObjectId("4b97e62bf1d8c7152c9ccb74"), "title" : "ABC", "comments" : [ { "by" : "joe", "votes" : 3 }, { "by" : "jane", "votes" : 7 } ] }
 
> t.update( {'comments.by':'joe'}, {$inc:{'comments.$.votes':1}}, false, true )
 
> t.find() { "_id" : ObjectId("4b97e62bf1d8c7152c9ccb74"), "title" : "ABC", "comments" : [ { "by" : "joe", "votes" : 4 }, { "by" : "jane", "votes" : 7 } ] }{
   "address": {
      "city": "Los Angeles",
      "state": "California",
      "pincode": "123"
   },
   "tags": [
      "music",
      "cricket",
      "blogs"
   ],
   "name": "Tom Benzamin"
}以上文档包含了 address 子文档和 tags 数组。
假设我们基于标签来检索用户,为此我们需要对集合中的数组 tags 建立索引。
在数组中创建索引,需要对数组中的每个字段依次建立索引。所以在我们为数组 tags 创建索引时,会为 music、cricket、blogs三个值建立单独的索引。
使用以下命令创建数组索引:
>db.users.ensureIndex({"tags":1})创建索引后,我们可以这样检索集合的 tags 字段:
>db.users.find({tags:"cricket"})为了验证我们使用使用了索引,可以使用 explain 命令:
>db.users.find({tags:"cricket"}).explain()以上命令执行结果中会显示 "cursor" : "BtreeCursor tags_1" ,则表示已经使用了索引。
假设我们需要通过city、state、pincode字段来检索文档,由于这些字段是子文档的字段,所以我们需要对子文档建立索引。
为子文档的三个字段创建索引,命令如下:
>db.users.ensureIndex({"address.city":1,"address.state":1,"address.pincode":1})一旦创建索引,我们可以使用子文档的字段来检索数据:
>db.users.find({"address.city":"Los Angeles"})   查询表达不一定遵循指定的索引的顺序,mongodb 会自动优化。所以上面创建的索引将支持以下查询:
>db.users.find({"address.state":"California","address.city":"Los Angeles"}) 同样支持以下查询:
>db.users.find({"address.city":"Los Angeles","address.state":"California","address.pincode":"123"})对象:
MongoClient对象:用于与MongoDB服务器建立连接
client=MongoClient('主机ip',端口)
DataBase对象:对应着MongoDB中的数据库
db=client.数据库名称
Collection对象:对应着MongoDB中的集合
col=db.集合名称
Cursor对象:查询方法find()返回的对象,用于进行多行数据的遍历,当调用集合对象的find()方法时,会返回Cursor对象,结合for...in...遍历cursor对象
主要方法:
insert_one:加入一条文档对象
insert_many:加入多条文档对象
find_one:查找一条文档对象
find:查找多条文档对象
update_one:更新一条文档对象
update_many:更新多条文档对象
delete_one:删除一条文档对象
delete_many:删除多条文档对象
插入方法:
insert_one() 传入一个字典,表示插入一个文档
insert_many() 传入一个列表,列表的元素为字典,插入多条文档
查询方法:
find_one()返回满足条件的文档集中第一条数据,类型为字典,如果没有查询结果返回None
方法find()返回满足条件的所有文档,类型为Cursor对象,可以使用for...in遍历,每项为字典对象,如果没有查询结果返一个空的Cursor对象
修改方法:
update_one()修改满足条件的文档集中的第一条文档
update_many()修改满足条件的文档集中的所有文档
注意:使用$set操作符修改特定属性的值,否则会修改整个文档
删除方法:
delete_one()删除满足条件的文档集中第一条文档
delete_many()删除满足条件的所有文档
注意:使用$set操作符修改特定属性的值,否则会修改整个文档插入数据
from pymongo import *
def data_insert():
    try:
        # 创建连接对象
        client = MongoClient(host="localhost", port=27017)
        # 获取数据库(有则使用,无则创建)
        db = client.school
        # 插入一条
        db.student.insert_one({"name": "tank", "age": 30})
    except Exception as e:
        print(e)
if __name__ == '__main__':
    data_insert()查询数据
from pymongo import *
def data_find():
    try:
        # 创建连接对象
        client = MongoClient(host="localhost", port=27017)
        # 获取数据库(有则使用,无则创建)
        db = client.school
        # 插入一条
        res = db.student.find_one({"name": "tank"})
        print(res)
    except Exception as e:
        print(e)
if __name__ == '__main__':
    data_find()修改数据
from pymongo import *
def data_update():
    try:
        # 创建连接对象
        client = MongoClient(host="localhost", port=27017)
        # 获取数据库(有则使用,无则创建)
        db = client.school
        # 修改数据
        db.student.update_one({"name": "tank"}, {"$set": {"name": "TANK"}})
    except Exception as e:
        print(e)
if __name__ == '__main__':
    data_update()删除数据
from pymongo import *
def data_delete():
    try:
        # 创建连接对象
        client = MongoClient(host="localhost", port=27017)
        # 获取数据库(有则使用,无则创建)
        db = client.school
        # 删除数据
        db.student.delete_one({"name": "allen"})
    except Exception as e:
        print(e)
if __name__ == '__main__':
    data_delete()原文:https://www.cnblogs.com/daizongqi/p/11688842.html