MongoDB:一次新的旅程

写在前面

之前,我是一个对数据库不“感冒”的人,但是偶然的机会,让我知道了MongoDB.一直在说要学,拖了好长时间才付诸行动(能行动已经不错了).

昨天抽空看了一下MongoDB的教程,发现有点“相见恨晚”的感觉.无论是bson存储,find()查询,还是_id,或者分片等等这些特性,都让我深深着迷.

好了,下面贴出我写的笔记,大部分代码已经跑过的.

[会有安装步骤吗?…当然没有,自行Google.]

mongodb
image-2241

MongoDB学习笔记

你也可以下载:mongoDB笔记PDF版

提示:字体加粗部分应当注意!

一、创建数据库

命令:
use DATABASE_NAME

创建一个数据库:use mydb
检查当前选择的数据库:db
检查数据库列表:show dbs
— 创建的数据库mydb列表中是不存在的.要显示的数据库,需要把它插入至少一个文件.
>db.movie.insert({“name”:”tutorials point”})
>show dbs
local 0.78125GB
mydb 0.23012GB
test 0.23012GB
— 在MongoDB默认数据库测试,如果没有创建任何数据库,然后集合将被存储在测试数据库.

删除数据库

命令:db.dropDatabase()
— 这将删除选定的数据库。如果还没有选择任何数据库,然后它会删除默认的’test’数据库.

示例:

首先,检查列表数据库通过使用命令 show dbs

>show dbs
local 0.78125GB
mydb 0.23012GB
test 0.23012GB
>
如果想删除新数据库 , 那么 dropDatabase() 命令如下:

>use mydb
switched to db mydb
>db.dropDatabase()
>{ “dropped” : “mydb”, “ok” : 1 }
>
现在检查的数据库列表

>show dbs
local 0.78125GB
test 0.23012GB

创建集合

命令语法:
db.createCollection(name,options)
— 在命令中,name是要创建的集合的名称.option是一个文件,用于指定配置的集合.

参数 类型 描述
Name String 要创建的集合名称
Options Document (可选)指定有关内存大小和索引选项

选项参数是可选的,所以只需要到指定的集合名称。以下是可以使用的选项列表:
字段 类型 描述
capped Boolean (可选)如果为true,则启用封顶集合。封顶集合是固定大小的集合,会自动覆盖最早的条目,当它达到其最大大小。如果指定true,则需要也指定尺寸参数。
autoIndexId Boolean (可选)如果为true,自动创建索引_id字段的默认值是false。
size number (可选)指定最大大小字节封顶集合。如果封顶如果是 true,那么你还需要指定这个字段。
max number (可选)指定封顶集合允许在文件的最大数量。

当插入文档,mongodb第一检查大小字段封顶集合,然后它会检查最大的字段中。

例子:

createCollection() 方法不使用选项的基本语法如下:

>use test
switched to db test
>db.createCollection(“mycollection”)
{ “ok” : 1 }
>
可以检查通过使用创建的集合命令 show collections

>show collections
mycollection
system.indexes
下面的例子显示了几个重要的选项 createCollection()方法的语法:

>db.createCollection(“mycol”, { capped : true, autoIndexID : true, size : 6142800, max : 10000 } )
{ “ok” : 1 }
>
在MongoDB中,不需要创建集合。当插入一些文件 MongoDB 自动创建的集合。

>db.yiibai.insert({“name” : “yiibai”})
>show collections
mycol
mycollection
system.indexes
yiibai
>

删除集合

命令语法:
db.COLLECTION_NAME.drop()

示例:
首先,检查可用的集合在mydb
>use mydb
switched to db mydb
>show collections
mycol
mycollection
system.indexes
yiibai
>
现在删除集合名称为 mycollection

>db.mycollection.drop()
true
>
再次检查到数据库中的集合列表

>show collections
mycol
system.indexes
yiibai
>
drop() 方法将返回 true,如果选择成功收集被丢弃,否则将返回 false

数据类型

MongoDB支持许多数据类型的列表下面给出:

String : 这是最常用的数据类型来存储数据。在MongoDB中的字符串必须是有效的UTF-8。

Integer : 这种类型是用来存储一个数值。整数可以是32位或64位,这取决于您的服务器。

Boolean : 此类型用于存储一个布尔值 (true/ false) 。

Double : 这种类型是用来存储浮点值。

Min/ Max keys : 这种类型被用来对BSON元素的最低和最高值比较。

Arrays : 使用此类型的数组或列表或多个值存储到一个键。

Timestamp : 时间戳。这可以方便记录时的文件已被修改或添加。

Object : 此数据类型用于嵌入式的文件。

Null : 这种类型是用来存储一个Null值。

Symbol : 此数据类型用于字符串相同,但它通常是保留给特定符号类型的语言使用。

Date : 此数据类型用于存储当前日期或时间的UNIX时间格式。可以指定自己的日期和时间,日期和年,月,日到创建对象。

Object ID : 此数据类型用于存储文档的ID。

Binary data : 此数据类型用于存储二进制数据。

Code : 此数据类型用于存储到文档中的JavaScript代码。

Regular expression : 此数据类型用于存储正则表达式

插入文档

命令:
insert()或save()方法.

语法:
insert()命令的基本语法如下:
db.COLLECTION_NAME.insert(document)

例子:

>db.mycol.insert({
_id: ObjectId(7df78ad8902c),
title: ‘MongoDB Overview’,
description: ‘MongoDB is no sql database’,
by: ‘tutorials point’,
url: ‘http://www.yiibai.com’,
tags: [‘mongodb’, ‘database’, ‘NoSQL’],
likes: 100
})
这里 mycol 是集合的名称,如前面的教程中创建。如果集合在数据库中不存在,那么MongoDB 将创建此集合,然后把它插入文档。

插入文档中,如果我们不指定_id参数,然后MongoDB 本文档分配一个独特的ObjectId。

_id 是12个字节的十六进制数,唯一一个集合中的每个文档。 12个字节被划分如下:

_id: ObjectId(4 bytes timestamp, 3 bytes machine id, 2 bytes process id, 3 bytes incrementer)
要插入单个查询的多个文档,可以传递一个数组 insert() 命令的文件。

示例:

db.post.insert([
… {
… title:’mongo db’,
… description:’mongodb is no sql database’,
… by:’tutorials point’
… },
… {
… title:’nosql database’,
… comments:[
… {
… user:’user1′,
… message:’My first comment’,
… dateCreated:new Date(2015,1,25,17,40),
… like:0
… }
… ]
… }
… ])

要插入文件,也可以使用 db.post.save(document)。 如果不指定_id在文档中,然后将其 save() 方法和 insert()方法工作一样。如果指定_id,它会替换整个数据文件,其中包含_id 指定save()方法。

查询文档

命令:
find()或者pretty()方法.

find()方法:

基本语法如下:
db.COLLECTION_NAME.find()
find()方法将在非结构化的方式显示所有的文件.

示例:
> db.mycol.find()
{ “_id” : ObjectId(“54c4b988eb3ef80c13dfeb29”), “title” : “MongoDB Overview”, “description” : “MongoDB is no sql database”, “by” : “tutorials point”, “url” : “http://www.baidu.com”, “tags” : [ “mongodb”, “database”, “NoSQL” ], “likes” : 100 }
>

pretty()方法:
结果显示在一个格式化的方式.可以使用pretty()方法.

基本语法如下:
db.myol.find().pretty()

例子:
–>
> db.mycol.find().pretty()
{
“_id” : ObjectId(“54c4b988eb3ef80c13dfeb29”),
“title” : “MongoDB Overview”,
“description” : “MongoDB is no sql database”,
“by” : “tutorials point”,
“url” : “http://www.baidu.com”,
“tags” : [
“mongodb”,
“database”,
“NoSQL”
],
“likes” : 100
}
>
除了find() 方法外,还有一个 findOne() 法,返回一个文件。

RDBMS Where子句和MongoDB等同语句
操作 语法 例子 RDBMS等同
Equality {:} db.mycol.find({“by”:”tutorialspoint”}}).pretty() where by = ‘tutorials point’
Less Than {:{$lt:}} db.mycol.find({“likes”:{$lt:50}}).pretty() where likes < 50
Less Than Equals {:{$lte:}} db.mycol.find({“likes”:{$lte:50}}).pretty() where likes <= 50
Greater Than {:{$gt:}} db.mycol.find({“likes”:{$gt:50}}).pretty() where likes > 50
Greater Than Equals {:{$gte:}} db.mycol.find({“likes”:{$gte:50}}).pretty() where likes >= 50
Not Equals {:{$ne:}} db.mycol.find({“likes”:{$ne:50}}).pretty() where likes != 50

AND在mongodb中用法

语法:

在find()方法,如果通过多个键分离’,’,那么Mongodb处理AND条件,AND基本语法如下所示:

> db.mycol.find({key1:value,key2:value}).pretty()

例子:
下面给出的例子将显示所有的教程,标题是“MongoDB Overview”

> db.mycol.find({“by”:”tutorials point”,”title”: “MongoDB Overview”}).pretty()
{
“_id” : ObjectId(“54c4b988eb3ef80c13dfeb29”),
“title” : “MongoDB Overview”,
“description” : “MongoDB is no sql database”,
“by” : “tutorials point”,
“url” : “http://www.baidu.com”,
“tags” : [
“mongodb”,
“database”,
“NoSQL”
],
“likes” : 100
}

对于上面给出的例子相当于where子句 ‘ where by=’yiibai’ AND title=’MongoDB Overview’ , 可以通过任意数量的键值对在 find 子句。

mongodb中的OR

语法:

OR条件的基础上要查询文件,需要使用$or关键字。OR基本语法如下所示:

>db.mycol.find({
{
$or:[
{key1:value1},{key2:value2}
}
}
}).pretty()

例子

下面给出的例子将显示所有的教程,由’yiibai’ 所写或标题是“MongoDB Overview ‘

> db.mycol.find({$or:[{“by”:”yiibai”},{“title”: “MongoDB Overview”}]}).pretty()
{
“_id” : ObjectId(“54c4b988eb3ef80c13dfeb29”),
“title” : “MongoDB Overview”,
“description” : “MongoDB is no sql database”,
“by” : “tutorials point”,
“url” : “http://www.baidu.com”,
“tags” : [
“mongodb”,
“database”,
“NoSQL”
],
“likes” : 100
}

AND和OR一起使用

例子:

下面给出的例子将显示有像的文件大于100,其标题是“MongoDB Overview’或者是’yiibai’ 。等效于 SQL where子句 为 ‘where likes>10 AND (by = ‘yiibai’ OR title = ‘MongoDB Overview’)’

> db.mycol.find({“likes”:{$gt:10}, $or: [{“by”: “yiibai”}, {“title”: “MongoDB Overview”}] }).pretty()
{
“_id” : ObjectId(“54c4b988eb3ef80c13dfeb29”),
“title” : “MongoDB Overview”,
“description” : “MongoDB is no sql database”,
“by” : “tutorials point”,
“url” : “http://www.baidu.com”,
“tags” : [
“mongodb”,
“database”,
“NoSQL”
],
“likes” : 100
}

更新文档

mongodb的update()和save()方法用于更新文档的集合。update()方法更新现有的文档值.而替换现有的文档通过文件中save()方法.

语法:
db.COLLECTION_NAME.update(SELECTION_CRITERIA,UPDATED_DATA)

例子:

考虑以下数据mycol集合。

{ “_id” : ObjectId(5983548781331adf45ec5), “title”:”MongoDB Overview”}
{ “_id” : ObjectId(5983548781331adf45ec6), “title”:”NoSQL Overview”}
{ “_id” : ObjectId(5983548781331adf45ec7), “title”:”Tutorials Point Overview”}
下面的例子将设置新标题’MongoDB Overview’的文件,更新其标题是“New MongoDB Tutorial”

下面的例子将设置新标题’MongoDB Overview’的文件,更新其标题是“New MongoDB Tutorial”

> db.mycol.update({“title”:”MongoDB Overview”},{$set:{“title”:”MongoDB”}})
WriteResult({ “nMatched” : 1, “nUpserted” : 0, “nModified” : 1 })
> db.mycol.find().pretty()
{
“_id” : ObjectId(“54c4b988eb3ef80c13dfeb29”),
“title” : “MongoDB”,
“description” : “MongoDB is no sql database”,
“by” : “tutorials point”,
“url” : “http://www.baidu.com”,
“tags” : [
“mongodb”,
“database”,
“NoSQL”
],
“likes” : 100
}

MongoDB默认将只更新单一的文件,来更新多个你需要设置参数置’multi’ 为true。

> db.mycol.update({“title”:”MongoDB”},{$set:{“title”:”Mongo”}},{multi:true})
WriteResult({ “nMatched” : 1, “nUpserted” : 0, “nModified” : 1 })
>

save()语法:
而替换现有的文档通过文件中save()方法.

db.COLLECTION_NAME.save({_id:objectId(),NEW_DATA})

例子:

> db.mycol.save({“_id”:ObjectId(“54c4b988eb3ef80c13dfeb29″),”by”:”Yiibai”})
WriteResult({ “nMatched” : 1, “nUpserted” : 0, “nModified” : 1 })
> db.mycol.find().pretty()
{ “_id” : ObjectId(“54c4b988eb3ef80c13dfeb29”), “by” : “Yiibai” }

删除文档

MongoDB的 remove() 方法用于从集合中删除文档。remove() 方法接受两个参数。第一个是删除criteria ,第二是justOne标志:

deletion criteria :(可选)删除标准,根据文件将被删除。

justOne : (可选)如果设置为true或1,然后只删除一个文件。

命令:
db.COLLECTION_NAME.remove(DELETEION_CRITTERIA)

例子:

[从创建集合到删除某数据]

>use mycol
> db.createCollection(“mycols”,{autoIndexID:true})
{ “ok” : 1 }
> db.mycols.insert({“title”:”MongoDB Overview”})
WriteResult({ “nInserted” : 1 })
> db.mycols.insert({“title”:”NoSQL Overview”})
WriteResult({ “nInserted” : 1 })
> db.mycols.insert({“title”:”baidu”})
WriteResult({ “nInserted” : 1 })
> db.mycols.remove({“title”:”baidu”})
WriteResult({ “nRemoved” : 1 })
> db.mycols.find().pretty()
{
“_id” : ObjectId(“54c4f625dc03cf4f7b5b165c”),
“title” : “MongoDB Overview”
}
{ “_id” : ObjectId(“54c4f634dc03cf4f7b5b165d”), “title” : “NoSQL Overview” }

删除只有一个

如果有多个记录且要删除的只有第一条记录,那么设置remove()方法中justOne参数

命令:db.COLLECTION_NAME.remove(DELETION_CRITERIA,1)

删除所有文件

如果不指定删除条件,然后MongoDB将从集合中删除整个文件。这相当于SQL的truncate命令.
>db.mycols.remove()
>db.mycols.find()
>

投影

mongodb 投影意思是只选择必要的数据而不是选择一个文件的数据的整个。如果一个文档有5个字段,需要显示只有3个,然后选择其中只有3个字段。

find() 方法

MongoDB 的find()方法,在 MongoDB查询 文档解释接受第二个可选参数是要检索的字段列表。在MongoDB中,当执行find()方法,那么它会显示一个文档所有字段。要限制这一点,需要设置的字段列表值1或0。 1用来显示字段而0是用来隐藏字段。

语法:
>db.COLLECTION_NAME.find({},{KEY:1})

例子:

隐藏记录的id;

> db.mycols.find({},{“title”:1,_id:0})
{ “title” : “MongoDB Overview” }
{ “title” : “NoSQL Overview” }
>

请注意_id字段始终显示在执行find()方法,如果不想显示这个字段,那么需要将其设置为0

限制记录

可以使用limit()方法来限制查询记录.
语法:
>db.COLLECTION_NAME.find().limit(NUMBER)

示例

下面的例子将只显示5条文档.

> db.mycols.find({},{“title”:1,_id:0}).limit(5)
{ “title” : “MongoDB Overview” }
{ “title” : “NoSQL Overview” }
{ “title” : “baidu” }
{ “title” : “google” }
{ “title” : “sogou” }
>

如果不指定limit()方法的参数,它会显示从集合中获取到的所有文件.

skip()方法.

设置跳过的文档数.

语法:

db.COLLECTION_NAME.find().limit(NUMBER).skip(NUMBER)

示例:

下面的例子将只显示第二个文档。

> db.mycols.find().pretty()
{
“_id” : ObjectId(“54c4f625dc03cf4f7b5b165c”),
“title” : “MongoDB Overview”
}
{ “_id” : ObjectId(“54c4f634dc03cf4f7b5b165d”), “title” : “NoSQL Overview” }
{ “_id” : ObjectId(“54c4f82ddc03cf4f7b5b165f”), “title” : “baidu” }
{ “_id” : ObjectId(“54c4f830dc03cf4f7b5b1660”), “title” : “google” }
{ “_id” : ObjectId(“54c4f836dc03cf4f7b5b1661”), “title” : “sogou” }
{ “_id” : ObjectId(“54c4f83adc03cf4f7b5b1662”), “title” : “net163” }
> db.mycols.find({},{“title”:1,”_id”:0}).limit(2).skip(1)
{ “title” : “NoSQL Overview” }
{ “title” : “baidu” }
> db.mycols.find({},{“title”:1,”_id”:0}).limit(2).skip(2)
{ “title” : “baidu” }
{ “title” : “google” }
>

注意:skip()方法的默认值是0.

排序文档

排序使用sort()方法,该方法接收一个参数:-1为降序,1为升序.

语法:
— [-1]降序,[1]升序
>db.COLLCTION_NAME.find().sort({KEY:1})

示例

> db.mycols.find({},{“title”:1,_id:0}).sort({“title”:-1})
{ “title” : “sogou” }
{ “title” : “net163” }
{ “title” : “google” }
{ “title” : “baidu” }
{ “title” : “NoSQL Overview” }
{ “title” : “MongoDB Overview” }
> db.mycols.find({},{“title”:1,_id:0}).sort({“title”:1})
{ “title” : “MongoDB Overview” }
{ “title” : “NoSQL Overview” }
{ “title” : “baidu” }
{ “title” : “google” }
{ “title” : “net163” }
{ “title” : “sogou” }
>

请注意,如果不指定排序优先,然后sort() 方法将文档显示在升序排列。

索引

索引支持的解析度的查询效率。如果没有索引,MongoDB 必须扫描每一个文档的集合,要选择那些文档相匹配的查询语句。这种扫描的效率非常低,会要求 mongod 做大数据量的处理。

索引是一种特殊的数据结构,存储设置在一个易于遍历形式的数据的一小部分。索引存储一个特定的字段或一组字段的值,在索引中指定的值的字段排列的。

语法

db.COLLECTION_NAME.ensureIndex({KEY:1})

这里关键字是要在其中创建索引,1是按升序排列,要创建降序索引,需要使用-1.

例子:

> db.mycols.ensureIndex({“title”:1})
{
“createdCollectionAutomatically” : false,
“numIndexesBefore” : 1,
“numIndexesAfter” : 2,
“ok” : 1
}

在ensureIndex()方法,可以通过多个字段多个字段上创建索引。

> db.mycols.ensureIndex({“title”:1,_id:-1})
{
“createdCollectionAutomatically” : false,
“numIndexesBefore” : 2,
“numIndexesAfter” : 3,
“ok” : 1
}

ensureIndex() 方法也可以接受的选项列表(可选),其下面给出的列表:

参数 类型 描述
background Boolean 在后台建立索引,以便建立索引并不能阻止其他数据库活动。指定true建立在后台。默认值是 false.
unique Boolean 创建唯一索引,以便收集不会接受插入索引键或键匹配现有的值存储在索引文档。指定创建唯一索引。默认值是 false.
name string 索引的名称。如果未指定,MongoDB中都生成一个索引名索引字段的名称和排序顺序串联.
dropDups Boolean 创建一个唯一索引的字段,可能有重复。 MongoDB的索引只有第一次出现的一个键,从集合中删除的所有文件包含该键的后续出现的。指定创建唯一索引。默认值是 false.
sparse Boolean 如果为true,指数只引用文档指定的字段。这些索引使用更少的空间,但在某些情况下,特别是各种不同的表现。默认值是 false.
expireAfterSeconds integer 指定一个值,以秒为TTL控制多久MongoDB的文档保留在此集合.
v index version 索引版本号。默认的索引版本取决于mongodb 运行的版本在创建索引时.
weights document 权重是从1到99999范围内的数,表示该字段的意义,相对于其他的索引字段分数.
default_language string 对于文本索引时,决定停止词和词干分析器和标记生成规则列表的语言。默认值是 english.
language_override string 对于文本索引时,指定的名称在文档中包含覆盖默认的语言,语言字段中。默认值是语言。

聚合

聚合操作过程中的数据记录和计算结果返回。聚合操作分组值从多个文档,并可以执行各种操作,分组数据返回单个结果。在SQL COUNT(*)和group by 相当于MongoDB的聚集。

对于在MongoDB中聚集,应该使用aggregate()方法。

命令语法:
>db.COLLECTION_NAME.aggregate(AGGREGATE_OPERATION)

示例:

集合中的数据:
{
_id: ObjectId(7df78ad8902c)
title: ‘MongoDB Overview’,
description: ‘MongoDB is no sql database’,
by_user: ‘yiibai point’,
url: ‘http://www.yiibai.com’,
tags: [‘mongodb’, ‘database’, ‘NoSQL’],
likes: 100
},
{
_id: ObjectId(7df78ad8902d)
title: ‘NoSQL Overview’,
description: ‘No sql database is very fast’,
by_user: ‘yiibai point’,
url: ‘http://www.yiibai.com’,
tags: [‘mongodb’, ‘database’, ‘NoSQL’],
likes: 10
},
{
_id: ObjectId(7df78ad8902e)
title: ‘Neo4j Overview’,
description: ‘Neo4j is no sql database’,
by_user: ‘Neo4j’,
url: ‘http://www.neo4j.com’,
tags: [‘neo4j’, ‘database’, ‘NoSQL’],
likes: 750
},

用aggregate()方法统计:

> db.mycols.aggregate([{$group : {_id : “$by_user”, num_tutorial : {$sum : 1}}}])
{ “_id” : “yiibai point”, “num_tutorial” : 1 }
{ “_id” : null, “num_tutorial” : 6 }

上述使用的情况相当于SQL查询 select by_user, count(*) from mycol group by by_user

在上面的例子中,我们已分组字段 by_user 文档,并在每个的次数by_user先前的值总和递增。没有聚集表达式列表。

表达式 描述 实例
$sum 总结从结合中的所有文件所定义的值 db.mycols
.aggregate([$group:{_id:”$by_user”,num_tutorial:{$sum:”$likes”}}])
$age 从所有文档集合中所有给定值计算的平均 db.mycols
.aggregate
([
$group:{_id:”$by_user”,
num_tutorial:
{$avg:”$likes”}}}
])

$min 获取集合中的所有文件中的相应值最小. db.mycols
.aggregate
([
$group:{_id:”$by_user”,
num_tutorial:
{$min:”$likes”}}}
])
$max 获取集合中的所有文件中的相应值的最大. db.mycols
.aggregate
([
$group:{_id:”$by_user”,
num_tutorial:
{$max:”$likes”}}}
])
$push 值插入到一个数组生成文档中. db.mycols
.aggregate
([
$group:{_id:”$by_user”,
url:
{$push:”$url”}}}
])
$addToSet 值插入到一个数组中所得到的文档,但不会创建重复. db.mycols
.aggregate
([
$group:{_id:”$by_user”,
url:
{$addToSet:”$url”}}}
])
$first 根据分组从源文档中获取的第一个文档。通常情况下,这才有意义,连同以前的一些应用 “$sort”-stage. db.mycols
.aggregate
([
$group:{_id:”$by_user”,
first_url:
{$first:”$url”}}}
])
$last 根据分组从源文档中获取最后的文档。通常,这才有意义,连同以前的一些应用 “$sort”-stage. db.mycols
.aggregate
([
$group:{_id:”$by_user”,
last_url:
{$last:”$url”}}}
])

管道概念

在UNIX 命令 shell 管道是指一些输入和输出作为输入下一个命令等执行操作的可能性。 MongoDB 聚合框架也支持同样的概念。有一组可能的阶段,每个这些的一组文档作为输入,并产生一个结果集的文件(或最终生成的JSON文档在管道末端)。然后又再次被用来为下一阶段等。

可能的阶段聚合框架如下:

$project: 用于选择从收集的一些具体字段。
$match: 这是一个滤波操作,因此可以减少量,作为下一阶段的输入给定的文档。
$group: 如上所讨论的,这不实际的聚合。
$sort: 文件排序。
$skip: 与此有可能向前跳过的文件列表中的一个给定的的文档数量。
$limit: 这限制了的文档数量看一下由从当前位置开始的给定数
$unwind: 这是用来平仓文档的中使用数组。使用数组时,数据是一种pre-joinded,再次有个别文件,此操作将被取消。因此,这个阶段,数量会增加文件的下一阶段。

复制

复制是跨多个服务器同步数据的过程中。复制提供了冗余和增加数据可用性与不同的数据库服务器上的数据的多个副本,复制保护数据库从一台服务器上的损失。复制也可以让恢复硬件故障和服务中断。额外的数据副本,可以奉献一​​到灾难恢复,报告,或备份。

为什么要复制?

为了让数据安全
高(24* 7)数据可用性
灾难恢复
无停机维护(如备份,索引重建,压实)
读缩放(额外的副本读取)
副本集对应用程序是透明
MongoDB中复制的工作原理

MongoDB 使用副本集达到复制。副本集是一组 mongod 实例,主机相同的数据集。副本中的一个节点,主节点接收所有的写操作。在所有其他情况下,次要节点,适用于从主操作,以使它们具有相同的数据集。副本集只能有一个主节点。

副本集是一组中的两个或多个节点(一般至少3个节点是必需的)。
在副本中设置一个节点是主节点和剩余节点都是次要的。
从主要到次要节点的所有数据复制。
自动故障转移或维修的时候,选初级建立,并选出新的主节点。
失败的节点恢复后,再加入副本集和作品作为辅助节点。
MongoDB 复制是一个典型的图显示在客户端应用程序总是与主节点和主节点,然后将数据复制到二级节点。

MongoDB Replication

副本集特点

N个节点的群集
任何节点可以是主要的
所有的写操作为主要的
自动故障转移
自动恢复
主要的共识选择
设置一个副本集

在本教程中,我们将mongod实例转换成独立的副本集。要转换到副本设置遵循以下步骤:

关闭停止已经运行的MongoDB服务器。
现在启动MongoDB服务器通过指定 –replSet 选项。 –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
它会启动一个mongod 实例名称rs0 ,端口为27017。启动命令提示符 rs.initiate(),并连接到这个mongod实例。在mongod客户端执行命令rs.initiate()启动一个新的副本集。要检查副本集的配置执行命令rs.conf()。要检查的状态副本sete执行命令:rs.status()。

将成员添加到副本集

将成员添加到副本集,在多台机器上启动mongod 实例。现在开始一个mongod 客户和发出命令 rs.add().

语法:

rs.add()命令的基本语法如下:

>rs.add(HOST_NAME:PORT)
实例

假设mongod实例的名字是mongod1.net它运行端口为27017。这种情况下,到副本集执行的命令rs.add() 在mongod 客户端。

>rs.add(“mongod1.net:27017”)
>
可以添加mongod实例副本设置,只有当连接到主节点。要检查是否连接至初级或mongo 客户端不执行命令db.isMaster()。

分片

分片

分片存储在多台机器上的数据记录的过程,它是MongoDB的方法来满足数据增长的需求。一台机器上的大小的数据的增加,可能并不足够来存储数据,也不能提供可接受的读取和写入通过。分片解决问题的水平缩放。通过分片,你添加更多的机器支持数据增长的读取和写入操作需求。

为什么要分片?

复制所有写转到掌握节点

延时敏感的查询还是到主节点

单副本集有12个节点的限制

内存不能足够大,当活动数据集很大

本地磁盘不够大

垂直缩放太贵

MongoDB分片

下面给出的图显示了在MongoDB中使用分片分片集群。

MongoDB Sharding
在上面给出的图中,有三个主要组成部分,这说明如下:

碎片: 碎片被用来存储数据。它们提供了高可用性和数据的一致性。在生产环境中,每个碎片是一个单独的副本集。

配置服务器: 配置服务器集群的元数据存储。该数据包含集群的数据碎片的映射。查询路由器使用这个元数据,操作具体的碎片。在生产环境中,有整整3分片集群配置服务器。

查询路由: 查询路由基本上是Mongos实例,客户端应用程序界面和直接操作相应的碎片。查询路由过程目标操作的碎片,然后将结果返回到客户端。分片集群可以包含多个查询路由来划分客户端请求负载。客户端发送请求到一个查询路由。一般分片集群有许多查询路由。

备份

创建备份MongoDB中的数据库,应该使用mongodump命令。此命令将服务器的所有数据转储到转储目录。有许多可供选择,通过它可以限制的数据量或创建备份您的远程服务器。

语法:
mongodump

示例

在mongodb的bin目录下找到并运行,即可备份.

此命令将仅备份指定的数据库,在指定的路径

语法 描述 示例
mongodump –host HOST_NAME –port PORT_NUMBER 这条命令,将指定mongod实例备份所有的数据库。 mongodump –host yiibai.com –port 27017
mongodump –dbpath DB_PATH –out BACKUP_DIRECTORY mongodump –dbpath /data/db/ –out /data/backup/
mongodump –collection COLLECTION –db DB_NAME 此命令将备份指定的数据库唯一指定的集合。 mongodump –collection mycol –db test

恢复数据

命令: mongorestore

若将dump目录[备份目录]放在bin目录下,mongorestore会自动恢复.

部署

当正在准备一个MongoDB 部署,应该尽量了解应用程序将要投入生产。制定一个一致的,可重复的方法来管理部署环境,可以最大限度地减少任何意外。

最好的方法采用了原型,进行负载测试,监测的关键指标,并利用这些信息来扩展设置。办法是主动监控整个系统的关键部分 – 这将帮助了解您的生产系统,然后再部署,并确定在这里将需要增加容量。洞察潜在内存使用峰值,例如,可以帮忙把写锁防火在它启动之前。

要监控部署 MongoDB 提供了一些命令,如下所示:

mongostat

此命令检查所有正在运行的 mongod 实例,并返回计数器的数据库操作的状态。这些计数器包括插入,查询,更新,删除,和游标。命令还显示,当打页面错误并展示锁的百分比。这意味着正在运行可能内存不足,命中写容量少,或有一些性能问题。

mongotop

此命令的跟踪和报告收集的基础上,读取和写入 MongoDB实例活动。默认情况下,mongotop 返回的信息在每一秒可以相应地改变它。应该检查此读写活动符合应用程序的意图,不触发太多写入到数据库的时间,从磁盘读过于频繁,或超过工作集大小。

要改变mongotop 命令返回的信息较少指定一个特定数量在 mongotop命令之后。

D:\set up\mongodb\bin>mongotop 30
上面的例子将每30秒返回值。

除了 MongoDB 工具,10gen公司提供:免费托管监控服务MongoDB 管理服务(MMS),提供了一个仪表板,可监视整个群集的指数。

MongoDB与Java

要在 java 中使用MongoDB,需要到 classpath 包括 mongo.jar。可以下载 jar包从路径 下载mongo.jar。请一定要下载它的最新版本。

建立连接

要连接,需要指定数据库名称,如果数据库不存在,则 MongoDB 会自动创建它。

代码片段连接到数据库,将如下:

import com.mongodb.MongoClient;
import com.mongodb.MongoException;
import com.mongodb.WriteConcern;
import com.mongodb.DB;
import com.mongodb.DBCollection;
import com.mongodb.BasicDBObject;
import com.mongodb.DBObject;
import com.mongodb.DBCursor;
import com.mongodb.ServerAddress;

import java.util.Arrays;

// To connect to mongodb server
MongoClient mongoClient = new MongoClient( “localhost” , 27017 );
// Now connect to your databases
DB db = mongoClient.getDB( “test” );
boolean auth = db.authenticate(myUserName, myPassword);
身份验证值是 true,那么所选数据库的用户名和密码是有效的。

获取一个集合列表

为了从数据库获得集合列表,com.mongodb.DB类使用getCollectionNames()方法。

代码片段集合列表:

Set colls = db.getCollectionNames();
for (String s : colls) {
System.out.println(s);
}
获取/选择一个集合

要 获得/选择数 据库中一个集合,使用com.mongodb.DBCollection类的 getCollection()方法。

代码片段获得/选择数一个集合:

DBCollection coll = db.getCollection(“mycol”);
插入文档

要插入到 MongoDB 文档, 使用com.mongodb.DBCollection类的insert() 方法

代码片段插入文件:

BasicDBObject doc = new BasicDBObject(“title”, “MongoDB”).
append(“description”, “database”).
append(“likes”, 100).
append(“url”, “http://www.yiibai.com/mongodb/”).
append(“by”, “yiibai.com”).
;
coll.insert(doc);
查找第一个文档

要选择从集合中的第一个文档,使用com.mongodb.DBCollection类的 findOne() 方法

先选择文档的代码片段:

DBObject myDoc = coll.findOne();
System.out.println(myDoc);
剩余的 MongoDB 方法,如: find(), save(), update(), limit(), skip(), sort() 等作品在随后的教程说明。

MongoDB与PHP

想要在 PHP 中使用MongoDB,需要使用 MongoDB 的 PHP驱动程序。从URL下载PHP驱动程序。请一定要下载它的最新版本。现在,解压存档并把php_mongo.dll,放在 PHP 扩展目录(默认情况下为“ext”),并添加下面一行到php.ini文件:

extension=php_mongo.dll
建立连接,并选择一个数据库

要连接,需要指定数据库名称,如果数据库不存在,则 MongoDB 会自动创建它。

代码片段连接到数据库,如下:

mydb;
?>
获取/选择一个集合

代码片段/选择一个集合:

mycol;
?>
插入文档

将文档插入到 MongoDB ,使用insert() 方法。

代码片段插入文档:

“MongoDB”,
“description” => “database”,
“likes” => 100,
“url” => “http://www.yiibai.com/mongodb/”,
“by”, “yiibai.com”
);
$collection->insert($document);
?>
查找从集合中的所有文档

要选择从集合中所有文档,使用find()方法。

选择所有文档的代码片段:

find();

// iterate cursor to display title of documents
foreach ($cursor as $document) {
echo $document[“title”] . “\n”;
}
?>
其余的 MongoDB 方法 findOne(), save(), update(), limit(), skip(), sort() 等工作原理在随后的教程说明。

Code:未经”深思熟虑”的后果

题外话

标题明显是”标题党”了,简单说下:只是因为今天想用Python写一个获取昨天的日期,但是复杂度是:我要连着获取好几天的.,比如今天是1号,连着获取10天就要到上个月21号了(是这样没错吧?)

一开始用Python写了一个版本,调了大半天,后来才调好……此刻才发现有更好的解决方案!!!!!!

python Logo
image-2239

Python实现

没错,正好今天没事,我也写了一个Java版本的.

请不要像下面这样去写,后面有比较好的方案!

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
#Python代码片段1
resultdata=[]
'''
计算过去的日期较复杂版本--失败版本--请不要使用这个版本的,可以学习下在哪个地方写失败了!!!
调用 :#print getlastdate(2015,01,05,20)
years:年
months:月
dates:日
number:往后退多少天.
'''
def getlastdate(years,months,dates,number):
    resudata=""
    if number > 0 :
        #print str(years)+"-----"+str(months)+"-----"+str(dates)
        rsnumber=number-1
        last = datetime.date( int(years)-1 if (months==01 and dates==01)  else int(years), int(months)-1 if dates==01 else int(months),int(dates))-datetime.timedelta(1)
        dates=str(last)[8:]
        monthss=str(last)[5:7]
        yearss = str(last)[0:4]
        #print monthss
        resudata=str(last).replace("-","")
    if number==0:
        return resultdata
    resultdata.append(resudata)
   
    return getlastdate(yearss,monthss,dates,rsnumber)

说来惭愧,上面那个版本太失败了,下面这个稍微好些,可能还有更好的办法去实现,等我发现在说吧.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#Python代码片段2
'''
计算过去的日期,可用版本

调用: print getLastDATE(10)

@prd
@2015.1.24

@numbs:要减少的数.
'''
jsdate=[]
def getLastDATE(numbs):
    resultdatas=""
    rsnums=numbs-1
    if numbs>0:
        last = datetime.date( datetime.datetime.now().year, datetime.datetime.now().month,datetime.datetime.now().day)-datetime.timedelta(numbs)
        resultdatas=str(last).replace("-","")
    if numbs==0:
        return jsdate

    jsdate.append(resultdatas)
    return getLastDATE(rsnums)

2015.1.28 :UPDATE:更加简洁的版本,上述版本可能有重大错误!

1
2
3
4
5
6
// thanks cys.

def getLastDATE(numbs):
    day = datetime.datetime.now() - datetime.timedelta(days=numbs)
    rdate = [(day + datetime.timedelta(days=i)).strftime("%Y%m%d") for i in range(numbs,0,-1 )]
    return rdate[:len(rdate)]

关于[Python代码片段1]和[Python代码片段2]想必一眼就看出来了…不说了,继续去看Python……不对,Java版本发完再说,哈哈

java标志
image-2240

Java实现

对于Java来说,实现还是没难度的,基本就是把Python上面写的思路拿过来改一下就可以用了.

但是….但是…..我还是做了两个版本…只是一个稍逊,另一个更好些.

一个是Java 1.7版本的,另外一个是最新版的[这个有亮点,写起来超爽啊!!!].

没有从[Python代码片段1]的思路来实现Java版本的,我是真的…………懒!

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
//Java 1.7 版本,稍微复杂些
/**

计算日期


@author prd
@date 2015.1.24

*/
public class Main{

    /**
     * 计算日期:在当前日期上加或者减
     *
     * @param numbers 天数:如果要加,请传递正数,反之则传递负数
     * @param flag 标识:标明是增加还是减少.增加:true,减少:false
     * @param list 返回此list:若无此参数可能导致数据重复
     * @return
     */
    private  List<String> addCalendar(int numbers,boolean flag,List<String> list){
        if (flag) {//上升
            if (numbers < 0) {
                Calendar ca = Calendar.getInstance();
                ca.add(Calendar.DAY_OF_YEAR, numbers);
                addCalendar(numbers += 1,true,list);
                list.add(new SimpleDateFormat("yyyy-MM-dd").format(ca.getTime()));
            }
        }else{
            if (numbers > 0) {
                Calendar ca = Calendar.getInstance();
                ca.add(Calendar.DAY_OF_YEAR, numbers);
                list.add(new SimpleDateFormat("yyyy-MM-dd").format(ca.getTime()));
                addCalendar(numbers -= 1, false,list);
            }

        }
        return list;
    }


    /*打印list*/
    private  void iteratorList(List li){
        for (int i = 0; i < li.size(); i++) {
            System.out.println(li.get(i));
        }
    }

    public static void main(String[] args) {
        Main m = new Main();
        m.iteratorList(m.addCalendar(-5, true,new ArrayList<String>())); // 当前日期减少5天
        System.out.println("--------------------------------");
        m.iteratorList(m.addCalendar(5, false,new ArrayList<String>()));//当前日期增加5天
    }
}

下面是Java 1.8版本,请使用Jdk 1.8 来运行!

说实话,Java 1.8版本提供的时间类库简直太爽了,竟然能让人想起写Python时的”飘逸”……

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
public class Main{
   /**
     * ----请使用Java 8 运行
     *
     * 在当前日期上进行加减操作.
     *
     *
     * @param number 要加减的数字,直接传递正整数即可
     * @param flag 加:true,减:false
     * @param list 用于返回数据,以保证不会有数据错误.
     * @return
     */
    private static List<String>  getDatejava8(int number,boolean flag,List<String> list){
            if (number > 0) {
                LocalDateTime date = LocalDateTime.now();
                    boolean rs = flag ? list.add(date.plusDays(number).format(DateTimeFormatter.ISO_LOCAL_DATE).toString()): list.add(date.minusDays(number).format(DateTimeFormatter.ISO_LOCAL_DATE).toString()) ;
                getDatejava8(number -= 1, flag, list);
            }
        return list;
    }

    public static void main(String[] args) {
        // 请使用Java8运行.
        getDatejava8(5,true,new ArrayList<String>()).forEach(System.out::println);//在当前日期上增加日期并打印
        System.out.println("--------------------------------");
        //-------------
        getDatejava8(10,false,new ArrayList<String>()).forEach(System.out::println);//在当前日期上减少日期并打印
    }
}

忽略,也可以不忽略

Java 1.8提供的更新一时半会还缓不过来,只能在每次写代码的时候,尽量用新特性.

但愿,但愿,以后可以不走弯路.

虽然,弯路也是一种成长!

Java:一段容易让人迷惑的代码

题外话

wordpress升级后的写文章页面更舒服了.

正题

有时候我们会遇到很多让我们迷惑的Java代码,比如下面这段就是,可以先想下其的输出结果.

1
2
3
4
5
6
7
8
9
10
11
12
public class Test{
      public static void main(String[] args) {
            StringBuffer a=new StringBuffer ("A");
            StringBuffer b=new StringBuffer ("B");
            oper(a,b);
            System.out.print(a+","+b);
        }
      static void oper(StringBuffer c,StringBuffer d){
          c.append("B");  
          d=c;
      }
}

其实上面的输出结果是:ABB.

因为c是实际添加了B,而d只是单纯的传入,与b没有任何关系.

java标志
image-2234

C++实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#include<iostream>
using namespace std;

void replace(int &a,int b)
{
   a=20;
   b+=a;
}

int main()
{
  int a = 30;
  int b = 70;
  replace(a,b);
  cout << a << "---" << b << endl;
  return 0;
}

一个跟原来的值是有关系的,另一个跟原来的值是没有关系的.

其他的一些代码片段就懒得发了…