So Tired !_! 逆水行舟, 不进则退!

4Sep/17

MongoDB 数据导出mongoexport(可导出为txt或csv)

Posted by Nick Xu

1、概述

mongoexport命令行用于数据的导出,默认导出的文件格式为JSON格式。当然也可以指定特定的文件格式。

2、语法

  1. C:\mongo\bin>mongoexport -help
  2. options:
  3.   --help                  produce help message
  4.   -v [ --verbose ]        be more verbose (include multiple times for more
  5.                           verbosity e.g. -vvvvv)
  6.   -h [ --host ] arg       mongo host to connect to ( <set name>/s1,s2 for sets)
  7.   --port arg              server port. Can also use --host hostname:port
  8.   --ipv6                  enable IPv6 support (disabled by default)
  9.   -u [ --username ] arg   username
  10.   -p [ --password ] arg   password
  11.   --dbpath arg            directly access mongod database files in the given
  12.                           path, instead of connecting to a mongod  server -
  13.                           needs to lock the data directory, so cannot be used
  14.                           if a mongod is currently accessing the same path
  15.   --directoryperdb        if dbpath specified, each db is in a separate
  16.                           directory
  17.   -d [ --db ] arg         database to use
  18.   -c [ --collection ] arg collection to use (some commands)
  19.   -f [ --fields ] arg     comma separated list of field names e.g. -f name,age
  20.   --fieldFile arg         file with fields names - 1 per line
  21.   -q [ --query ] arg      query filter, as a JSON string
  22.   --csv                   export to csv instead of json
  23.   -o [ --out ] arg        output file; if not specified, stdout is used
  24.   --jsonArray             output to a json array rather than one object per
  25.                           Line

说明:

-h:数据库宿主机的IP

-u:数据库用户名

-p:数据库密码

-d:数据库名字

-c:集合的名字

-f:导出的列名

-q:导出数据的过滤条件

--csv:导出格式为csv

3、示例

现有如下数据:

现在进行全部导出操作,默认导出的是JSON格式的数据:

上述命令行:从test数据库中导出集合为emp的数据,保存文件到emp.dat中。

查看:

下面是导出csv格式的命令:

查看:

实战示例:

1、将集合中account和balance字段导出为txt格式

mongoexport -h 127.0.0.1 --port 27017 --username username  --password password  --collection accounts -f account,balance --out /usr/local/bak/db/1.txt --db dbname

2、将集合中account和balance字段导出为csv格式

mongoexport -h 127.0.0.1 --port 27017 --username username --csv --password password  --collection accounts -f account,balance --out /usr/local/bak/db/1.csv --db dbname

Tagged as: , Comments Off
8Jul/16

MongoDB 备份(mongodump)与恢复(mongorerstore)

Posted by Nick Xu

MongoDB数据备份

在Mongodb中我们使用mongodump命令来备份MongoDB数据。该命令可以导出所有数据到指定目录中。

mongodump命令可以通过参数指定导出的数据量级转存的服务器。

语法

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:

>mongodump

执行以上命令后,客户端会连接到ip为 127.0.0.1 端口号为 27017 的MongoDB服务上,并备份所有数据到 bin/dump/ 目录中。命令输出结果如下:

MongoDB数据备份

mongodump 命令可选参数列表如下所示:

语法 描述 实例
mongodump --host HOST_NAME --port PORT_NUMBER 该命令将备份所有MongoDB数据 mongodump --host w3cschool.cc --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

MongoDB数据恢复

mongodb使用 mongorerstore 命令来恢复备份的数据。

语法

mongorestore命令脚本语法如下:

>mongorestore -h dbhost -d dbname --directoryperdb dbdirectory
  • -h:MongoDB所在服务器地址
  • -d:需要恢复的数据库实例,例如:test,当然这个名称也可以和备份时候的不一样,比如test2
  • --directoryperdb:备份数据所在位置,例如:c:\data\dump\test,这里为什么要多加一个test,而不是备份时候的dump,读者自己查看提示吧!
  • --drop:恢复的时候,先删除当前数据,然后恢复备份的数据。就是说,恢复后,备份后添加修改的数据都会被删除,慎用哦!

接下来我们执行以下命令:

>mongorestore

执行以上命令输出结果如下:

MongoDB数据恢复

Tagged as: Comments Off
17Nov/15

MongoDB 副本集的原理、搭建、应用

Posted by Nick Xu

概念:

在了解了这篇文章之后,可以进行该篇文章的说明和测试。MongoDB 副本集(Replica Set)是有自动故障恢复功能的主从集群,有一个Primary节点和一个或多个Secondary节点组成。类似于MySQL的MMM架构。更多关于副本集的介绍请见官网。也可以在google、baidu上查阅。

副本集中数据同步过程Primary节点写入数据,Secondary通过读取Primary的oplog得到复制信息,开始复制数据并且将复制信息写入到自己的oplog。如果某个操作失败,则备份节点停止从当前数据源复制数据。如果某个备份节点由于某些原因挂掉了,当重新启动后,就会自动从oplog的最后一个操作开始同步,同步完成后,将信息写入自己的oplog,由于复制操作是先复制数据,复制完成后再写入oplog,有可能相同的操作会同步两份,不过MongoDB在设计之初就考虑到这个问题,将oplog的同一个操作执行多次,与执行一次的效果是一样的。简单的说就是:

当Primary节点完成数据操作后,Secondary会做出一系列的动作保证数据的同步:
1:检查自己local库的oplog.rs集合找出最近的时间戳。
2:检查Primary节点local库oplog.rs集合,找出大于此时间戳的记录。
3:将找到的记录插入到自己的oplog.rs集合中,并执行这些操作。

副本集的同步和主从同步一样,都是异步同步的过程,不同的是副本集有个自动故障转移的功能。其原理是:slave端从primary端获取日志,然后在自己身上完全顺序的执行日志所记录的各种操作(该日志是不记录查询操作的),这个日志就是local数据 库中的oplog.rs表,默认在64位机器上这个表是比较大的,占磁盘大小的5%,oplog.rs的大小可以在启动参数中设 定:--oplogSize 1000,单位是M。

注意:在副本集的环境中,要是所有的Secondary都宕机了,只剩下Primary。最后Primary会变成Secondary,不能提供服务。

一:环境搭建

1:准备服务器

192.168.200.25

192.168.200.245

192.168.200.252

2:安装

http://www.cnblogs.com/zhoujinyi/archive/2013/06/02/3113868.html

3:修改配置,只需要开启:replSet 参数即可。格式为:

192.168.200.252: --replSet = mmm/192.168.200.245:27017  # mmm是副本集的名称,192.168.200.25:27017 为实例的位子。

192.168.200.245: --replSet = mmm/192.168.200.252:27017

192.168.200.25: --replSet = mmm/192.168.200.252:27017,192.168.200.245:27017

4:启动

启动后会提示:

replSet info you may need to run replSetInitiate -- rs.initiate() in the shell -- if that is not already done

说明需要进行初始化操作,初始化操作只能执行一次。

5:初始化副本集

登入任意一台机器的MongoDB执行:因为是全新的副本集所以可以任意进入一台执行;要是有一台有数据,则需要在有数据上执行;要多台有数据则不能初始化。

复制代码
zhoujy@zhoujy:~$ mongo --host=192.168.200.252
MongoDB shell version: 2.4.6
connecting to: 192.168.200.252:27017/test
> rs.initiate({"_id":"mmm","members":[
... {"_id":1,
... "host":"192.168.200.252:27017",
... "priority":1
... },
... {"_id":2,
... "host":"192.168.200.245:27017",
... "priority":1
... }
... ]})
{
    "info" : "Config now saved locally.  Should come online in about a minute.",
    "ok" : 1
}
######
"_id": 副本集的名称
"members": 副本集的服务器列表
"_id": 服务器的唯一ID
"host": 服务器主机
"priority": 是优先级,默认为1,优先级0为被动节点,不能成为活跃节点。优先级不位0则按照有大到小选出活跃节点。
"arbiterOnly": 仲裁节点,只参与投票,不接收数据,也不能成为活跃节点。
> rs.status()
{
    "set" : "mmm",
    "date" : ISODate("2014-02-18T04:03:53Z"),
    "myState" : 1,
    "members" : [
        {
            "_id" : 1,
            "name" : "192.168.200.252:27017",
            "health" : 1,
            "state" : 1,
            "stateStr" : "PRIMARY",
            "uptime" : 76,
            "optime" : Timestamp(1392696191, 1),
            "optimeDate" : ISODate("2014-02-18T04:03:11Z"),
            "self" : true
        },
        {
            "_id" : 2,
            "name" : "192.168.200.245:27017",
            "health" : 1,
            "state" : 2,
            "stateStr" : "SECONDARY",
            "uptime" : 35,
            "optime" : Timestamp(1392696191, 1),
            "optimeDate" : ISODate("2014-02-18T04:03:11Z"),
            "lastHeartbeat" : ISODate("2014-02-18T04:03:52Z"),
            "lastHeartbeatRecv" : ISODate("2014-02-18T04:03:53Z"),
            "pingMs" : 0,
            "syncingTo" : "192.168.200.252:27017"
        }
    ],
    "ok" : 1
}
复制代码

6:日志

查看252上的日志:

Tue Feb 18 12:03:29.334 [rsMgr] replSet PRIMARY
…………
…………
Tue Feb 18 12:03:40.341 [rsHealthPoll] replSet member 192.168.200.245:27017 is now in state SECONDARY

至此,整个副本集已经搭建成功了。

上面的的副本集只有2台服务器,还有一台怎么添加?除了在初始化的时候添加,还有什么方法可以后期增删节点?

二:维护操作

1:增删节点。

把25服务加入到副本集中:

rs.add("192.168.200.25:27017")

复制代码
mmm:PRIMARY> rs.add("192.168.200.25:27017")
{ "ok" : 1 }
mmm:PRIMARY> rs.status()
{
    "set" : "mmm",
    "date" : ISODate("2014-02-18T04:53:00Z"),
    "myState" : 1,
    "members" : [
        {
            "_id" : 1,
            "name" : "192.168.200.252:27017",
            "health" : 1,
            "state" : 1,
            "stateStr" : "PRIMARY",
            "uptime" : 3023,
            "optime" : Timestamp(1392699177, 1),
            "optimeDate" : ISODate("2014-02-18T04:52:57Z"),
            "self" : true
        },
        {
            "_id" : 2,
            "name" : "192.168.200.245:27017",
            "health" : 1,
            "state" : 2,
            "stateStr" : "SECONDARY",
            "uptime" : 2982,
            "optime" : Timestamp(1392699177, 1),
            "optimeDate" : ISODate("2014-02-18T04:52:57Z"),
            "lastHeartbeat" : ISODate("2014-02-18T04:52:59Z"),
            "lastHeartbeatRecv" : ISODate("2014-02-18T04:53:00Z"),
            "pingMs" : 0,
            "syncingTo" : "192.168.200.252:27017"
        },
        {
            "_id" : 3,
            "name" : "192.168.200.25:27017",
            "health" : 1,
            "state" : 6,
            "stateStr" : "UNKNOWN",             #等一会就变成了 SECONDARY 
            "uptime" : 3,
            "optime" : Timestamp(0, 0),
            "optimeDate" : ISODate("1970-01-01T00:00:00Z"),
            "lastHeartbeat" : ISODate("2014-02-18T04:52:59Z"),
            "lastHeartbeatRecv" : ISODate("1970-01-01T00:00:00Z"),
            "pingMs" : 0,
            "lastHeartbeatMessage" : "still initializing"
        }
    ],
    "ok" : 1
}
复制代码

把25服务从副本集中删除:

rs.remove("192.168.200.25:27017")

复制代码
mmm:PRIMARY> rs.remove("192.168.200.25:27017")
Tue Feb 18 13:01:09.298 DBClientCursor::init call() failed
Tue Feb 18 13:01:09.299 Error: error doing query: failed at src/mongo/shell/query.js:78
Tue Feb 18 13:01:09.300 trying reconnect to 192.168.200.252:27017
Tue Feb 18 13:01:09.301 reconnect 192.168.200.252:27017 ok
mmm:PRIMARY> rs.status()
{
    "set" : "mmm",
    "date" : ISODate("2014-02-18T05:01:19Z"),
    "myState" : 1,
    "members" : [
        {
            "_id" : 1,
            "name" : "192.168.200.252:27017",
            "health" : 1,
            "state" : 1,
            "stateStr" : "PRIMARY",
            "uptime" : 3522,
            "optime" : Timestamp(1392699669, 1),
            "optimeDate" : ISODate("2014-02-18T05:01:09Z"),
            "self" : true
        },
        {
            "_id" : 2,
            "name" : "192.168.200.245:27017",
            "health" : 1,
            "state" : 2,
            "stateStr" : "SECONDARY",
            "uptime" : 10,
            "optime" : Timestamp(1392699669, 1),
            "optimeDate" : ISODate("2014-02-18T05:01:09Z"),
            "lastHeartbeat" : ISODate("2014-02-18T05:01:19Z"),
            "lastHeartbeatRecv" : ISODate("2014-02-18T05:01:18Z"),
            "pingMs" : 0,
            "lastHeartbeatMessage" : "syncing to: 192.168.200.252:27017",
            "syncingTo" : "192.168.200.252:27017"
        }
    ],
    "ok" : 1
}
复制代码

192.168.200.25 的节点已经被移除。

2:查看复制的情况

db.printSlaveReplicationInfo()

复制代码
mmm:PRIMARY> db.printSlaveReplicationInfo()
source:   192.168.200.245:27017
     syncedTo: Tue Feb 18 2014 13:02:35 GMT+0800 (CST)
         = 145 secs ago (0.04hrs)
source:   192.168.200.25:27017
     syncedTo: Tue Feb 18 2014 13:02:35 GMT+0800 (CST)
         = 145 secs ago (0.04hrs)
复制代码

source:从库的ip和端口。

syncedTo:目前的同步情况,以及最后一次同步的时间。

从上面可以看出,在数据库内容不变的情况下他是不同步的,数据库变动就会马上同步。

3:查看副本集的状态

rs.status()

复制代码
mmm:PRIMARY> rs.status()
{
    "set" : "mmm",
    "date" : ISODate("2014-02-18T05:12:28Z"),
    "myState" : 1,
    "members" : [
        {
            "_id" : 1,
            "name" : "192.168.200.252:27017",
            "health" : 1,
            "state" : 1,
            "stateStr" : "PRIMARY",
            "uptime" : 4191,
            "optime" : Timestamp(1392699755, 1),
            "optimeDate" : ISODate("2014-02-18T05:02:35Z"),
            "self" : true
        },
        {
            "_id" : 2,
            "name" : "192.168.200.245:27017",
            "health" : 1,
            "state" : 2,
            "stateStr" : "SECONDARY",
            "uptime" : 679,
            "optime" : Timestamp(1392699755, 1),
            "optimeDate" : ISODate("2014-02-18T05:02:35Z"),
            "lastHeartbeat" : ISODate("2014-02-18T05:12:27Z"),
            "lastHeartbeatRecv" : ISODate("2014-02-18T05:12:27Z"),
            "pingMs" : 0,
            "syncingTo" : "192.168.200.252:27017"
        },
        {
            "_id" : 3,
            "name" : "192.168.200.25:27017",
            "health" : 1,
            "state" : 2,
            "stateStr" : "SECONDARY",
            "uptime" : 593,
            "optime" : Timestamp(1392699755, 1),
            "optimeDate" : ISODate("2014-02-18T05:02:35Z"),
            "lastHeartbeat" : ISODate("2014-02-18T05:12:28Z"),
            "lastHeartbeatRecv" : ISODate("2014-02-18T05:12:28Z"),
            "pingMs" : 0,
            "syncingTo" : "192.168.200.252:27017"
        }
    ],
    "ok" : 1
}
复制代码

4:副本集的配置

rs.conf()/rs.config()

复制代码
mmm:PRIMARY> rs.conf()
{
    "_id" : "mmm",
    "version" : 4,
    "members" : [
        {
            "_id" : 1,
            "host" : "192.168.200.252:27017"
        },
        {
            "_id" : 2,
            "host" : "192.168.200.245:27017"
        },
        {
            "_id" : 3,
            "host" : "192.168.200.25:27017"
        }
    ]
}
复制代码

5:操作Secondary

默认情况下,Secondary是不提供服务的,即不能读和写。会提示:
error: { "$err" : "not master and slaveOk=false", "code" : 13435 }

在特殊情况下需要读的话则需要:
rs.slaveOk() ,只对当前连接有效。

mmm:SECONDARY> db.test.find()
error: { "$err" : "not master and slaveOk=false", "code" : 13435 }
mmm:SECONDARY> rs.slaveOk()
mmm:SECONDARY> db.test.find()
{ "_id" : ObjectId("5302edfa8c9151a5013b978e"), "a" : 1 }

6:更新ing

 

三:测试

1:测试副本集数据复制功能

在Primary(192.168.200.252:27017)上插入数据:

mmm:PRIMARY> for(var i=0;i<10000;i++){db.test.insert({"name":"test"+i,"age":123})}
mmm:PRIMARY> db.test.count()
10001

在Secondary上查看是否已经同步:

mmm:SECONDARY> rs.slaveOk()
mmm:SECONDARY> db.test.count()
10001

数据已经同步。

2:测试副本集故障转移功能

关闭Primary节点,查看其他2个节点的情况:

复制代码
mmm:PRIMARY> rs.status()
{
    "set" : "mmm",
    "date" : ISODate("2014-02-18T05:38:54Z"),
    "myState" : 1,
    "members" : [
        {
            "_id" : 1,
            "name" : "192.168.200.252:27017",
            "health" : 1,
            "state" : 1,
            "stateStr" : "PRIMARY",
            "uptime" : 5777,
            "optime" : Timestamp(1392701576, 2678),
            "optimeDate" : ISODate("2014-02-18T05:32:56Z"),
            "self" : true
        },
        {
            "_id" : 2,
            "name" : "192.168.200.245:27017",
            "health" : 1,
            "state" : 2,
            "stateStr" : "SECONDARY",
            "uptime" : 2265,
            "optime" : Timestamp(1392701576, 2678),
            "optimeDate" : ISODate("2014-02-18T05:32:56Z"),
            "lastHeartbeat" : ISODate("2014-02-18T05:38:54Z"),
            "lastHeartbeatRecv" : ISODate("2014-02-18T05:38:53Z"),
            "pingMs" : 0,
            "syncingTo" : "192.168.200.252:27017"
        },
        {
            "_id" : 3,
            "name" : "192.168.200.25:27017",
            "health" : 1,
            "state" : 2,
            "stateStr" : "SECONDARY",
            "uptime" : 2179,
            "optime" : Timestamp(1392701576, 2678),
            "optimeDate" : ISODate("2014-02-18T05:32:56Z"),
            "lastHeartbeat" : ISODate("2014-02-18T05:38:54Z"),
            "lastHeartbeatRecv" : ISODate("2014-02-18T05:38:53Z"),
            "pingMs" : 0,
            "syncingTo" : "192.168.200.252:27017"
        }
    ],
    "ok" : 1
}

#关闭
mmm:PRIMARY> use admin
switched to db admin
mmm:PRIMARY> db.shutdownServer()

#进入任意一台:
mmm:SECONDARY> rs.status()
{
    "set" : "mmm",
    "date" : ISODate("2014-02-18T05:47:41Z"),
    "myState" : 2,
    "syncingTo" : "192.168.200.25:27017",
    "members" : [
        {
            "_id" : 1,
            "name" : "192.168.200.252:27017",
            "health" : 0,
            "state" : 8,
            "stateStr" : "(not reachable/healthy)",
            "uptime" : 0,
            "optime" : Timestamp(1392701576, 2678),
            "optimeDate" : ISODate("2014-02-18T05:32:56Z"),
            "lastHeartbeat" : ISODate("2014-02-18T05:47:40Z"),
            "lastHeartbeatRecv" : ISODate("2014-02-18T05:45:57Z"),
            "pingMs" : 0
        },
        {
            "_id" : 2,
            "name" : "192.168.200.245:27017",
            "health" : 1,
            "state" : 2,
            "stateStr" : "SECONDARY",
            "uptime" : 5888,
            "optime" : Timestamp(1392701576, 2678),
            "optimeDate" : ISODate("2014-02-18T05:32:56Z"),
            "errmsg" : "syncing to: 192.168.200.25:27017",
            "self" : true
        },
        {
            "_id" : 3,
            "name" : "192.168.200.25:27017",
            "health" : 1,
            "state" : 1,
            "stateStr" : "PRIMARY",
            "uptime" : 2292,
            "optime" : Timestamp(1392701576, 2678),
            "optimeDate" : ISODate("2014-02-18T05:32:56Z"),
            "lastHeartbeat" : ISODate("2014-02-18T05:47:40Z"),
            "lastHeartbeatRecv" : ISODate("2014-02-18T05:47:39Z"),
            "pingMs" : 0,
            "syncingTo" : "192.168.200.252:27017"
        }
    ],
    "ok" : 1
}
复制代码

看到192.168.200.25:27017 已经从 SECONDARY 变成了 PRIMARY。具体的信息可以通过日志文件得知。继续操作:

在新主上插入:

mmm:PRIMARY> for(var i=0;i<10000;i++){db.test.insert({"name":"test"+i,"age":123})}
mmm:PRIMARY> db.test.count()
20001

重启启动之前关闭的192.168.200.252:27017

复制代码
mmm:SECONDARY> rs.status()
{
    "set" : "mmm",
    "date" : ISODate("2014-02-18T05:45:14Z"),
    "myState" : 2,
    "syncingTo" : "192.168.200.245:27017",
    "members" : [
        {
            "_id" : 1,
            "name" : "192.168.200.252:27017",
            "health" : 1,
            "state" : 2,
            "stateStr" : "SECONDARY",
            "uptime" : 12,
            "optime" : Timestamp(1392702168, 8187),
            "optimeDate" : ISODate("2014-02-18T05:42:48Z"),
            "errmsg" : "syncing to: 192.168.200.245:27017",
            "self" : true
        },
        {
            "_id" : 2,
            "name" : "192.168.200.245:27017",
            "health" : 1,
            "state" : 2,
            "stateStr" : "SECONDARY",
            "uptime" : 11,
            "optime" : Timestamp(1392702168, 8187),
            "optimeDate" : ISODate("2014-02-18T05:42:48Z"),
            "lastHeartbeat" : ISODate("2014-02-18T05:45:13Z"),
            "lastHeartbeatRecv" : ISODate("2014-02-18T05:45:12Z"),
            "pingMs" : 0,
            "syncingTo" : "192.168.200.25:27017"
        },
        {
            "_id" : 3,
            "name" : "192.168.200.25:27017",
            "health" : 1,
            "state" : 1,
            "stateStr" : "PRIMARY",
            "uptime" : 9,
            "optime" : Timestamp(1392702168, 8187),
            "optimeDate" : ISODate("2014-02-18T05:42:48Z"),
            "lastHeartbeat" : ISODate("2014-02-18T05:45:13Z"),
            "lastHeartbeatRecv" : ISODate("2014-02-18T05:45:13Z"),
            "pingMs" : 0
        }
    ],
    "ok" : 1
}
复制代码

启动之前的主,发现其变成了SECONDARY,在新主插入的数据,是否已经同步:

mmm:SECONDARY> db.test.count()
Tue Feb 18 13:47:03.634 count failed: { "note" : "from execCommand", "ok" : 0, "errmsg" : "not master" } at src/mongo/shell/query.js:180
mmm:SECONDARY> rs.slaveOk()
mmm:SECONDARY> db.test.count()
20001

已经同步。

注意

所有的Secondary都宕机、或则副本集中只剩下一个节点,则该节点只能为Secondary节点,也就意味着整个集群智能进行读操作而不能进行写操作,当其他的恢复时,之前的primary节点仍然是primary节点。

当某个节点宕机后重新启动该节点会有一段的时间(时间长短视集群的数据量和宕机时间而定)导致整个集群中所有节点都成为secondary而无法进行写操作(如果应用程序没有设置相应的ReadReference也可能不能进行读取操作)。

官方推荐的最小的副本集也应该具备一个primary节点和两个secondary节点。两个节点的副本集不具备真正的故障转移能力。

四:应用

1:手动切换Primary节点到自己给定的节点
上面已经提到过了优先集priority,因为默认的都是1,所以只需要把给定的服务器的priority加到最大即可。让245 成为主节点,操作如下:

复制代码
mmm:PRIMARY> rs.conf() #查看配置
{
    "_id" : "mmm",
    "version" : 6,  #每改变一次集群的配置,副本集的version都会加1。
    "members" : [
        {
            "_id" : 1,
            "host" : "192.168.200.252:27017"
        },
        {
            "_id" : 2,
            "host" : "192.168.200.245:27017"
        },
        {
            "_id" : 3,
            "host" : "192.168.200.25:27017"
        }
    ]
}
mmm:PRIMARY> rs.status() #查看状态
{
    "set" : "mmm",
    "date" : ISODate("2014-02-18T07:25:51Z"),
    "myState" : 1,
    "members" : [
        {
            "_id" : 1,
            "name" : "192.168.200.252:27017",
            "health" : 1,
            "state" : 2,
            "stateStr" : "SECONDARY",
            "uptime" : 47,
            "optime" : Timestamp(1392708304, 1),
            "optimeDate" : ISODate("2014-02-18T07:25:04Z"),
            "lastHeartbeat" : ISODate("2014-02-18T07:25:50Z"),
            "lastHeartbeatRecv" : ISODate("2014-02-18T07:25:50Z"),
            "pingMs" : 0,
            "lastHeartbeatMessage" : "syncing to: 192.168.200.25:27017",
            "syncingTo" : "192.168.200.25:27017"
        },
        {
            "_id" : 2,
            "name" : "192.168.200.245:27017",
            "health" : 1,
            "state" : 2,
            "stateStr" : "SECONDARY",
            "uptime" : 47,
            "optime" : Timestamp(1392708304, 1),
            "optimeDate" : ISODate("2014-02-18T07:25:04Z"),
            "lastHeartbeat" : ISODate("2014-02-18T07:25:50Z"),
            "lastHeartbeatRecv" : ISODate("2014-02-18T07:25:51Z"),
            "pingMs" : 0,
            "lastHeartbeatMessage" : "syncing to: 192.168.200.25:27017",
            "syncingTo" : "192.168.200.25:27017"
        },
        {
            "_id" : 3,
            "name" : "192.168.200.25:27017",
            "health" : 1,
            "state" : 1,
            "stateStr" : "PRIMARY",
            "uptime" : 13019,
            "optime" : Timestamp(1392708304, 1),
            "optimeDate" : ISODate("2014-02-18T07:25:04Z"),
            "self" : true
        }
    ],
    "ok" : 1
}
mmm:PRIMARY> cfg=rs.conf() #
{
    "_id" : "mmm",
    "version" : 4,
    "members" : [
        {
            "_id" : 1,
            "host" : "192.168.200.252:27017"
        },
        {
            "_id" : 2,
            "host" : "192.168.200.245:27017"
        },
        {
            "_id" : 3,
            "host" : "192.168.200.25:27017"
        }
    ]
}
mmm:PRIMARY> cfg.members[1].priority=2  #修改priority
2
mmm:PRIMARY> rs.reconfig(cfg) #重新加载配置文件,强制了副本集进行一次选举,优先级高的成为Primary。在这之间整个集群的所有节点都是secondary

mmm:SECONDARY> rs.status()
{
    "set" : "mmm",
    "date" : ISODate("2014-02-18T07:27:38Z"),
    "myState" : 2,
    "syncingTo" : "192.168.200.245:27017",
    "members" : [
        {
            "_id" : 1,
            "name" : "192.168.200.252:27017",
            "health" : 1,
            "state" : 2,
            "stateStr" : "SECONDARY",
            "uptime" : 71,
            "optime" : Timestamp(1392708387, 1),
            "optimeDate" : ISODate("2014-02-18T07:26:27Z"),
            "lastHeartbeat" : ISODate("2014-02-18T07:27:37Z"),
            "lastHeartbeatRecv" : ISODate("2014-02-18T07:27:38Z"),
            "pingMs" : 0,
            "lastHeartbeatMessage" : "syncing to: 192.168.200.245:27017",
            "syncingTo" : "192.168.200.245:27017"
        },
        {
            "_id" : 2,
            "name" : "192.168.200.245:27017",
            "health" : 1,
            "state" : 1,
            "stateStr" : "PRIMARY",
            "uptime" : 71,
            "optime" : Timestamp(1392708387, 1),
            "optimeDate" : ISODate("2014-02-18T07:26:27Z"),
            "lastHeartbeat" : ISODate("2014-02-18T07:27:37Z"),
            "lastHeartbeatRecv" : ISODate("2014-02-18T07:27:38Z"),
            "pingMs" : 0,
            "syncingTo" : "192.168.200.25:27017"
        },
        {
            "_id" : 3,
            "name" : "192.168.200.25:27017",
            "health" : 1,
            "state" : 2,
            "stateStr" : "SECONDARY",
            "uptime" : 13126,
            "optime" : Timestamp(1392708387, 1),
            "optimeDate" : ISODate("2014-02-18T07:26:27Z"),
            "errmsg" : "syncing to: 192.168.200.245:27017",
            "self" : true
        }
    ],
    "ok" : 1
}
复制代码

这样,给定的245服务器就成为了主节点。

2:添加仲裁节点

把25节点删除,重启。再添加让其为仲裁节点:

rs.addArb("192.168.200.25:27017")
复制代码
mmm:PRIMARY> rs.status()
{
    "set" : "mmm",
    "date" : ISODate("2014-02-18T08:14:36Z"),
    "myState" : 1,
    "members" : [
        {
            "_id" : 1,
            "name" : "192.168.200.252:27017",
            "health" : 1,
            "state" : 2,
            "stateStr" : "SECONDARY",
            "uptime" : 795,
            "optime" : Timestamp(1392711068, 100),
            "optimeDate" : ISODate("2014-02-18T08:11:08Z"),
            "lastHeartbeat" : ISODate("2014-02-18T08:14:35Z"),
            "lastHeartbeatRecv" : ISODate("2014-02-18T08:14:35Z"),
            "pingMs" : 0,
            "syncingTo" : "192.168.200.245:27017"
        },
        {
            "_id" : 2,
            "name" : "192.168.200.245:27017",
            "health" : 1,
            "state" : 1,
            "stateStr" : "PRIMARY",
            "uptime" : 14703,
            "optime" : Timestamp(1392711068, 100),
            "optimeDate" : ISODate("2014-02-18T08:11:08Z"),
            "self" : true
        },
        {
            "_id" : 3,
            "name" : "192.168.200.25:27017",
            "health" : 1,
            "state" : 7,
            "stateStr" : "ARBITER",
            "uptime" : 26,
            "lastHeartbeat" : ISODate("2014-02-18T08:14:34Z"),
            "lastHeartbeatRecv" : ISODate("2014-02-18T08:14:34Z"),
            "pingMs" : 0,
            "syncingTo" : "192.168.200.252:27017"
        }
    ],
    "ok" : 1
}
mmm:PRIMARY> rs.conf()
{
    "_id" : "mmm",
    "version" : 9,
    "members" : [
        {
            "_id" : 1,
            "host" : "192.168.200.252:27017"
        },
        {
            "_id" : 2,
            "host" : "192.168.200.245:27017",
            "priority" : 2
        },
        {
            "_id" : 3,
            "host" : "192.168.200.25:27017",
            "arbiterOnly" : true
        }
    ]
}
复制代码

上面说明已经让25服务器成为仲裁节点。副本集要求参与选举投票(vote)的节点数为奇数,当我们实际环境中因为机器等原因限制只有两个(或偶数)的节点,这时为了实现 Automatic Failover引入另一类节点:仲裁者(arbiter),仲裁者只参与投票不拥有实际的数据,并且不提供任何服务,因此它对物理资源要求不严格。

通过实际测试发现,当整个副本集集群中达到50%的节点(包括仲裁节点)不可用的时候,剩下的节点只能成为secondary节点,整个集群只能读不能 写。比如集群中有1个primary节点,2个secondary节点,加1个arbit节点时:当两个secondary节点挂掉了,那么剩下的原来的 primary节点也只能降级为secondary节点;当集群中有1个primary节点,1个secondary节点和1个arbit节点,这时即使 primary节点挂了,剩下的secondary节点也会自动成为primary节点。因为仲裁节点不复制数据,因此利用仲裁节点可以实现最少的机器开 销达到两个节点热备的效果。

3:添加备份节点

hidden(成员用于支持专用功能):这样设置后此机器在读写中都不可见,并且不会被选举为Primary,但是可以投票,一般用于备份数据。

把25节点删除,重启。再添加让其为hidden节点:

复制代码
mmm:PRIMARY> rs.add({"_id":3,"host":"192.168.200.25:27017","priority":0,"hidden":true})
{ "down" : [ "192.168.200.25:27017" ], "ok" : 1 }
mmm:PRIMARY> rs.conf()
{
    "_id" : "mmm",
    "version" : 17,
    "members" : [
        {
            "_id" : 1,
            "host" : "192.168.200.252:27017"
        },
        {
            "_id" : 2,
            "host" : "192.168.200.245:27017"
        },
        {
            "_id" : 3,
            "host" : "192.168.200.25:27017",
            "priority" : 0,
            "hidden" : true
        }
    ]
}
复制代码

测试其能否参与投票:关闭当前的Primary,查看是否自动转移Primary

复制代码
关闭Primary(252):
mmm:PRIMARY> use admin
switched to db admin
mmm:PRIMARY> db.shutdownServer()

连另一个链接察看:
mmm:PRIMARY> rs.status()
{
    "set" : "mmm",
    "date" : ISODate("2014-02-19T09:11:45Z"),
    "myState" : 1,
    "members" : [
        {
            "_id" : 1,
            "name" : "192.168.200.252:27017",
            "health" : 1,
            "state" : 1,
            "stateStr" :"(not reachable/healthy)",
            "uptime" : 4817,
            "optime" : Timestamp(1392801006, 1),
            "optimeDate" : ISODate("2014-02-19T09:10:06Z"),
            "self" : true
        },
        {
            "_id" : 2,
            "name" : "192.168.200.245:27017",
            "health" : 1,
            "state" : 2,
            "stateStr" : "PRIMARY",
            "uptime" : 401,
            "optime" : Timestamp(1392801006, 1),
            "optimeDate" : ISODate("2014-02-19T09:10:06Z"),
            "lastHeartbeat" : ISODate("2014-02-19T09:11:44Z"),
            "lastHeartbeatRecv" : ISODate("2014-02-19T09:11:43Z"),
            "pingMs" : 0,
            "syncingTo" : "192.168.200.252:27017"
        },
        {
            "_id" : 3,
            "name" : "192.168.200.25:27017",
            "health" : 1,
            "state" : 2,
            "stateStr" : "SECONDARY",
            "uptime" : 99,
            "optime" : Timestamp(1392801006, 1),
            "optimeDate" : ISODate("2014-02-19T09:10:06Z"),
            "lastHeartbeat" : ISODate("2014-02-19T09:11:44Z"),
            "lastHeartbeatRecv" : ISODate("2014-02-19T09:11:43Z"),
            "pingMs" : 0,
            "syncingTo" : "192.168.200.252:27017"
        }
    ],
    "ok" : 1
}
上面说明Primary已经转移,说明hidden具有投票的权利,继续查看是否有数据复制的功能。
#####
mmm:PRIMARY> db.test.count()
20210
mmm:PRIMARY> for(var i=0;i<90;i++){db.test.insert({"name":"test"+i,"age":123})}
mmm:PRIMARY> db.test.count()
20300

Secondady:
mmm:SECONDARY> db.test.count()
Wed Feb 19 17:18:19.469 count failed: { "note" : "from execCommand", "ok" : 0, "errmsg" : "not master" } at src/mongo/shell/query.js:180
mmm:SECONDARY> rs.slaveOk()
mmm:SECONDARY> db.test.count()
20300
上面说明hidden具有数据复制的功能
复制代码

后面大家可以在上面进行备份了,后一篇会介绍如何备份、还原以及一些日常维护需要的操作。

4:添加延迟节点

Delayed(成员用于支持专用功能):可以指定一个时间延迟从primary节点同步数据。主要用于处理误删除数据马上同步到从节点导致的不一致问题。

把25节点删除,重启。再添加让其为Delayed节点:

复制代码
mmm:PRIMARY> rs.add({"_id":3,"host":"192.168.200.25:27017","priority":0,"hidden":true,"slaveDelay":60})  #语法
{ "down" : [ "192.168.200.25:27017" ], "ok" : 1 }

mmm:PRIMARY> rs.conf()
{
    "_id" : "mmm",
    "version" : 19,
    "members" : [
        {
            "_id" : 1,
            "host" : "192.168.200.252:27017"
        },
        {
            "_id" : 2,
            "host" : "192.168.200.245:27017"
        },
        {
            "_id" : 3,
            "host" : "192.168.200.25:27017",
            "priority" : 0,
            "slaveDelay" : 60,   
            "hidden" : true
        }
    ]
}
复制代码

测试:操作Primary,看数据是否60s后同步到delayed节点。

复制代码
mmm:PRIMARY> db.test.count()
20300
mmm:PRIMARY> for(var i=0;i<200;i++){db.test.insert({"name":"test"+i,"age":123})}
mmm:PRIMARY> db.test.count()
20500

Delayed:
mmm:SECONDARY> db.test.count()
20300
#60秒之后
mmm:SECONDARY> db.test.count()
20500
复制代码

上面说明delayed能够成功的把同步操作延迟60秒执行。除了上面的成员之外,还有:

Secondary-Only:不能成为primary节点,只能作为secondary副本节点,防止一些性能不高的节点成为主节点。

Non-Voting:没有选举权的secondary节点,纯粹的备份数据节点。

具体成员信息如下:

成为primary 对客户端可见 参与投票 延迟同步 复制数据
Default
Secondary-Only
Hidden
Delayed
Arbiters
Non-Voting

5:读写分离

MongoDB副本集对读写分离的支持是通过Read Preferences特性进行支持的,这个特性非常复杂和灵活。

应用程序驱动通过read reference来设定如何对副本集进行读取操作,默认的,客户端驱动所有的读操作都是直接访问primary节点的,从而保证了数据的严格一致性。

支持五种的read preference模式官网说明

复制代码
primary
主节点,默认模式,读操作只在主节点,如果主节点不可用,报错或者抛出异常。
primaryPreferred
首选主节点,大多情况下读操作在主节点,如果主节点不可用,如故障转移,读操作在从节点。
secondary
从节点,读操作只在从节点, 如果从节点不可用,报错或者抛出异常。
secondaryPreferred
首选从节点,大多情况下读操作在从节点,特殊情况(如单主节点架构)读操作在主节点。
nearest
最邻近节点,读操作在最邻近的成员,可能是主节点或者从节点,关于最邻近的成员请参考
复制代码

注意:2.2版本之前的MongoDB对Read Preference支持的还不完全,如果客户端驱动采用primaryPreferred实际上读取操作都会被路由到secondary节点。

因为读写分离是通过修改程序的driver的,故这里就不做说明,具体的可以参考这篇文章或则可以在google上查阅。

验证:(Python)

通过python来验证MongoDB ReplSet的特性。

1:主节点断开,看是否影响写入

脚本:

复制代码
#coding:utf-8
import time
from pymongo import ReplicaSetConnection
conn = ReplicaSetConnection("192.168.200.201:27017,192.168.200.202:27017,192.168.200.204:27017", replicaSet="drug",read_preference=2, safe=True)
#打印Primary服务器
#print conn.primary
#打印所有服务器
#print conn.seeds
#打印Secondary服务器
#print conn.secondaries

#print conn.read_preference
#print conn.server_info()
for i in xrange(1000):
    try:
        conn.test.tt.insert({"name":"test" + str(i)})
        time.sleep(1)
        print conn.primary
        print conn.secondaries
    except:
        pass
复制代码

脚本执行打印出的内容:

View Code

体操作如下:

在执行脚本的时候,模拟Primary宕机,再把其开启。看到其从201(Primary)上迁移到202上,201变成了Secondary。查看插入的数据发现其中间有一段数据丢失了。

{ "name" : "GOODODOO15" }
{ "name" : "GOODODOO592" }
{ "name" : "GOODODOO593" }

其实这部分数据是由于在选举过程期间丢失的,要是不允许数据丢失,则把在选举期间的数据放到队列中,等到找到新的Primary,再写入。

上面的脚本可能会出现操作时退出,这要看xrange()里的数量了,所以用一个循环修改(更直观):

复制代码
#coding:utf-8
import time
from pymongo import ReplicaSetConnection
conn = ReplicaSetConnection("192.168.200.201:27017,192.168.200.202:27017,192.168.200.204:27017", replicaSet="drug",read_preference=2, safe=True)

#打印Primary服务器
#print conn.primary
#打印所有服务器
#print conn.seeds
#打印Secondary服务器
#print conn.secondaries

#print conn.read_preference
#print conn.server_info()

while True:
    try:
        for i in xrange(100):
            conn.test.tt.insert({"name":"test" + str(i)})
            print "test" + str(i)
            time.sleep(2)
            print conn.primary
            print conn.secondaries
            print '\n'
    except:
        pass
复制代码

上面的实验证明了:在Primary宕机的时候,程序脚本仍可以写入,不需要人为的去干预。只是期间需要10s左右(选举时间)的时间会出现不可用,进一步说明,写操作时在Primary上进行的。

2:主节点断开,看是否影响读取

脚本:

复制代码
#coding:utf-8
import time
from pymongo import ReplicaSetConnection
conn = ReplicaSetConnection("192.168.200.201:27017,192.168.200.202:27017,192.168.200.204:27017", replicaSet="drug",read_preference=2, safe=True)

#打印Primary服务器
#print conn.primary
#打印所有服务器
#print conn.seeds
#打印Secondary服务器
#print conn.secondaries

#print conn.read_preference
#print conn.server_info()

for i in xrange(1000):
    
    time.sleep(1)
    obj=conn.test.tt.find({},{"_id":0,"name":1}).skip(i).limit(1)
    for item in obj:
        print item.values()
    print conn.primary
    print conn.secondaries
复制代码

脚本执行打印出的内容:

View Code

具体操作如下:

在执行脚本的时候,模拟Primary宕机,再把其开启。看到201(Primary)上迁移到202上,201变成了Secondary,读取数据没有间断。再让Primary宕机,不开启,读取也不受影响。

上面的实验证明了:在Primary宕机的时候,程序脚本仍可以读取,不需要人为的去干预。一进步说明,读取是在Secondary上面。

 

1。冻结其中的一个从节点,使其不参与到与primary 的内部选举工作

  进入客户端,执行(单位:秒)
  rs.freeze(30);
2.对原主节点进行降级
进入客户端,执行下面代码 (单位:秒)
rs.stepDown(15);
3.经过冻结和降级之后查看复制集状态
rs.status();
复制集顺利切换
Tagged as: Comments Off
1Sep/14

浅谈MongoDB中几种不同查询方法

Posted by Nick Xu

 1.find

MongoDB使用find来进行查询.查询就是返回一个集合中文档的子集,子集合的范围从0个文档到整个集合.find的第一个参数

决定了要返回哪些文档.其形式也是一个文档,说明要查询的细节.

空的查询文档{}会匹配集合的全部内容.要是不指定查询文档,默认是{}.

如:db.users.find()返回集合users中的所有内容.

向查询文档中添加键值对,就意味着添加了查询条件.对绝大多数类型来说,整数匹配整数,布尔类型匹配布尔类型,字符串匹配

字符串.

2.指定返回的键

有时并不需要返回文档中的所有键值对返回.可以通过find或findOne的第二个参数来指定要返回的键.这样做能节省传输的

数据量,又能节省客户端解码文档的时间和内存消耗.

  1. db.users.findOne({"name":"refactor"},{"age":1,"sex":1})

只会将键为_id,age,sex的数据返回.

"_id"键总是会被返回.

也可以用第二个参数来剔除查询结果中的某个键值对.

如:

键name不会显示在返回的结果中

  1. db.users.findOne({"name":"refactor"},{"name":0})

只会将键为age,sex的数据返回."_id"键是不会返回的

  1. db.users.findOne({"name":"refactor"},{"age":1,"sex":1,"_id":0})

 3.查询条件

"$lt","$lte","$gt","$gte"分别对应<,<=,>,>=

如:

查询age >=18  <=30

db.users.find({"age":{"$gte":18,"$lte":30}})

向文档增加键birthday

 

  1. db.users.update(
  2. {"name":"refactor"},
  3. {
  4. "$set":
  5. {
  6. "birthday":new Date("1989/10/26")
  7. }
  8. }
  9. )

查询birthday日期是1990-1-1之前的人

  1. db.users.find({"birthday":{"$lt":new Date("1990/01/01")}})

使用"$ne"

查出所有name不等refactor1的文档,注意 文档中不存在键name的文档也会被查出来

  1. db.users.find({"name":{"$ne":"refactor1"}})

使用or查询

MongoDB可以使用"$in","$or"

使用"$in"

查询出pageViews为10000,20000的数据

  1. db.users.find({pageViews:{"$in":[10000,20000]}})

"$in"可以指定不同类型的条件和值,如正在将用户的ID号迁移成用户名的过程中,要做到两者兼顾的查询:

  1. db.users.find({"user_id":{"$in":[12345,"refactor"]}})

这会匹配user_id为12345和"refactor"的文档.

要是 "$in"的数组只有一个值和直接匹配这个值效果是一样的.

 

  1. db.users.find({"pageViews":{"$in":[10000]}})
  2. db.users.find({"pageViews":10000})

使用"$nin"返回与数组中所有条件都不匹配的文档

如 查出所有pageViews不等10000,20000的文档,注意 文档中不存在键pageViews的文档也会被查出来

  1. db.users.find({"pageViews":{"$nin":[10000,20000]}})

"$in"能对单个键进行or查询.

使用"$or"

 

  1. db.users.find(
  2. {
  3. "$or":
  4. [
  5. {"pageViews":{"$in":[10000,20000]}},
  6. {"url":"http://www.cnblogs.com/refactor"}
  7. ]
  8. }
  9. )

这将查询出pageViews是10000,20000或url是http://www.cnblogs.com/refactor的文档.

注意:使用普通的and查询时,要尽量将最苛刻的条件放在前面.

使用"$not"

"$not"可以用在任何条件之上.

如:

 

  1. db.users.find(
  2. {"id_num":{"mod":[5,1]}}
  3. )

这会查询出id_num取模后值为1的文档.

 

  1. db.users.find(
  2. {"id_num":{"$not":{"mod":[1,5]}}}
  3. )

4.条件句的规则

在查询中,"$lt"在内层文档,在更新中"$inc" 是外层文档的键.

条件句是内层文档的键,修改器是外层文档的键.

可对一个键应用多个条件,但一个键不能对应多个更新修改器.

5.特定于类型的查询

null可以匹配自身,而且可以匹配"不存在的"

能查出url 是"http://www.cnblogs.com/refactor",pageViews为null的文档

 

  1. db.users.find({"url":"http://www.cnblogs.com/refactor","pageViews":null})

能查出pageViews为null的文档,不存在键pageViews的也能查出来

 

  1. db.users.find({"pageViews":null})

能查出url 是"http://www.cnblogs.com/refactor",pageViews为null的文档,但不能查出不存在键pageViews的的文档
db.users.find({"url":"http://www.cnblogs.com/refactor","pageViews":{"$in":[null],"$exists":true}})

MongoDB没有"$eq"操作符,但是只有一个元素的"$in"的操作效果是一样的

如果仅仅想要匹配键值为null的文档,既要检查该键的值是否为null,还要通过"$exists"条件判断该键是不是存在.

6.正则表达式

正则表达式能够灵活有效的匹配字符串.

查找所有名包含refact或Refact的用户,使用正则表达式执行忽略大小写的匹配

 

  1. db.users.find({"name":/refact/i})

系统可以接受正则表达式标识(i),但不是一定有.现在匹配了各种大小写形式的refact.

MongoDB可以为前缀型正则表达式(如:/^refactor/)查询创建索引.所以这种类型的查询非常高效.

正则表达式也可以匹配自身

 

  1. db.users.find({"name":/refact/})

可以查出name为/refact/的文档.

7.查询数组

数组很大多数情况下可以这样理解:每一个元素都是整个键的值.

db.users.findOne({"userName":"refactor","emails":"295240648@qq.com"})能匹配到

使用"$all"

如果需要多个元素来匹配数组,就要用"$all"

db.users.insert({"userName":"refactor",emails:["295240648@qq.com","295240648@163.com","295240648@126.com"]})
db.users.insert({"userName":"refactor",emails:["295240648@qq.com","295240648@126.com","295240648@111.com"]})
db.users.insert({"userName":"refactor",emails:["295240648@126.com","295240648@163.com","295240648@111.com"]})

要找到邮箱有"295240648@163.com"又有"295240648@126.com",顺序无关紧要的文档

  1. db.users.find(
  2. {
  3. "emails":
  4. {
  5. "$all":
  6. [
  7. "295240648@163.com",
  8. "295240648@126.com"
  9. ]
  10. }
  11. }
  12. )

要是只对一个元素的数组使用"$all"就和不用"$all"是一样的,如

 

  1. db.users.find({"emails":{"$all":["295240648@126.com"]}})
  2. db.users.find({"emails":"295240648@126.com"})

效果是一样的.

也可以精确的匹配数组

  1. db.users.find({"userName":"refactor",emails:["295240648@qq.com","295240648@163.com","295240648@126.com"]})

若想查询数组指定位置的元素,需要使用key.index语法指定下标

db.users.find({"emails.1":"295240648@163.com"})

使用"$size"

"$size"可以查询指定长度的数组

查询数组长度为3的数组

db.users.find({"emails":{"$size":3}})

常见的查询是数组长度范围的查询."$size"并不能与其他查询子句组合(如:"$gt"),但是这种查询可以通过

在文档中添加一个"size"键的方式来实现.这样每一次向指定数组添加元素的时候,同时增加"size"值.原来这样

的更新:

db.users.update({"$push":{"emails":"295240648@139.com"}})

变成这样的更新:
db.users.update({"$push":{"emails":"295240648@139.com"},"$inc":{"size":1}})

这样就可以这样查询了

db.users.find({"size":{"$gt":3}})

 

使用"$slice"查询

find的第二个参数是可选的,可以指定返回那些键,"$slice"返回数组的一个子集合

返回emails数组的前两个元素

 

  1. db.users.find({"userName":"refactor"},{"emails":{"$slice":2}})

返回emails数组的后两个元素

 

  1. db.users.find({"userName":"refactor"},{"emails":{"$slice":-2}})

返回emails数组的第2个和第11个元素.如果数组不够11个,则返回第2个后面的所有元素

 

  1. db.users.find({"userName":"refactor"},{"emails":{"$slice":[1,10]}})

"$slice"默认将返回文档中的所有键.

Tagged as: , , Comments Off
   
site
site