Mongodb学习手册
启动mongodb服务进程:
MongoDB默认的数据库文件的位置是/data/db,但是它不会自动的生产目录,所以如果采用默认的文件位置的话,我们需要自己先建立该目录,如下:
$ sudo mkdir -p /data/db/
$ sudo chown `id -u` /data/db
接下来可以启动 mongodb的服务了:
$ ./mongod
我们也可以用-dbpath选项来指定自己的数据库位置,如下:
$ ./mongod -dbpath ../../data/db
启动成功以后,会有如下界面显示:
[tsaip@ossesbwas bin]$ Mon May 30 12:17:16 [initandlisten] MongoDB starting :
pid=3662 port=27017 dbpath=../../data/db 64-bit
Mon May 30 12:17:16 [initandlisten] db version v1.8.2-rc2, pdfile version 4.5
Mon May 30 12:17:16 [initandlisten] git version:
373038f53049071fddb5404698c8bebf99e3b51f
Mon May 30 12:17:16 [initandlisten] build sys info: Linux bs-linux64.10gen.cc
2.6.21.7-2.ec2.v1.2.fc8xen #1 SMP Fri Nov 20 17:48:28 EST 2009 x86_64
BOOST_LIB_VERSION=1_41
Mon May 30 12:17:16 [initandlisten] waiting for connections on port 27017
Mon May 30 12:17:16 [websvr] web admin interface listening on port 28017
启动命令常用参数选项说明
mongod 启动数据库进程
--dbpath 指定数据库的目录
--port 指定数据库的端口,默认是27017
--bind_ip 绑定IP
--directoryperdb为每个db创建一个独立子目录
--logpath指定日志存放目录
--logappend指定日志生成方式(追加/覆盖)
--pidfilepath 指定进程文件路径,如果不指定,那么将不产生进程文件
--keyFile 集群模式的关键标识
--cpu 周期性的显示 CPU和IO的利用率
--journal 启用日志
--ipv6 启用IPV6支持
--nssize 指定.ns 文件的大小,单位MB,默认是16M,最大是 2GB
--maxConns 最大的并发连接数 10
--notablescan 不允许进行表扫描
--quota 限制每个数据库的文件个数,默认是 8个
--quotaFiles 每个数据库的文件个数,配合—quota参数
--noprealloc 关闭数据文件的预分配功能
更多的参数选项利用 mongod –help 进行查看启动客户端
服务端进程启动成功以后,就可以启动客户端,跟服务端进行连接,如下:
[tsaip@ossesbwas bin]$ ./mongo
MongoDB shell version: 1.8.2-rc2
connecting to: test
Mon May 30 12:36:18 [initandlisten] connection accepted from
127.0.0.1:59154 #1
>
6. 关闭mongodb服务
> use admin;
switched to db admin
> db.shutdownServer();
Mon May 30 12:41:52 [conn2] terminating, shutdown command received
Mon May 30 12:41:52 dbexit: shutdown called
Mon May 30 12:41:52 [conn2] shutdown: going to close listening sockets...
Mon May 30 12:41:52 [conn2] closing listening socket: 5
Mon May 30 12:41:52 [conn2] closing listening socket: 6
Mon May 30 12:41:52 [conn2] closing listening socket: 7
Mon May 30 12:41:52 [conn2] closing listening socket: 8
Mon May 30 12:41:52 [conn2] removing socket file: /tmp/mongodb-27017.sock
Mon May 30 12:41:52 [conn2] removing socket file: /tmp/mongodb-28017.sock
Mon May 30 12:41:52 [conn2] shutdown: going to flush diaglog...
Mon May 30 12:41:52 [conn2] shutdown: going to close sockets...
Mon May 30 12:41:52 [conn2] shutdown: waiting for fs preallocator...
Mon May 30 12:41:52 [conn2] shutdown: closing all files...
Mon May 30 12:41:52 closeAllFiles() finished
Mon May 30 12:41:52 [conn2] shutdown: removing fs lock...
Mon May 30 12:41:52 dbexit: really exiting now
Mon May 30 12:41:52 DBClientCursor::init call() failed
Mon May 30 12:41:52 query failed : admin.$cmd { shutdown: 1.0 } to: 127.0.0.1
server should be down...
Mon May 30 12:41:52 trying reconnect to 127.0.0.1
Mon May 30 12:41:52 reconnect 127.0.0.1 failed couldn't connect to server
127.0.0.1
Mon May 30 12:41:52 Error: error doing query: unknown shell/collection.js:150
六、 MongoDB 数据类型
MongoDB除了包含这些string, integer, boolean, double, null, array, and
object基本的数据类型外,还包含:date, object id, binary data, regular
expression, and code这些附加的数据类型。
1. Timestamp类型(时间戳)
Timestamp类型从 1.8 版本开始支持,Timestamp 有一个特殊的用法:timestamp类型的字段必须是位于文档的前两位.看下面例子:
//位于第三个字段
> db.coll.insert({_id:1,x:2,y:new Timestamp()});
> db.coll.findOne({_id:1});
{ "_id" : 1, "x" : 2, "y" : { "t" : 0, "i" : 0 } }
//位于第二个字段
> db.coll.insert({_id:2,y:new Timestamp(),x:2});
> db.coll.findOne({_id:2});
{ "_id" : 2, "y" : { "t" : 1306746538000, "i" : 1 }, "x" : 2 }
2. ObjectId类型
在 mongodb 中,几乎每个文档(除了某些 Collection 或者某些Capped Collection)都要求有一个主键:_id,用来唯一标识他们,通常它的值就是 ObjectId 类型。当用户往文档中插入一条新记录的时候,如果没有指定_id属性,那么MongoDB会自动生成一个ObjectId类型的值,保存为_id的值。
_id 的值可以为任何类型,除了数组,在实际应用中,鼓励用户自己定义_id值,但是要保证它的唯一性。如下有两个方案: Sequence Numbers:序列号传统的数据库中,通常用一个递增的序列来提供 MongoDB
中用ObjectId的来代替,我们可以通过如下的函数来获取主键:
function counter(name) {
var ret = db.counters.findAndModify({query:{_id:name},
update:{$inc : {next:1}}, "new":true, upsert:true});
return ret.next;
}
db.users.insert({_id:counter("users"), name:"Sarah C."}) // _id : 1
db.users.insert({_id:counter("users"), name:"Bob D."}) // _id :2
利用UUID
如果用UUID来提供主键,我们的应用需要自己去生成 UUID,考虑到效率,建议把UUID保存为BSON BinData类型,如果用例中对效率要求不是很高,也可以保存为字符串类型。
3. 数据库关联
在 MongoDB 中,通常的关联习惯有两种,一种是简单的手动关联,一种是用DBRef。
简单的手工关联
//查找
> db.post.save({title:'MongoDB Manual',author:'sam'});
> p = db.post.findOne();
{
"_id" : ObjectId("4de36b33282677bdc555a83a"),
"title" : "MongoDB Manual",
"author" : "sam"
}
//关联
> db.authors.findOne({name:p.author});
{
"_id" : ObjectId("4de36c14282677bdc555a83b"),
"name" : "sam",
"age" : 24,
"email" : "[email protected]"
}
利用 DBRef关联
DBRef 关联语法:
{ $ref : <collname>, $id : <idvalue>[, $db : <dbname>] }
例子:
> x = { name : 'Biology' }
{ "name" : "Biology" }
> db.courses.save(x)
> x
{ "name" : "Biology", "_id" :
ObjectId("4b0552b0f0da7d1eb6f126a1") }
> stu = { name : 'Joe', classes : [ new DBRef('courses',
x._id) ] }
// or we could write:
// stu = { name : 'Joe', classes :
[ {$ref:'courses',$id:x._id} ] }
> db.students.save(stu)
> stu
{
"name" : "Joe",
"classes" : [
{
"$ref" : "courses",
"$id" :
ObjectId("4b0552b0f0da7d1eb6f126a1")
}
],
"_id" : ObjectId("4b0552e4f0da7d1eb6f126a2")
}
> stu.classes[0]
{ "$ref" : "courses", "$id" : ObjectId("4b0552b0f0da7d1eb6f126a1") }
> stu.classes[0].fetch()
{ "_id" : ObjectId("4b0552b0f0da7d1eb6f126a1"), "name" : "Biology" }
七、 GridFS文件系统
由于在MongoDB 中,1.7版本之前,BSON 对象的大小只有 4MB 的限制,1.7-1.8版本,大小限制是16MB,将来的版本,这个数值还会提高,不适合存储一些大型文件。但是MongoDB提 GridFS 文件系统,为大型文件的存储提供了解决方案
八、 索引
MongoDB 的索引跟传统数据库的索引相似,一般的如果在传统数据库中需要建立索引的字段,在MongoDB 中也可以建立索引。MongoDB中_id字段默认已经建立了索引,这个索引特殊,并且不可删除,不过Capped Collections 例外。
1. 建立索引
建立索引的函数:ensureIndex()
例子:
>$ db.persons.ensureIndex({name:1});
2. 使用索引
a) 普通索引
>$ db.persons.find({name sam // fast - uses index
>$ db. persons.find({age: 3}); // slow - has to check all
because 'age' isn't indexed
b) 嵌入式索引
>$ db.things.ensureIndex({"address.city": 1})
c) 文档式索引
>$db.factories.insert( { name: "xyz", metro: { city: "New
York", state: "NY" } } );
>$db.factories.ensureIndex( { metro : 1 } );
d) 组合索引
>$db.things.ensureIndex({j:1, name:-1});
e) 唯一索引
>$db.things.ensureIndex({firstname: 1, lastname: 1},
{unique: true});
当一个记录被插入到唯一性索引文档时,缺失的字段会以 null 为默认值被插入文档
例子:
>$db.things.ensureIndex({firstname: 1}, {unique: true});
>$db.things.save({lastname: "Smith"});
//下面这个操作将会失败,因为 firstname 上有唯一性索引,值为 null
>$db.things.save({lastname: "Jones"});
3. 查看索引
> db.persons.getIndexes();
[
{
"name" : "_id_",
"ns" : "test.persons",
"key" : {
"_id" : 1
},
"v" : 0
},
{
"_id" : ObjectId("4de39060282677bdc555a83d"),
"ns" : "test.persons",
"key" : {
"name" : 1
},
"name" : "name_1",
"v" : 0
}
]
4. 删除索引
a) 删除所有索引
db.collection.dropIndexes();
b) 删除单个索引
db.collection.dropIndex({x: 1, y: -1})
c) 用运行命令的方式删除索引
// note: command was "deleteIndexes", not "dropIndexes",
before MongoDB v1.3.2
// remove index with key pattern {y:1} from collection foo
db.runCommand({dropIndexes:'foo', index : {y:1}})
// remove all indexes:
db.runCommand({dropIndexes:'foo', index : '*'})
5. 重建索引
db.myCollection.reIndex()
// same as:
db.runCommand( { reIndex : 'myCollection' } )
这个操作是个加锁操作,并且如果集合很大,这个操作会很耗时。
注:用repair 命令修复数据库的时候,会重建索引
九、 主(Master)/从(slave)数据库同步
需要启动的两个 MongoDB 文档数据库,一个是以主模式启动,另一个属于从模式启动。因此,主服务器进程将创建一个 local.oplog,将通过这个“交易记录”同 Slave服务器中。
1. 建立主/从服务器
主服务器:132.129.31.213:10111(A)
从服务器:132.129.31.213:10112(B)
启动Master数据库服务器:
$./mongod –master -port=10111 -dbpath=/home/tsaip/mongodb/data/10111 -nohttpinterface &
启动Slave数据库服务器:5s 同步一次
$./mongod –slave –source=132.129.31.213:10111 -port=10112 -dbpath=/home/tsaip/mongodb/data/10112 –slavedelay 5 -nohttpinterface &
测试同步结果:
//登录master 数据库服务器
$ ./mongo -host 132.129.31.213 -port 10111
MongoDB shell version: 1.8.2-rc2
connecting to: 132.129.31.213:10111/test
> use test;
switched to db test
> db.user.insert({_id:1,name:'samlee',age:80});
> db.user.find();
{ "_id" : 1, "name" : "samlee", "age" : 80 }
>
//登录slave 数据库服务器
$ ./mongo -host 132.129.31.213 -port 10112
MongoDB shell version: 1.8.2-rc2
connecting to: 132.129.31.213:10112/test
> use test;
switched to db test
> db.user.find();
{ "_id" : 1, "name" : "samlee", "age" : 80 }
>
数据同步成功!!
在slave数据库服务器上执行如下操作:
> db.user.insert({_id:2,name:'Jack',age:23});
not master
>
提:not master,所以slave服务器只可以执行读操作,不可以执行写操作,如下图所示:
2. 相关参数说明
Master
--master #master 模式
--oplogSize arg #size limit (in MB) for op log
Slave
--slave #slave 模式
--source arg #source 指定 master 位置
--only arg #单独指定备份某一 database
--slavedelay arg #指定与Master 延迟时间(秒)
--autoresync #当Slave数据过时后自动重连
3. Slave顶替 Master
如果上面的主服务器A宕了,此时需要用B机器来顶替master服务,步骤如下:
停止B 进程(mongod),删除B 数据目录中的 local.*,以--master模式启动 B
4. 切换Master/Slave 角色
a) 假设已经具备主机A 和从机B,此时想切换它们的角色,步骤如下:(假设A 是健康的)
b) 用fsync命令暂停A上的写操作
c) 确定B 是从机,关闭 B上的服务
d) 清空B 上的local.*文件
e) 用-master选项重启B 服务
f) 在B上执行一次写操作,初始化 oplog,获得一个同步起始点
g) 关闭B 服务,此时B 已经有了信的local.*文件
h) 关闭A 服务,并且用 B上新的local.*文件来代替 A上的 local.*文件(拷贝之前,记得先压缩,因为文件可能很大)
i) 用-master选项重启B 服务
j) 在平时的slave选项上加一个-fastsync选项来重启 A服务如果 A 不是健康的,但是硬件是健康的,那么跳过上面的前两步,并且用 B 上所有文件去替换A 上的文件,重启服务。
5. 更新主服务器位置
假设现有从机启动方式如下:
$ mongod --slave --source 132.129.31.213:10000
此时如果想更换主机的位置,可以通过以下的步骤来完成:
重启mongod服务,不要加-slave 和–source选项:
$ mongod
启动shell,执行如下操作:
> use local
switched to db local
> db.sources.update({host : "132.129.31.213:10000"},
{$set : {host : "132.129.31.213:10001"}})
接着重启从机上的服务:
$ ./mongod --slave --source 132.129.31.213:10001
$ # or
$ ./mongod –slave
十、 MongoDB 分片和集群
1. 简单分片实例
MongoDB的数据分块称为 chunk。每个 chunk 都是Collection中一段连续的数据记录,通常最大尺寸是 200MB,超出则生成新的数据块。
要构建一个 MongoDB Sharding Cluster,需要三种角色:
* 实例,用于存储实际的数据块。
* 实例,存储了整个 Cluster Metadata,其中包括 chunk 信息。
* 实例,前端路由,客户端由此接入,且让整个集群看上去像单一进程数据库。Route 转发请求到实际的目标服务进程,并将多个结果合并回传
给客户端。Route 本身并不存储任何数据和状态,仅在启动时从Config Server 获取信息。Config Server 上的任何变动都会传递给所有的 Route Process。在实际使用中,为了获取高可用、高性能的集群方案,我们会将Shard Server 部署成 Replica Sets,然后用 LVS 部署多个 Route。
我们先构建一个简单的 Sharding Cluster,以熟悉相关的配置。
1) 启动 Shard Server
//server_1
$ ./mongod -shardsvr -port 10000 -dbpath /home/tsaip/mongodb/data/10000 -fork -logpath
/home/tsaip/mongodb/data/logs/null
all output going to: /home/tsaip/mongodb/data/logs/null
forked process: 9347
//server_2
$ ./mongod -shardsvr -port 10011 -dbpath /home/tsaip/mongodb/data/10011 -fork -logpath
/home/tsaip/mongodb/data/logs/null
all output going to: /home/tsaip/mongodb/data/logs/null
forked process: 9383
2) 启动Config Server
$ ./mongod -configsvr -port 20000 -dbpath /home/tsaip/mongodb/data/config -fork -logpath
/home/tsaip/mongodb/data/logs/null
all output going to: /home/tsaip/mongodb/data/logs/null
forked process: 9399
3) 启动Route Process
./mongos -configdb 132.129.31.213:20000 -fork -logpath /home/tsaip/mongodb/data/logs/null
all output going to: /home/tsaip/mongodb/data/logs/null
forked process: 9431
注:可以用 -chunkSize 参数指定分块大小
4) 开始配置
相关命令:
addshard:添加Shard Server, 相关的命令还有listshards和removeshard。
enablesharding:用于设置可以被分布存储的数据库。
shardcollection:用于设置具体被切块的集合名称,且必须指定Shard Key系统会自动创建索引。
注 : Sharded Collection只能有一个unique index,且必须是shard key
$ ./mongo
MongoDB shell version: 1.8.2-rc2
connecting to: test
> use admin;
switched to db admin
> db.runCommand({addshard:'132.129.31.213:10000'});
{ "shardAdded" : "shard0000", "ok" : 1 }
> db.runCommand({addshard:'132.129.31.213:10011'});
{ "shardAdded" : "shard0001", "ok" : 1 }
> db.runCommand({enablesharding:'test'});
{ "ok" : 1 }
> db.runCommand({shardcollection:'test.user',key:{_id:1}});
{ "collectionsharded" : "test.user", "ok" : 1 }
>
5) 管理命令 listshards:列出所有的 Shard Server
> db.runCommand({listshards:1});
{
"shards" : [
{
"_id" : "shard0000",
"host" : "132.129.31.213:10000"
},
{
"_id" : "shard0001",
"host" : "132.129.31.213:10011"
}
],
"ok" : 1
}
移除 shard
> db.runCommand( { removeshard : "localhost:10000" } );
{ msg : "draining started successfully" , state: "started" ,
shard :"localhost:10000" , ok : 1 printShardingStatus:查看 Sharding 信息
> printShardingStatus();
--- Sharding Status ---
sharding version: { "_id" : 1, "version" : 3 }
shards:
{ "_id" : "shard0000", "host" : "132.129.31.213:10000" }
{ "_id" : "shard0001", "host" : "132.129.31.213:10011" }
databases:
{ "_id" : "admin", "partitioned" : false, "primary" : "config" }
{ "_id" : "test", "partitioned" : true, "primary" : "shard0000" }
test.user chunks:
shard0000 1
{ "_id" : { $minKey : 1 } } -->> { "_id" : { $maxKey : 1 } } on : shard0000
{ "t" : 1000, "i" : 0 }
>
db.<collection_name>.stats():查看具体的 Shard 存储信息
> use test;
switched to db test
> db.user.stats();
{
"sharded" : true,
"ns" : "test.user",
"count" : 0,
"size" : 0,
"avgObjSize" : NaN,
"storageSize" : 8192,
"nindexes" : 1,
"nchunks" : 1,
"shards" : {
"shard0000" : {
"ns" : "test.user",
"count" : 0,
"size" : 0,
"storageSize" : 8192,
"numExtents" : 1,
"nindexes" : 1,
"lastExtentSize" : 8192,
"paddingFactor" : 1,
"flags" : 1,
"totalIndexSize" : 8192,
"indexSizes" : {
"_id_" : 8192
},
"ok" : 1
}
},
"ok" : 1
}
>
isdbgrid:用来确认当前是否是Sharding Cluster
> db.runCommand({isdbgrid:1});
{ "isdbgrid" : 1, "hostname" : "ossesbwas", "ok" : 1 }
> db.runCommand({ismaster:1});
{
"ismaster" : true,
"msg" : "isdbgrid",
"maxBsonObjectSize" : 16777216,
"ok" : 1
}
>
2. 高级分片实例
MongoDB Auto-Sharding 解决了海量存储和动态扩容的问题,但离实际生产环境所需的高可靠(high reliability)、高可用(high availability)还有些距离。
解决方案: Shard: 使用 Replica Sets,确保每个数据节点都具有备份、自动容错转移、
自动恢复能力。 Config: 使用 3 个配置服务器,确保元数据完整性(two-phase commit)。 Route: 配合 LVS,实现负载平衡,提 (high performance)。 配置一个 Replica Sets + Sharding 环境
1. 创建所有需要的数据库目录
$ mkdir -p /home/tsaip/mongodb/data/10001
$ mkdir -p /home/tsaip/mongodb/data/10002
$ mkdir -p /home/tsaip/mongodb/data/10003
$ mkdir -p /home/tsaip/mongodb/data/10011
$ mkdir -p /home/tsaip/mongodb/data/10012
$ mkdir -p /home/tsaip/mongodb/data/10013
$ mkdir -p /home/tsaip/mongodb/data/config1
$ mkdir -p /home/tsaip/mongodb/data/config2
$ mkdir -p /home/tsaip/mongodb/data/config3
$ mkdir -p /home/tsaip/mongodb/data/logs
2. 配置 Shard Replica Sets
//第一组sets
//10001
$./mongod --shardsvr --fork --logpath /home/tsaip/mongodb/data/logs/null --dbpath
/home/tsaip/mongodb/data/10001 --port 10001 --nohttpinterface --replSet set1
forked process: 9704
all output going to: /home/tsaip/mongodb/data/logs/null
//10002
$./mongod --shardsvr --fork --logpath /home/tsaip/mongodb/data/logs/null --dbpath
/home/tsaip/mongodb/data/10002 --port 10002 --nohttpinterface --replSet set1
all output going to: /home/tsaip/mongodb/data/logs/null
forked process: 9718
//10003
$./mongod --shardsvr --fork --logpath /home/tsaip/mongodb/data/logs/null --dbpath
/home/tsaip/mongodb/data/10003 --port 10003 --nohttpinterface --replSet set1
forked process: 9732
all output going to: /home/tsaip/mongodb/data/logs/null
$ ./mongo -port 10001
MongoDB shell version: 1.8.2-rc2
connecting to: 127.0.0.1:10001/test
>cfg= {_id:'set1',
members:[
{_id:1,host:'132.129.31.213:10001'},
{_id:2,host:'132.129.31.213:10002'},
{_id:3,host:'132.129.31.213:10003'}]
};
{
"_id" : "set1",
"members" : [
{
"_id" : 1,
"host" : "132.129.31.213:10001"
},
{
"_id" : 2,
"host" : "132.129.31.213:10002"
},
{
"_id" : 3,
"host" : "132.129.31.213:10003"
}
]
}
> rs.initiate(cfg);
{
"info" : "Config now saved locally. Should come online in about a minute.",
"ok" : 1
}
> rs.status();
{
"set" : "set1",
"date" : ISODate("2011-05-31T04:28:50Z"),
"myState" : 1,
"members" : [
{
"_id" : 1,
"name" : "132.129.31.213:10001",
"health" : 1,
"state" : 1,
"stateStr" : "PRIMARY",
"optime" : {
"t" : 1306816113000,
"i" : 1
},
"optimeDate" : ISODate("2011-05-31T04:28:33Z"),
"self" : true
},
{
"_id" : 2,
"name" : "132.129.31.213:10002",
"health" : 1,
"state" : 3,
"stateStr" : "RECOVERING",
"uptime" : 9,
"optime" : {
"t" : 0,
"i" : 0
},
"optimeDate" : ISODate("1970-01-01T00:00:00Z"),
"lastHeartbeat" : ISODate("2011-05-31T04:28:49Z")
},
{
"_id" : 3,
"name" : "132.129.31.213:10003",
"health" : 0,
"state" : 6,
"stateStr" : "(not reachable/healthy)",
"uptime" : 0,
"optime" : {
"t" : 0,
"i" : 0
},
"optimeDate" : ISODate("1970-01-01T00:00:00Z"),
"lastHeartbeat" : ISODate("2011-05-31T04:28:49Z"),
"errmsg" : "still initializing"
}
],
"ok" : 1
}
set1:PRIMARY>
//第二组sets
//10011
[tsaip@ossesbwas bin]$./mongod --shardsvr –fork --logpath
/home/tsaip/mongodb/data/logs/null --dbpath /home/tsaip/mongodb/data/10011 --port 10011
--nohttpinterface --replSet set2
all output going to: /home/tsaip/mongodb/data/logs/null
forked process: 10326
//10012
[tsaip@ossesbwas bin]$./mongod –shardsvr –fork --logpath
/home/tsaip/mongodb/data/logs/null --dbpath /home/tsaip/mongodb/data/10012 --port 10012
--nohttpinterface --replSet set2
all output going to: /home/tsaip/mongodb/data/logs/null
forked process: 10335
//10013
[tsaip@ossesbwas bin]$./mongod –shardsvr –fork --logpath
/home/tsaip/mongodb/data/logs/null --dbpath /home/tsaip/mongodb/data/10013 --port 10013
--nohttpinterface --replSet set2
forked process: 10347
[tsaip@ossesbwas bin]$ ./mongo -port 10011
MongoDB shell version: 1.8.2-rc2
connecting to: 127.0.0.1:10011/test
> cfg =
{_id:'set2',members:[{_id:0,host:'132.129.31.213:10011'},{_id:1,host:'132.129.31.213:10012'},{_i
d:2,host:'132.129.31.213:10013'}]};
{
"_id" : "set2",
"members" : [
{
"_id" : 0,
"host" : "132.129.31.213:10011"
},
{
"_id" : 1,
"host" : "132.129.31.213:10012"
},
{
"_id" : 2,
"host" : "132.129.31.213:10013"
}
]
}
> rs.initiate(cfg);
{
"info" : "Config now saved locally. Should come online in about a minute.",
"ok" : 1
}
set2:STARTUP2>
set2:PRIMARY>
set2:PRIMARY> rs.status();
{
"set" : "set2",
"date" : ISODate("2011-05-31T06:32:07Z"),
"myState" : 1,
"members" : [
{
"_id" : 0,
"name" : "132.129.31.213:10011",
"health" : 1,
"state" : 1,
"stateStr" : "PRIMARY",
"optime" : {
"t" : 1306823510000,
"i" : 1
},
"optimeDate" : ISODate("2011-05-31T06:31:50Z"),
"self" : true
},
{
"_id" : 1,
"name" : "132.129.31.213:10012",
"health" : 1,
"state" : 3,
"stateStr" : "RECOVERING",
"uptime" : 11,
"optime" : {
"t" : 0,
"i" : 0
},
"optimeDate" : ISODate("1970-01-01T00:00:00Z"),
"lastHeartbeat" : ISODate("2011-05-31T06:32:07Z"),
"errmsg" : "."
},
{
"_id" : 2,
"name" : "132.129.31.213:10013",
"health" : 1,
"state" : 3,
"stateStr" : "RECOVERING",
"uptime" : 3,
"optime" : {
"t" : 0,
"i" : 0
},
"optimeDate" : ISODate("1970-01-01T00:00:00Z"),
"lastHeartbeat" : ISODate("2011-05-31T06:32:06Z")
}
],
"ok" : 1
}
3. 启动Config Server
可以只使用1个Config Server,但3个理论上更有保障性。
[tsaip@ossesbwas bin]$ ./mongod --configsvr --fork --logpath
/home/tsaip/mongodb/data/logs/null --dbpath /home/tsaip/mongodb/data/config1 --port
20000 --nohttpinterface --fork --logpath /home/tsaip/mongodb/data/logs/null --dbpath
/home/tsaip/mongodb/data/config1 --port 20000
all output going to: /home/tsaip/mongodb/data/logs/null
forked process: 10460
[tsaip@ossesbwas bin]$ ./mongod --configsvr --fork --logpath
/home/tsaip/mongodb/data/logs/null --dbpath /home/tsaip/mongodb/data/config2 --port
20001 --nohttpinterface
all output going to: /home/tsaip/mongodb/data/logs/null
forked process: 10467
[tsaip@ossesbwas bin]$ ./mongod --configsvr --fork --logpath
/home/tsaip/mongodb/data/logs/null --dbpath /home/tsaip/mongodb/data/config3 --port
20002 --nohttpinterface
all output going to: /home/tsaip/mongodb/data/logs/null
forked process: 10476
注:这个不是 Replica Sets,不需要 --replSet 参数。
4. 启动 Route Server
[tsaip@ossesbwas bin]$ ./mongos --fork --logpath /home/tsaip/mongodb/data/logs/null
--configdb "132.129.31.213:20000,132.129.31.213:20001,132.129.31.213:20002"
all output going to: /home/tsaip/mongodb/data/logs/null
forked process: 10512
[tsaip@ossesbwas bin]$ ps aux | grep mongos | grep -v grep
tsaip 10497 0.0 0.0 155988 2092 ? Sl 14:40 0:00 ./mongos --fork
--logpath /home/tsaip/mongodb/data/logs/null --configdb
132.129.31.213:20000,132.129.31.213:20001,132.129.31.213:20002
注:注意 --configdb 参数
5. 开始配置 Sharding
[tsaip@ossesbwas bin]$ ./mongo
MongoDB shell version: 1.8.2-rc2
connecting to: test
> use admin;
switched to db admin
>
db.runCommand({addshard:'set1/132.129.31.213:10001,132.129.31.213:10002,132.129.31.213:
10003'});
{ "shardAdded" : "set1", "ok" : 1 }
>
db.runCommand({ addshard:'set2/132.129.31.213:10011,132.129.31.213:10012,132.129.31.213:
10013' })
{ "shardAdded" : "set2", "ok" : 1 }
> db.runCommand({ enablesharding:'test' });
{ "ok" : 1 }
> db.runCommand({ shardcollection:'test.user', key:{_id:1} });
{ "collectionsharded" : "test.user", "ok" : 1 }
> db.runCommand({ listshards:1 });
{
"shards" : [
{
"_id" : "set1",
"host" :
"set1/132.129.31.213:10001,132.129.31.213:10002,132.129.31.213:10003"
},
{
"_id" : "set2",
"host" :
"set2/132.129.31.213:10011,132.129.31.213:10012,132.129.31.213:10013"
}
],
"ok" : 1
}
> printShardingStatus();
--- Sharding Status ---
sharding version: { "_id" : 1, "version" : 3 }
shards:
{
"_id" : "set1",
"host" : "set1/132.129.31.213:10001,132.129.31.213:10002,132.129.31.213:10003"
}
{
"_id" : "set2",
"host" : "set2/132.129.31.213:10011,132.129.31.213:10012,132.129.31.213:10013"
}
databases:
{ "_id" : "admin", "partitioned" : false, "primary" : "config" }
{ "_id" : "test", "partitioned" : true, "primary" : "set1" }
test.user chunks:
set1 1
{ "_id" : { $minKey : 1 } } -->> { "_id" : { $maxKey : 1 } } on : set1 { "t" : 1000, "i" : 0 }
>
注:addshard 添加 Replica Sets 的格式。
至此配置结束,测试下:
> use test;
switched to db test
> db.user.insert({_id:10,name:'tom',age:20});
> db.user.insert({_id:11,name:'jim',age:20});
> db.user.insert({_id:12,name:'smith',age:20});
> db.user.find();
{ "_id" : 10, "name" : "tom", "age" : 20 }
{ "_id" : 11, "name" : "jim", "age" : 20 }
{ "_id" : 12, "name" : "smith", "age" : 20 }
十一、 数据库基本操作:增查删改
1. Insert
Mongodb 是面向文档存储的数据库,文档结构形式叫 BSON(类似JSON)。
实例:
//定义文档
>doc = {
"_id" : 1,
"author" : "sam",
"title" : "i love you",
"text" : "this is a test",
"tags" : [
"love",
"test"
],
"comments" : [
{
"author" : "jim",
"comment" : "yes"
},
{
"author" : "tom",
"comment" : "no"
}
]
}
//插入文档
> db.posts.insert(doc);
//查找文档
> db.posts.find({'comments.author':'jim'});
{ "_id" : 1, "author" : "sam", "title" : "i love you", "text" : "this is a test", "tags" :
[ "love", "test" ], "comments" : [
{
"author" : "jim",
"comment" : "yes"
},
{
"author" : "tom",
"comment" : "no"
}
] }
2. Query
Mongodb 最大的功能之一就是它支持动态查询,就跟传统的关系型数据库查询一样,但是它的查询来的更灵活。
1) Query Expression Objects:查询表达式对象
查询表达式文档也是一个 BSON 结构的文档,例如,我们可以用下面的查询语句来查询集合中的所有记录:
db.users.find({})
这里,表达式对象是一个空文档,在查询的时候去去匹配所有的记录。
再看:
db.users.find({'last_name': 'Smith'})
这里,我们将会查询出所有“last_name”属性值为“Smith”的文档记录。
2) 查询选项
除了查询表达式意外,Mongodb 还支持一些额外的参数选项。例如,我们可能仅仅只想返回某
//返回除了age 字段外的所有字段
> db.user.find({},{age:0});
//返回tags=tennis 除了comments 的所有列
db.posts.find( { tags : 'tennis' }, { comments : 0 } );
//返回userid=16 的 name 字段
> db.user.find({userid:16},{name:1});
{ "_id" : 16, "name" : "user16" }
//返回x=john 的所有 z字段
db.things.find( { x : "john" }, { z : 1 } );
注: _id 字段始终都会被返回,哪怕没有明确指定
3) 条件表达式
1) <, <=, >, >=
语法:
// 大于: field > value
db.collection.find({ "field" : { $gt: value } } );
//小于:field < value
db.collection.find({ "field" : { $lt: value } } );
//大于等于: field >= value
db.collection.find({ "field" : { $gte: value } } );
//小于等于:field<=value
db.collection.find({ "field" : { $lte: value } } );
实例:
//大于
> db.user.find({_id:{$gt:20}}).limit(8);
{ "_id" : 21, "name" : "user21", "userid" : 21, "age" : 30 }
{ "_id" : 22, "name" : "user22", "userid" : 22, "age" : 30 }
{ "_id" : 23, "name" : "user23", "userid" : 23, "age" : 30 }
{ "_id" : 24, "name" : "user24", "userid" : 24, "age" : 30 }
{ "_id" : 25, "name" : "user25", "userid" : 25, "age" : 30 }
{ "_id" : 26, "name" : "user26", "userid" : 26, "age" : 30 }
{ "_id" : 27, "name" : "user27", "userid" : 27, "age" : 30 }
{ "_id" : 28, "name" : "user28", "userid" : 28, "age" : 30 }
//大于等于
> db.user.find({_id:{$gte:20}}).limit(8);
{ "_id" : 20, "name" : "user20", "userid" : 20, "age" : 30 }
{ "_id" : 21, "name" : "user21", "userid" : 21, "age" : 30 }
{ "_id" : 22, "name" : "user22", "userid" : 22, "age" : 30 }
{ "_id" : 23, "name" : "user23", "userid" : 23, "age" : 30 }
{ "_id" : 24, "name" : "user24", "userid" : 24, "age" : 30 }
{ "_id" : 25, "name" : "user25", "userid" : 25, "age" : 30 }
{ "_id" : 26, "name" : "user26", "userid" : 26, "age" : 30 }
{ "_id" : 27, "name" : "user27", "userid" : 27, "age" : 30 }
//小于
> db.user.find({_id:{$lt:8}}).limit(8);
{ "_id" : 0, "name" : "user0", "userid" : 0, "age" : 30 }
{ "_id" : 1, "name" : "user1", "userid" : 1, "age" : 30 }
{ "_id" : 2, "name" : "user2", "userid" : 2, "age" : 30 }
{ "_id" : 3, "name" : "user3", "userid" : 3, "age" : 30 }
{ "_id" : 4, "name" : "user4", "userid" : 4, "age" : 30 }
{ "_id" : 5, "name" : "user5", "userid" : 5, "age" : 30 }
{ "_id" : 6, "name" : "user6", "userid" : 6, "age" : 30 }
{ "_id" : 7, "name" : "user7", "userid" : 7, "age" : 30 }
//小于等于
> db.user.find({_id:{$lte:8}}).limit(8);
{ "_id" : 0, "name" : "user0", "userid" : 0, "age" : 30 }
{ "_id" : 1, "name" : "user1", "userid" : 1, "age" : 30 }
{ "_id" : 2, "name" : "user2", "userid" : 2, "age" : 30 }
{ "_id" : 3, "name" : "user3", "userid" : 3, "age" : 30 }
{ "_id" : 4, "name" : "user4", "userid" : 4, "age" : 30 }
{ "_id" : 5, "name" : "user5", "userid" : 5, "age" : 30 }
{ "_id" : 6, "name" : "user6", "userid" : 6, "age" : 30 }
{ "_id" : 7, "name" : "user7", "userid" : 7, "age" : 30 }
//区间查询
> db.user.find({_id:{$gt:5,$lte:10}});
{ "_id" : 6, "name" : "user6", "userid" : 6, "age" : 30 }
{ "_id" : 7, "name" : "user7", "userid" : 7, "age" : 30 }
{ "_id" : 8, "name" : "user8", "userid" : 8, "age" : 30 }
{ "_id" : 9, "name" : "user9", "userid" : 9, "age" : 30 }
{ "_id" : 10, "name" : "user10", "userid" : 10, "age" : 30 }
2) $all
$all 操作类似$in 操作,但是不同的是,$all 操作要求数组里面的值全部被包含在返回的记录里面,如:
> use test;
switched to db test
> db.things.insert({a:[1,2,3]});
> db.things.find();
{ "_id" : ObjectId("4de73360059e7f4bdf907cfe"), "a" : [ 1, 2, 3 ] }
> db.things.find({a:{$all:[2,3]}});
{ "_id" : ObjectId("4de73360059e7f4bdf907cfe"), "a" : [ 1, 2, 3 ] }
> db.things.find({a:{$all:[1,2,3]}});
{ "_id" : ObjectId("4de73360059e7f4bdf907cfe"), "a" : [ 1, 2, 3 ] }
> db.things.find({a:{$all:[1]}});
{ "_id" : ObjectId("4de73360059e7f4bdf907cfe"), "a" : [ 1, 2, 3 ] }
> db.things.find({a:{$all:[1,2,3,4]}});
>
3) $exists
$exists操作检查一个字段是否存在,如:
> for(var i=0;i<1000;i++) db.user.save({_id:i,name:'user'+i,userid:i,age:20});
//包含_id,索引
> db.user.find({_id:{$exists:true}}).limit(5);
{ "_id" : 0, "name" : "user0", "userid" : 0, "age" : 20 }
{ "_id" : 1, "name" : "user1", "userid" : 1, "age" : 20 }
{ "_id" : 2, "name" : "user2", "userid" : 2, "age" : 20 }
{ "_id" : 3, "name" : "user3", "userid" : 3, "age" : 20 }
{ "_id" : 4, "name" : "user4", "userid" : 4, "age" : 20 }
//包含userid
> db.user.find({userid:{$exists:true}}).limit(5);
{ "_id" : 0, "name" : "user0", "userid" : 0, "age" : 20 }
{ "_id" : 1, "name" : "user1", "userid" : 1, "age" : 20 }
{ "_id" : 2, "name" : "user2", "userid" : 2, "age" : 20 }
{ "_id" : 3, "name" : "user3", "userid" : 3, "age" : 20 }
{ "_id" : 4, "name" : "user4", "userid" : 4, "age" : 20 }
//不包含sex字段
> db.user.find({sex:{$exists:true}}).limit(5);
>
注:1.8 版本之前,$exists操作不可用于索引上面
4) $mod
$mod 操作可以让我们简单的进行取模操作,而不需要用到 where 子句,如:
//where 子句
> db.user.find("this._id%10==1").limit(5);
{ "_id" : 1, "name" : "user1", "userid" : 1, "age" : 20 }
{ "_id" : 11, "name" : "user11", "userid" : 11, "age" : 20 }
{ "_id" : 21, "name" : "user21", "userid" : 21, "age" : 20 }
{ "_id" : 31, "name" : "user31", "userid" : 31, "age" : 20 }
{ "_id" : 41, "name" : "user41", "userid" : 41, "age" : 20 }
//$mod 操作
> db.user.find({_id:{$mod:[10,1]}}).limit(5);
{ "_id" : 1, "name" : "user1", "userid" : 1, "age" : 20 }
{ "_id" : 11, "name" : "user11", "userid" : 11, "age" : 20 }
{ "_id" : 21, "name" : "user21", "userid" : 21, "age" : 20 }
{ "_id" : 31, "name" : "user31", "userid" : 31, "age" : 20 }
{ "_id" : 41, "name" : "user41", "userid" : 41, "age" : 20 }
>
5) $ne
$ne 意思是not equal,不等于,不用多说,看例子:
> db.user.find().limit(5);
{ "_id" : 0, "name" : "user0", "userid" : 0, "age" : 20 }
{ "_id" : 1, "name" : "user1", "userid" : 1, "age" : 20 }
{ "_id" : 2, "name" : "user2", "userid" : 2, "age" : 20 }
{ "_id" : 3, "name" : "user3", "userid" : 3, "age" : 20 }
{ "_id" : 4, "name" : "user4", "userid" : 4, "age" : 20 }
> db.user.find({_id:{$ne:0}}).limit(5);
{ "_id" : 1, "name" : "user1", "userid" : 1, "age" : 20 }
{ "_id" : 2, "name" : "user2", "userid" : 2, "age" : 20 }
{ "_id" : 3, "name" : "user3", "userid" : 3, "age" : 20 }
{ "_id" : 4, "name" : "user4", "userid" : 4, "age" : 20 }
{ "_id" : 5, "name" : "user5", "userid" : 5, "age" : 20 }
>
6) $in
$in 操作类似于传统关系数据库中的 IN,看例子:
//数据库中有所有数组对应的记录
> db.user.find({_id:{$in:[2,3,4,5,6]}}).limit(5);
{ "_id" : 2, "name" : "user2", "userid" : 2, "age" : 20 }
{ "_id" : 3, "name" : "user3", "userid" : 3, "age" : 20 }
{ "_id" : 4, "name" : "user4", "userid" : 4, "age" : 20 }
{ "_id" : 5, "name" : "user5", "userid" : 5, "age" : 20 }
{ "_id" : 6, "name" : "user6", "userid" : 6, "age" : 20 }
//因为数据库中没有_id=1111 的记录
> db.user.find({_id:{$in:[2,3,4,5,1111]}}).limit(5);
{ "_id" : 2, "name" : "user2", "userid" : 2, "age" : 20 }
{ "_id" : 3, "name" : "user3", "userid" : 3, "age" : 20 }
{ "_id" : 4, "name" : "user4", "userid" : 4, "age" : 20 }
{ "_id" : 5, "name" : "user5", "userid" : 5, "age" : 20 }
>
7) $nin
$nin 跟$in 操作相反,看例子:
//扣掉_id=1/2/3/4 的记录
> db.user.find({_id:{$nin:[1,2,3,4]}}).limit(5);
{ "_id" : 0, "name" : "user0", "userid" : 0, "age" : 20 }
{ "_id" : 5, "name" : "user5", "userid" : 5, "age" : 20 }
{ "_id" : 6, "name" : "user6", "userid" : 6, "age" : 20 }
{ "_id" : 7, "name" : "user7", "userid" : 7, "age" : 20 }
{ "_id" : 8, "name" : "user8", "userid" : 8, "age" : 20 }
>
8) $nor
$nor 跟$or 相反,不好解释,看例子:
> db.user.find({$nor:[{_id:2},{name:'user3'},{userid:4}]}).limit(5);
{ "_id" : 0, "name" : "user0", "userid" : 0, "age" : 20 }
{ "_id" : 1, "name" : "user1", "userid" : 1, "age" : 20 }
{ "_id" : 5, "name" : "user5", "userid" : 5, "age" : 20 }
{ "_id" : 6, "name" : "user6", "userid" : 6, "age" : 20 }
{ "_id" : 7, "name" : "user7", "userid" : 7, "age" : 20 }
>
可以看到,_id=2,name=user3 和userid=4 的记录都被过滤了
9) $or
> db.user.find({$or:[{_id:2},{name:'user3'},{userid:4}]}).limit(5);
{ "_id" : 2, "name" : "user2", "userid" : 2, "age" : 20 }
{ "_id" : 3, "name" : "user3", "userid" : 3, "age" : 20 }
{ "_id" : 4, "name" : "user4", "userid" : 4, "age" : 20 }
>
可以看到,只有_id=2,name=user3 和userid=4三条记录都选择了
10) $size
$size 操作将会查询数组长度等于输入参数的数组,例子:
> db.things.find();
{ "_id" : ObjectId("4de73360059e7f4bdf907cfe"), "a" : [ 1, 2, 3 ] }
> db.things.find({a:{$size:3}});
{ "_id" : ObjectId("4de73360059e7f4bdf907cfe"), "a" : [ 1, 2, 3 ] }
> db.things.find({a:{$size:2}});
> db.things.find({a:{$size:1}});
11) $where
例子:
db.mycollection.find( { $where : function() { return this.a
== 3 || this.b == 4; } } );
//同上效果
db.mycollection.find( function() { return this.a == 3 ||
this.b == 4; } );
12) $type
$type 将会根据字段的BSON 类型来检索数据,例如:
//返回a是字符串的记录
db.things.find( { a : { $type : 2 } } ); // matches if a is
a string
//返回a是int类型的记录
db.things.find( { a : { $type : 16 } } ); // matches if a is
an int
下表是BSON 数据类型表格映射:
Type Name Type Number
Double 1
String 2
Object 3
Array 4
Binary data 5
Object id 7
Boolean 8
Date 9
Null 10
Regular expression 11
JavaScript code 13
Symbol 14
JavaScript code with scope 15
32-bit integer 16
Timestamp 17
64-bit integer 18
Min key 255
Max key 127
4) 正则表达式
Mongodb 同样支持正则表达式进行检索,如:
//检索name 属性是以u 开头,4 结尾的所有用户
> db.user.find({name:/u.*4$/i}).limit(5);
{ "_id" : 4, "name" : "user4", "userid" : 4, "age" : 20 }
{ "_id" : 14, "name" : "user14", "userid" : 14, "age" : 20 }
{ "_id" : 24, "name" : "user24", "userid" : 24, "age" : 20 }
{ "_id" : 34, "name" : "user34", "userid" : 34, "age" : 20 }
{ "_id" : 44, "name" : "user44", "userid" : 44, "age" : 20 }
>
//同样效果的查询语句
> db.user.find({name:{$regex:'u.*4$',$options:'i'}}).limit(5);
{ "_id" : 4, "name" : "user4", "userid" : 4, "age" : 20 }
{ "_id" : 14, "name" : "user14", "userid" : 14, "age" : 20 }
{ "_id" : 24, "name" : "user24", "userid" : 24, "age" : 20 }
{ "_id" : 34, "name" : "user34", "userid" : 34, "age" : 20 }
{ "_id" : 44, "name" : "user44", "userid" : 44, "age" : 20 }
>
配合其他操作一起使用:
> db.user.find({name:{$regex:'u.*4$',$options:'i',$nin:['user4']}}).limit(5);
{ "_id" : 14, "name" : "user14", "userid" : 14, "age" : 20 }
{ "_id" : 24, "name" : "user24", "userid" : 24, "age" : 20 }
{ "_id" : 34, "name" : "user34", "userid" : 34, "age" : 20 }
{ "_id" : 44, "name" : "user44", "userid" : 44, "age" : 20 }
{ "_id" : 54, "name" : "user54", "userid" : 54, "age" : 20 }
> db.user.find({name:{$regex:'u.*4$',$options:'i',$in:['user4']}}).limit(5);
{ "_id" : 4, "name" : "user4", "userid" : 4, "age" : 20 }
>
注意:/^a/;/^a.*/; /^a.*$/这三个表达式最后的效果一样,但是后两种查询
效率比第一种要低很多,因为后两种表达式会执行扫描整个字符串,然而第一种
扫描到第一个字符就停止了
选项:
-i:忽略大小写
-m:起始符^,结束符$对于每一个新行都起作用
-x:忽略空白字符
-s:这个选项从1.9 版本后才支持,加上它就可以让“.”表示所有字符了,包括
换行符,例如 /a.*b/不匹配"apple\nbanana",但是 /a.*b/s可以
5) 排序
Mongodb 支持排序,例如,按照last_name 属性进行升序排序返回所有文档:
//1表示升序,-1 表示降序
db.users.find({}).sort({last_name: 1});
6) Group
语法:
db.coll.group(
{ cond: {filed:conditions}
, key: {filed: true}
, initial: {count: 0, total_time:0}
, reduce: function(doc, out){ }
, finalize: function(out){}
} );
参数说明:
Key:对那个字段进行 Group
Cond:查询条件
Initial:初始化group 计数器
Reduce:通常做统计操作
Finalize:通常都统计结果进行进一步操作,例如求平均值
Keyf:用一个函数来返回一个替代 KEY 的值
例子:
db.test.group(
{ cond: {"invoked_at.d": {$gte: "2009-11", $lt:
"2009-12"}}
, key: {http_action: true}
, initial: {count: 0, total_time:0}
, reduce: function(doc, out){ out.count++;
out.total_time+=doc.response_time }
, finalize: function(out){ out.avg_time = out.total_time
/ out.count }
} );
[
{
"http_action" : "GET /display/DOCS/Aggregation",
"count" : 1,
"total_time" : 0.05,
"avg_time" : 0.05
}
]
7) Distinct
类似于关系数据库中的 Distinct,如:
//
> db.addresses.insert({"zip-code": 10010})
> db.addresses.insert({"zip-code": 10010})
> db.addresses.insert({"zip-code": 99701})
> // shell helper:
> db.addresses.distinct("zip-code");
[ 10010, 99701 ]
> // running as a command manually:
> db.runCommand( { distinct: 'addresses', key: 'zip-code' } )
{ "values" : [ 10010, 99701 ], "ok"
//
> db.comments.save({"user": {"points": 25}})
> db.comments.save({"user": {"points": 31}})
> db.comments.save({"user": {"points": 25}})
> db.comments.distinct("user.points");
[ 25, 31 ]
8) 分页查询
Mongodb 支持skip 和limit 命令来进行分页查询,例如:
//跳过前10 条记录
> db.user.find().skip(10);
//每页返回8 条记录
> db.user.find().limit(8);
//跳过前20 条记录,并且每页返回 10 条记录
>db.user.find().skip(20).limit(8);
//下面这个语句跟上一条一样,只是表达不够清晰
> db.user.find({},{},8,20);
9) $elemMatch
> t.find( { x : { $elemMatch : { a : 1, b : { $gt : 1 } } } } )
{ "_id" : ObjectId("4b5783300334000000000aa9"),
"x" : [ { "a" : 1, "b" : 3 }, 7, { "b" : 99 }, { "a" : 11 } ]
}
//同样效果
> t.find( { "x.a" : 1, "x.b" : { $gt : 1 } } )
10) slaveOk
当我们在replica set 进行检索操作时,默认的路由会选择 master 机器,当我们需要针对任意的从机进行查询的时候,我们需要开启 slaveOk选项。当我们在没有开启salveOk选项的时候,如果进行此操作会报如下错:
*** not master and slaveok=false
所以我们要进行如下操作:
rs.slaveOk(); // enable querying a secondary
db.users.find(...)
11) Cursors游标及 Cursor Methods
a) 遍历游标
类似传统的关系型数据库,Mongodb 也有游标的概念。可以利用游标对查询结果进行遍历,如:
//利用游标遍历检索结果
> var cur = db.user.find().skip(10).limit(8);
> cur.forEach(function(x){print(tojson(x))});
{ "_id" : 10, "name" : "user10", "userid" : 10, "age" : 30 }
{ "_id" : 11, "name" : "user11", "userid" : 11, "age" : 30 }
{ "_id" : 12, "name" : "user12", "userid" : 12, "age" : 30 }
{ "_id" : 13, "name" : "user13", "userid" : 13, "age" : 30 }
{ "_id" : 14, "name" : "user14", "userid" : 14, "age" : 30 }
{ "_id" : 15, "name" : "user15", "userid" : 15, "age" : 30 }
{ "_id" : 16, "name" : "user16", "userid" : 16, "age" : 30 }
{ "_id" : 17, "name" : "user17", "userid" : 17, "age" : 30 }
>
b) skip()
skip()方法指定查询记录从第几条开始返回,如,我要从第 10 个用户开始返回:
> db.user.find().skip(10);
{ "_id" : 10, "name" : "user10", "userid" : 10, "age" : 30 }
{ "_id" : 11, "name" : "user11", "userid" : 11, "age" : 30 }
{ "_id" : 12, "name" : "user12", "userid" : 12, "age" : 30 }
{ "_id" : 13, "name" : "user13", "userid" : 13, "age" : 30 }
{ "_id" : 14, "name" : "user14", "userid" : 14, "age" : 30 }
{ "_id" : 15, "name" : "user15", "userid" : 15, "age" : 30 }
{ "_id" : 16, "name" : "user16", "userid" : 16, "age" : 30 }
{ "_id" : 17, "name" : "user17", "userid" : 17, "age" : 30 }
{ "_id" : 18, "name" : "user18", "userid" : 18, "age" : 30 }
{ "_id" : 19, "name" : "user19", "userid" : 19, "age" : 30 }
{ "_id" : 20, "name" : "user20", "userid" : 20, "age" : 30 }
{ "_id" : 21, "name" : "user21", "userid" : 21, "age" : 30 }
{ "_id" : 22, "name" : "user22", "userid" : 22, "age" : 30 }
{ "_id" : 23, "name" : "user23", "userid" : 23, "age" : 30 }
{ &q