mongodb学习笔记-tina

[size=small]mongodb
mongodb是面向文档的数据库,不是关系型数据库。基本思路是将原来的“行row” 概念换成更加灵活的“文档”document模型。

关于MongoDB的好处,优点之类的这里就不说了,唯一要讲的一点就是MongoDB中有三元素:数据库,集合,文档,其中“集合”就是对应关系数据库中的“表”,“文档”对应“行”。
---
# ll mongo*
-rwxr-xr-x. 1 root root 2350320 Feb 7 2014 mongo
-rwxr-xr-x. 1 root root 9420888 Feb 7 2014 mongod
-rwxr-xr-x. 1 root root 9379800 Feb 7 2014 mongodump
-rwxr-xr-x. 1 root root 9355616 Feb 7 2014 mongoexport
-rwxr-xr-x. 1 root root 9376320 Feb 7 2014 mongofiles
-rwxr-xr-x. 1 root root 9367808 Feb 7 2014 mongoimport
-rwxr-xr-x. 1 root root 9347168 Feb 7 2014 mongooplog
-rwxr-xr-x. 1 root root 9347832 Feb 7 2014 mongoperf
-rwxr-xr-x. 1 root root 9381248 Feb 7 2014 mongorestore
-rwxr-xr-x. 1 root root 5838832 Feb 7 2014 mongos
-rwxr-xr-x. 1 root root 9334752 Feb 7 2014 mongosniff
-rwxr-xr-x. 1 root root 9386040 Feb 7 2014 mongostat
-rwxr-xr-x. 1 root root 9349336 Feb 7 2014 mongotop
# pwd
/usr/bin
------------------------


1、安装
centos -6.5 自带mongo的包
yum install mongodb

# mongo
MongoDB shell version: 2.4.6
connecting to: test
Wed Sep 17 17:34:21.052 Error: couldn't connect to server 127.0.0.1:27017 at src/mongo/shell/mongo.js:145
exception: connect failed

yum install mongodb-server

2、启动
192.168.12.104
# mongod --dbpath=/mongo
Thu Sep 18 10:05:29.359 [initandlisten] MongoDB starting : pid=22539 port=27017 dbpath=/mongo master=1 64-bit host=testvip1
Thu Sep 18 10:05:29.360 [initandlisten] db version v2.4.6
Thu Sep 18 10:05:29.360 [initandlisten] git version: nogitversion
Thu Sep 18 10:05:29.360 [initandlisten] build info: Linux buildvm-04.phx2.fedoraproject.org 3.12.8-300.fc20.x86_64 #1 SMP Thu Jan 16 01:07:50 UTC 2014 x86_64 BOOST_LIB_VERSION=1_41
Thu Sep 18 10:05:29.360 [initandlisten] allocator: tcmalloc
Thu Sep 18 10:05:29.360 [initandlisten] options: { dbpath: "/mongo", master: true }
Thu Sep 18 10:05:29.366 [initandlisten] journal dir=/mongo/journal
Thu Sep 18 10:05:29.366 [initandlisten] recover : no journal files present, no recovery needed
Thu Sep 18 10:05:29.491 [initandlisten] ******
Thu Sep 18 10:05:29.491 [initandlisten] creating replication oplog of size: 990MB...
Thu Sep 18 10:05:29.492 [FileAllocator] allocating new datafile /mongo/local.1, filling with zeroes...
Thu Sep 18 10:05:29.492 [FileAllocator] creating directory /mongo/_tmp
Thu Sep 18 10:05:29.525 [FileAllocator] done allocating datafile /mongo/local.1, size: 1024MB, took 0.03 secs
Thu Sep 18 10:05:29.525 [initandlisten] ******
Thu Sep 18 10:05:29.535 [websvr] admin web console waiting for connections on port 28017
Thu Sep 18 10:05:29.535 [initandlisten] waiting for connections on port 27017
Thu Sep 18 10:07:10.933 [initandlisten] connection accepted from 127.0.0.1:49504 #1 (1 connection now open)

3、连接
# mongo
MongoDB shell version: 2.4.6
connecting to: test
>


4、基本操作
<1> insert 操作
  好,数据库有了,下一步就是集合,这里就取集合名为“person”,要注意的就是文档是一个json的扩展(Bson)形式。
db.person.insert({"name":"jack","age":"20"})

<2> find 操作
  我们将数据插入后,肯定是要find出来,不然插了也白插,这里要注意两点:
  ① “_id": 这个字段是数据库默认给我们加的GUID,目的就是保证数据的唯一性。
  ② 严格的按照Bson的形式书写文档,不过也没关系,错误提示还是很强大的。

db.person.find({"name":"joe"})

<3> update操作
  update方法的第一个参数为“查找的条件”,第二个参数为“更新的值”,学过C#,相信还是很好理解的。
db.person.update({"name":"joe"},{"name":"joe","age":30})

<4> remove操作
  remove中如果不带参数将删除所有数据,呵呵,很危险的操作,在MongoDB中是一个不可撤回的操作,三思而后行。
db.person.remove()

<5> count
  count是最简单,最容易,也是最常用的聚合工具,它的使用跟我们C#里面的count使用简直一模一样。
db.person.count()

<6> distinct
  这个操作相信大家也是非常熟悉的,指定了谁,谁就不能重复,直接上图。
db.person.distinct("age")


5、建库、建表
> show dbs
local 1.078125GB
> use testdb
switched to db testdb
> show dbs
local 1.078125GB
> use testdb
switched to db testdb
> db.blog.save({title:"new article"})
> show dbs
local 1.078125GB
testdb 0.203125GB

mongos->use ztytest
mongos->db.createCollection("docinfo") --建表
注意上面的use ztytest后如果不建表而离开的话,ztytest库并没有建立成功,mongodb会自动删掉ztytest这个库。


开始进行集群搭建
---
1、理解概念
  mongos:首先我们要了解”片键“的概念,也就是说拆分集合的依据是什么?按照什么键值进行拆分集合....好了,mongos就是一个路由服务器,它会根据管理员设置的“片键”将数据分摊到自己管理的mongod集群,数据和片的对应关系以及相应的配置信息保存在"config服务器"上。
  mongod:一个普通的数据库实例,如果不分片的话,我们会直接连上mongod。
首先我们准备4个mongodb程序,直接在12.104,12.107两条机器上部署,可以做多个文件夹的形式。

2、开启config服务器
 先前也说了,mongos要把mongod之间的配置放到config服务器里面,理所当然首先开启它,我这里就建立27018端口。
mongod --dbpath=/mongo --port 27018
config
192.168.12.107 /mongo --端口27018

3、开启mongos服务器
 这里要注意的是我们开启的是mongos,不是mongod,同时指定下config服务器
mongod --dbpath=/mongos --port=27020 --configdb=192.168.12.107:27018
mongos
192.168.12.107 /mongos --端口27020
# mongos --port 27020 --configdb=127.0.0.1:27018 --不能指定本地localhost或者127.0.0.1,要使用ip
# mongos --port 27020 --configdb=192.168.12.107:27018

4、启动mongod服务器
  对分片来说,也就是要添加片了,端口为:27017,27019。
# mkdir mongo1
# mkdir mongo2
# mongod --dbpath=/mongo1 --port 27017

再开一个窗口
# mongod --dbpath=/mongo2 --port 27019


5、服务配置
 <1> 先前图中也可以看到,我们client直接跟mongos打交道,也就说明我们要连接mongos服务器,然后将27017,27019的mongod交给mongos,添加分片也就是addshard()。
  这里要注意的是,在addshard中,我们也可以添加副本集,这样能达到更高的稳定性。
在12.107 mongos上配置分片信息
# mongo 192.168.12.107:27020/admin
MongoDB shell version: 2.4.6
connecting to: 192.168.12.107:27020/admin
Welcome to the MongoDB shell.
For interactive help, type "help".
For more comprehensive documentation, see
http://docs.mongodb.org/
Questions? Try the support group
http://groups.google.com/group/mongodb-user


addshard 遇到的错误
db.runCommand({addshard:”192.168.12.104:27017″})
{
“ok” : 0,
“errmsg”: “can’t use localhost as a shard since all shards need to communicate. either use all shards and configdbs in localhost or all in actual IPs host: 192.168.12.104:27017 isLocalHost:0″
}

遇到这样的错误是由于某些服务启动在localhost 地址。
经过检查发现route 启动时,读取config 服务是读取的localhost 地址:
将localhost 修改为IP 地址,问题解决。

重新进入:
# mongo 192.168.12.107:27020/admin
MongoDB shell version: 2.4.6
connecting to: 192.168.12.107:27020/admin
mongos>

mongos> db.runCommand({addshard:"192.168.12.104:27019",allowLocal:true })
当路由进程和分片在同一台机器上要指定allowLocal为true,因为MongoDB尽量避免错误的配置,将集群配置在本地,所以这个配置指明当前仅仅是用于开发。

因为路由和分片不在同一台机器上,因此不需要执行true,如下添加分片成功
mongos> db.runCommand({"addshard":"192.168.12.104:27017"});
{ "shardAdded" : "shard0000", "ok" : 1 }
mongos> db.runCommand({"addshard":"192.168.12.104:27019"});
{ "shardAdded" : "shard0001", "ok" : 1 }


<2>mongos不知道该如何切分数据,也就是我们先前所说的片键,在mongodb中设置片键要做两步
  ①:开启数据库分片功能,命令很简单 enablesharding(),这里我就开启test数据库。
  ②:指定集合中分片的片键,这里我就指定为person.name字段。

mongos> show collections; --查看表
mongos> db.runCommand({"drop":"t1"}) --删除表 ---db.t1.drop()一样的效果
{
"nIndexesWas" : 1,
"msg" : "indexes dropped for collection",
"ns" : "testdb.t1",
"ok" : 1
}
mongos> show dbs; --查看数据库
admin (empty)
config 0.1875GB

mongos> use testdb --创建数据库,必须做一些操作,如果是建的空库,就会自动被删除
switched to db testdb
mongos> db.usr.insert({"name":"tom"}); --创建表
mongos> show collections;
system.indexes
usr
mongos> db.usr.find(); --查找表
{ "_id" : ObjectId("541a4f38156a058cc29cce8e"), "name" : "tom" }
mongos> db.usr.insert({"name":"tom","id":1,"address":"China","phone":"15926492390","sex":"M"})--再插入一条
mongos> db.usr.find(); --结果显示两条
{ "_id" : ObjectId("541a4f38156a058cc29cce8e"), "name" : "tom" }
{ "_id" : ObjectId("541a4fcd156a058cc29cce8f"), "name" : "tom", "id" : 1, "address" : "China", "phone" : "15926492390", "sex" : "M" }

mongos> db.runCommand({"enablesharding":"testdb"})
{ "ok" : 0, "errmsg" : "access denied - use admin db" } --要使用admin数据库进行配置

mongos> use admin
switched to db admin
mongos> db.runCommand({"enablesharding":"testdb"}) --切换到admin果然配置成功
{ "ok" : 1 }

表进行分片
mongos> db.runCommand({"shardcollection":"testdb.usr","key":{"id":1}})
{
"proposedKey" : {
"id" : 1
},
"curIndexes" : [
{
"v" : 1,
"key" : {
"_id" : 1
},
"ns" : "testdb.usr",
"name" : "_id_"
}
],
"ok" : 0,
"errmsg" : "please create an index that starts with the shard key before sharding." --提示需要先建索引
}

原来判断分支是查找是否有可用的索引存在,当无可用的索引,并且表不为空时,就会出现这个错误信息。
好了找到根源了,现在解决问题:在分片key上个建立索引
use testdb
db.usr.ensureIndex({"id":1})
然后对表进行分片
mongos> use admin
switched to db admin
mongos> db.runCommand({"shardcollection":"testdb.usr","key":{"id":1}})
{
"ok" : 0,
"errmsg" : "found missing value in key { id: null } for doc: { _id: ObjectId('541a4f38156a058cc29cce8e'), name: \"tom\" }"--最开始插入的一条有问题,没有id这个字段
}
修改这条记录:
mongos> db.usr.update({"name":"tom"},{"name":"tina","id":2,"address":"US","phone":"18707551657","sex":"F"})
mongos> db.usr.find();
{ "_id" : ObjectId("541a4fcd156a058cc29cce8f"), "name" : "tom", "id" : 1, "address" : "China", "phone" : "15926492390", "sex" : "M" }
{ "_id" : ObjectId("541a4f38156a058cc29cce8e"), "name" : "tina", "id" : 2, "address" : "US", "phone" : "18707551657", "sex" : "F" }
结果显示:
mongos> db.runCommand({"shardcollection":"testdb.usr","key":{"id":1}})
{ "collectionsharded" : "testdb.usr", "ok" : 1 } --创建成功,以id为分片的主键

6、 查看效果
  好了,至此我们的分片操作全部结束,接下来我们通过mongos向mongodb插入多条记录,然后通过printShardingStatus命令查看mongodb的数据分片情况。

  这里主要看三点信息:
  ① shards: 我们清楚的看到已经别分为两个片了,shard0000和shard0001。
  ② databases: 这里有个partitioned字段表示是否分区,这里清楚的看到已经分区。
  ③ chunks: 集合

mongos> use testdb
switched to db testdb
mongos> db.printShardingStatus()
--- Sharding Status ---
sharding version: {
"_id" : 1,
"version" : 3,
"minCompatibleVersion" : 3,
"currentVersion" : 4,
"clusterId" : ObjectId("541a47a9124d847f09e99204")
}
shards:
{ "_id" : "shard0000", "host" : "192.168.12.104:27017" }
{ "_id" : "shard0001", "host" : "192.168.12.104:27019" }
databases:
{ "_id" : "admin", "partitioned" : false, "primary" : "config" }
{ "_id" : "testdb", "partitioned" : true, "primary" : "shard0000" }
testdb.usr
shard key: { "id" : 1 }
chunks:
shard0000 1
{ "id" : { "$minKey" : 1 } } -->> { "id" : { "$maxKey" : 1 } } on : shard0000 Timestamp(1, 0)

--插入1000条数据
mongos> for (var i=0;i<1000;i++){db.usr.insert({"name":"tina"+i,"id":3+i,"address":"China","phone":15926492390+i,"sex":"M"})}


--在路由mongo上连接单个分片
# mongo 192.168.12.104:27017
MongoDB shell version: 2.4.6
connecting to: 192.168.12.104:27017/test
> use testdb
switched to db testdb
> show collections
system.indexes
usr
> db.usr.find()
{ "_id" : ObjectId("541a4fcd156a058cc29cce8f"), "name" : "tom", "id" : 1, "address" : "China", "phone" : "15926492390", "sex" : "M" }
{ "_id" : ObjectId("541a4f38156a058cc29cce8e"), "name" : "tina", "id" : 2, "address" : "US", "phone" : "18707551657", "sex" : "F" }
{ "_id" : ObjectId("541a5455156a058cc29cce91"), "id" : 3, "name" : "tina0", "address" : "China", "phone" : 15926492390, "sex" : "M" }
Type "it" for more
> db.usr.status()
Thu Sep 18 14:54:19.410 TypeError: Property 'status' of object testdb.usr is not a function
> db.usr.status
testdb.usr.status
> db.usr.status()
Thu Sep 18 14:54:31.494 TypeError: Property 'status' of object testdb.usr is not a function
> db.usr.count() --以id做key,结果全部分到一个分片上了。
35407

-------------------

mongod --dbpath=/mongo3 --port 27023
--添加分片
mongos> db.runCommand({addshard:"192.168.12.104:27023" }) --use admin,在表有数据的情况下添加成功。

--删除分片
mongos> db.runCommand({"removeshard":"192.168.12.104:27023"})
shards:
{ "_id" : "shard0000", "draining" : true, "host" : "192.168.12.104:27017" } --删除后,会出现迁移数据的现象


删除文档:
db.usr.remove()
会删除usr集合中所有的文档,但不会删除集合本身,原有的索引也会保留。
可以条件删除
db.usr.remove({"name":"tina"})
删除数据是永久性的,不能撤销,也不能恢复。

默认情况下,mongodb监听的27017端口
mongod还会启动一个非常基本的http服务器,监听数字比主端口号高1000的端口,也就是28017,这意味着,你可以通过
http://localhost:28017来获取数据库的管理信息。


mongodb shell
mongodb自带一个javascript shell,可以从命令行与mongodb实例交互。
mongo
>


shell查看操作数据会用到4个基本操作:创建、读取、更新和删除(CRUD)
1、创建
mongos> post={"title":"my post","content":"this is tina's blog","date":new Date()} --定义变量
{
"title" : "my post",
"content" : "this is tina's blog",
"date" : ISODate("2014-09-22T10:39:47.987Z")
}
mongos> db.blog.insert(post) --将变量插入表blog
mongos> db.blog.find()
{ "_id" : ObjectId("541bceb83351616d81bb0398"), "name" : "a", "date" : "Fri Sep 19 2014 14:35:36 GMT+0800 (CST)" }
{ "_id" : ObjectId("541bcebf3351616d81bb0399"), "name" : "a", "date" : ISODate("2014-09-19T06:35:43.627Z") }
{ "_id" : ObjectId("541bcec53351616d81bb039a"), "name" : "a", "date" : ISODate("2014-09-19T06:35:49.891Z") }
{ "_id" : ObjectId("541ffca6821e918f723f8dc0"), "title" : "my post", "content" : "this is tina's blog", "date" : ISODate("2014-09-22T10:39:47.987Z") }


2、读取
>db.usr.findOne() --只查看一个文档,注意大写O

mongos> db.log_content.find({"DATE":"Mar 24 16:19:00"}) --特定条件的查询


3、更新
>post.comments=[]
>db.blog.update({title:"my blog post"},post)

4、删除
remove 从数据库中永久性的删除文档。可以接受一个文档以指定限定条件。
>db.blog.remove({title:"my blog post"})
会删除集合中所有的文档,但不会删除集合本身,原来的索引也会保留。
>db.blog.list.remove({"title":"my blog post"})
删除数据是永久性的,不能撤销,也不能恢复。
mongos> db.games.remove();
mongos> show collections;
address
blog
games ---集合还在
system.indexes
testa
usr

帮助
mongos> help
db.help() help on db methods
db.mycoll.help() help on collection methods
sh.help() sharding helpers
rs.help() replica set helpers
help admin administrative help
help connect connecting to a db help
help keys key shortcuts
help misc misc things to know
help mr mapreduce

show dbs show database names
show collections show collections in current database
show users show users in current database
show profile show most recent system.profile entries with time >= 1ms
show logs show the accessible logger names
show log [name] prints out the last segment of log in memory, 'global' is default
use set current database
db.foo.find() list objects in collection foo
db.foo.find( { a : 1 } ) list objects in foo where a == 1
it result of the last line evaluated; use to further iterate
DBQuery.shellBatchSize = x set default number of items to display on shell
exit quit the mongo shell

了解函数功能:输入的时候不要输括号

----mongodb介绍
mongo 不但区分类型,也区分大小
do.help() --获得帮助,可以查到左右以db.开头的命令

mongodb的数据类型
null
null用于表示空值或者不存在的字段。
{"x": null}

布尔
布尔类型有两个值 true和false
{"x": true}

64位浮点数
shell中的数字都是这种类型
{"x": 3.14}
这个也是浮点数
{"x":3}

字符串
UTF-8字符串都可以表示
{"x":"snooby"

shell中符号类型、32、64位整数都不支持,要么会换成字符串,要么会换成浮点数
要是插入的64位整数不能精确地作为双精度数显示,shell会添加两个键,top和bottom,分别表示搞32位和低32位。

mongos> db.testa.insert({"id":9223372036854775807})
mongos> db.testa.find()
{ "_id" : ObjectId("5420ddf92a3a0d10c400578b"), "id" : 9223372036854776000 }--居然不精确
mongos> db.testa.insert({"id":9223372036854775807256158})
mongos> db.testa.find()
{ "_id" : ObjectId("5420ddf92a3a0d10c400578b"), "id" : 9223372036854776000 }
{ "_id" : ObjectId("5420de4e2a3a0d10c400578c"), "id" : 9.223372036854776e+24 }

对象id
对象id是文档的12字节的唯一id
{"x":ObjectId()}

日期
日期类型存储的是从标准纪元开始的毫秒数,不存储时区
{"x":new Date()}
要是忘记使用Date构造函数,最后会导致日期和字符串混淆,字符串和日期不能互相匹配,会给很多操作带来问题。


文档中可以包含正则表达式,采用javascript的正则表达式语法
{"x":/foobar/i}

文档中还可以包含java代码
{"x":function{}{/*...*/}}

数组:
值的集合或者列表可以表示成数组:
{"x":["a","b","c"]}


mongdb时间类型有Date(),new Date(),ISODate()
> Date() --显示当前时间
Wed Mar 20 2013 10:50:49 GMT+0800 (CST)
> new Date() --构建一个格林尼治时间 可以看到正好和Date()相差8小时,我们是+8时区,也就是时差相差8,所以+8小时就是系统当前时间
ISODate("2013-03-20T02:50:54.607Z")
> ISODate() --有new无new格式一样,也是格林尼治时间
ISODate("2013-03-20T02:51:02.949Z")

可以通过typeof查看其类型:
> typeof Date()
string
> typeof new Date()
object
> typeof ISODate()
object
算时间戳按时间查询:
>ISODate("2013-03-20T02:36:21.289Z").valueof
1363746981289
>db.tb1.find({time:{$gt:new Date(1363746981289)}})
支持多种格式:
> db.tb1.insert({mydate:ISODate("2012-11-02 07:58:51")})

> db.tb1.insert({mydate:ISODate("20121102 07:58:51")})
> db.tb1.insert({mydate:ISODate("20121102")})

也可以直接操作:

>db.tb1.find({"mydate":{$gt:ISODate("2012-10-02T07:58:51Z")}})和db.tb1.find({"mydate":{$gt:new Date("2012-10-02T07:58:51Z")}})相同


在mongodb中使用Date()创建的日期类型是一个字符串的类型,这个字符串中包含时区等数据 。使用new Date()创建的将会是ISODate类型的一个日期对象。
同样我们可以使用ISODate()方式去创建日期对象,得到的结果是使用new Date相同的。下面是我在电脑上测试的:
mongos> db.blog.insert({"name":"a",date:Date()})
mongos> db.blog.insert({"name":"a",date:new Date()})
mongos> db.blog.insert({"name":"a",date:ISODate()})
mongos> db.blog.find()
{ "_id" : ObjectId("541bceb83351616d81bb0398"), "name" : "a", "date" : "Fri Sep 19 2014 14:35:36 GMT+0800 (CST)" }
{ "_id" : ObjectId("541bcebf3351616d81bb0399"), "name" : "a", "date" : ISODate("2014-09-19T06:35:43.627Z") }
{ "_id" : ObjectId("541bcec53351616d81bb039a"), "name" : "a", "date" : ISODate("2014-09-19T06:35:49.891Z") }


内嵌文档
就是把整个mongodb文档作为另一个文档键的一个值,这样数据组织得更加自然些,不用非得存成扁平结构的。
mongos> db.address.insert({"name":"john","address":{"street":"123 street","city":"anytown","state":"ny"}})
mongos> db.address.findOne()
{
"_id" : ObjectId("5420dfd52a3a0d10c400578d"),
"name" : "john",
"address" : {
"street" : "123 street",
"city" : "anytown",
"state" : "ny"
}
}


插入文档
大于4mb的文档是不能存入数据库的。主要是避免不良的模式设计,保证稳定的性能。
要查看doc文档转成bson的大小,在shell中运行object.bsonsize(doc)即可。


mongos> ava1 = db.usr.findOne({"name":"ava1"}); --定义变量
{
"_id" : ObjectId("541bd1573351616d81bb0784"),
"name" : "ava1",
"id" : 1004,
"sex" : "M",
"address" : "yichang",
"age" : 18,
"hobby" : "playing badminton",
"birthday" : ISODate("2014-09-19T06:46:47.097Z")
}
mongos> ava1.age++; --指定变量中某一个键值改变
18


$set 修改器入门
$set用来指定一个键的值,如果这个键不存在,则创建它。这对更新模式或者增加用户定义键来说非常方便。

可以指定修改某一条,新增字段,但一次只能修改一条。
mongos> db.usr.count()
1211002
mongos> db.usr.update({"name":"tina"},{"$set":{"phone":18706541269}})
mongos> db.usr.find({"name":"tina"})
{ "_id" : ObjectId("541b9c382876106859f91f2c"), "address" : "wuhan", "age" : 26, "birthday" : ISODate("2014-09-19T06:24:49.420Z"), "hobby" : "singing songs", "id" : 1, "name" : "tina", "phone" : 18706541269, "sex" : "M" }
---新增字段的位置根据你写的位置而定
mongos> db.usr.update({"name":"tina"},{"$unset":{"phone":18706541269}}) --unset删除


$inc
修改器用来增加已有键的值,或者在键不存在的时候创建一个键。
mongos> db.games.insert({"game":"pingpang","user":"joe"})
mongos> db.games.update({"game":"pingpang"},{"$inc":{"score":50}})
mongos> db.games.findOne()
{
"_id" : ObjectId("541be4948bdc31a37d19d155"),
"game" : "pingpang",
"score" : 50,
"user" : "joe"
}

mongos> db.games.update({"game":"pingpang"},{"$inc":{"score":10000}})
mongos> db.games.find()
{ "_id" : ObjectId("541be4948bdc31a37d19d155"), "game" : "pingpang", "score" : 10050, "user" : "joe" }

数据类型不同的,查询的时候也要注意,不然会查不出结果
mongos> db.usr.find({"name":"ava100","age":"18"})
mongos> db.usr.find({"name":"ava100","age":18}) ---age是整型

mongodb的插入、删除、更新都是瞬间完成的,这是因为它们都不需要等待数据库响应。
这个特点的有点很明显,速度快,这些操作都会非常快的执行。它只会收客户端发送的速度和网络速度的制约。
通常会工作得很好,但当服务器崩溃了,网络断了等情况下,在没有服务器的情况下,客户端还是会发送写操作到服务器的,
完全不理会到底有没有服务器。

$push数组修改器

upsert 特殊的更新

getLastError命令,键n就是显示更新了多少文档。
mongos> db.usr.update({"name":"ava100","address":"yichang","age":18},{"name":"ava100","address":"yichangdong","age":50})
--更新的时候,一定要带上key键值,不然它找不到
mongos> db.runCommand({getLastError:1})
{
"singleShard" : "192.168.12.104:27019",
"updatedExisting" : true,
"n" : 1, --更新了一个文档
"connectionId" : 5,
"err" : null,
"ok" : 1
}

用getLastError仅能获得有限的信息,并不能返回已更新的文档。
这个可以通过findAndModify命令来做到。 --速度比较慢,耗时相当于一次查找,一次更新和一次getLastError


查询条件:
$lt $lte $gt $gte 就是全部的比较操作符,分别对应< <= > >=
mongos> db.usr.find({"age":{"$gte":1,"$lte":60}}) 1-60岁(含)的人

$ne 不相等
mongos>db.usr.find({"name":{"$ne":"tina"}})

$in查询一个键的多个值
db.usr.find({"name":{"$in":["tina","ava0","ava3"]}})

$nin 将返回与数组中所有条件都不匹配的文档
db.usr.find({"name":{"nin":["tina","ava0","ava3"]}})

$or 或
db.usr.find({"$or":[{"name":"tina"},{"name":"ava0"}]})

$not 可用在任何其他条件之上
mongos> db.usr.find({"id":{"$mod":[5,1]}}) --除以5余数为1的
mongos> db.usr.find({"id":{"$not:{$mod":[5,1]}}})

$exists
db.usr.find({"z":{"$in":[null],"$exists":true}})

正则表达式
能够灵活有效地匹配字符串
>db.usr.find({"name":/joe/i}) --忽略大小写

查询数组
$all 通过多个元素来匹配数组
$size 查询指定长度的数组
$slice 返回数组的一个子集合


排序:
limit\skip\sort
mongos> db.usr.find().limit(10)
mongos> db.usr.find().skip(10)
mongos> db.usr.find().limit(50).sort({name:1,age:-1}) --按照名字升序,年纪倒序
mongos> db.usr.find().limit(40).sort({"id":-1})

随机选取文档
random

高级查询
$maxscan:integer ---查询最多扫描的文档数量
$min:document --查询的开始条件
$max:document$hint:document ---查询的结束条件
$hint:document ---指定服务器使用哪个索引进行查询
$explain:boolean ---获取查询执行的细节,而并非真正执行查询
$snapshot:boolean

db.usr.find({"id":1005}).explain() ---在id字段创建了索引db.usr.ensureIndex({"id":1})
"cursor" : "BasicCursor", --这个结果说明没有使用索引
"n" : 1, --返回文档的数量
"nChunkSkips" : 0,
"nYields" : 0,
"nscanned" : 1211002, ---代表数据库查找了多少个文档,尽可能地接近返回结果的数量
"nscannedAllPlans" : 1211002,
"nscannedObjects" : 1211002,
"nscannedObjectsAllPlans" : 1211002,
"millisShardTotal" : 1205,
"millisShardAvg" : 401,
"numQueries" : 3,
"numShards" : 3,
"millis" : 603 --毫秒数,0是非常理想的成绩

"cursor" : "BtreeCursor age_1", --在age字段建索引,发现查询走了索引。

创建索引
db.usr.ensureIndex({"name":1})
db.usr.ensureIndex({"name":1,"date" -1})

对于同一个集合,同样的索引只需要创建一次,反复创建是徒劳的。
创建索引的缺点就是每次插入,更新和删除时都会产生额外的开销。
要尽可能地少创建索引,每个集合默认的最大索引个数为64个。

为排序创建索引:如果对没有索引的键调用sort,mongodb需要将所有数据提取到内存来排序。

唯一索引
可以确保集合的每一个文档的指定键都有唯一值。如果想保证文档的"username"键都有不一样的值,创建一个唯一索引就好了。
db.people.ensureIndex({"username":1},{"unique":true})

一定要牢记,默认情况下,insert并不检查文档是否插入过了,所以为了避免插入的文档中包含与唯一键重复的值,可能要用安全插入才能满足要求。这样,在插入这样的文档时会看到存在重复键错误的提示。

消除重复
--鲁莽的做法
db.people.ensureIndex({"username":1,{"unique":true,"dropDups":true})

索引管理:
索引的元信息存储在每个数据库的system.indexes集合中,这是一个保留集合,不能对其插入或者删除文档。
操作只能通过ensureIndex或者dropIndexes进行
system.indexes集合包含每个索引的详细信息,同时system.namespaces集合也含有索引的名字。

注意:集合名和索引名加起来不能超过127字节

修改索引
db.people.ensureIndex({"username":1},{"background":true}) --可以让建索引这个过程在后台完成。同时正常处理请求。要是不包括background这个选项,数据库会阻塞建立索引期间的所有请求。阻塞会让索引建立得更快。

删除索引
db.runCommand({"dropIndexes":"foo","index":"alphabet"})
db.runCommand({"dropIndexes":"foo","index":"*"}) --删除所有索引

地理空间索引---找到离当前位置最近的n个场所
ensureIndex 2d
db.map.ensureIndex({"gps":"2d"})
db.map.ensureIndex({"gps":"2d"},{"min":-1000,"max":1000})
gps键的值必须是某种形式的一对值,一个包含两个元素的数组或是包含两个键的内嵌文档,下面这些都是有效的。
{"gps":[0,100]}
{"gps":{"x":-30,"y":30}}

$near
db.map.find({"gps":{"$near":[40,-73]}})
这样离点(40,-73)由近及远的方式将map集合的所有文档都返回,在没有指定limit时,默认是100个文档
db.runCommand({geoNear:"map",near:[40,-73],num:10});
geoNear还会返回每个文档到查询点的距离。

count --计算查询结果的数量
mongos> db.usr.count({"name":"ava300"})

distinct --用来找出给定键的所有不同的值。使用时必须指定集合和键
mongos> db.runCommand({"distinct":"usr","key":"age"})
{ "values" : [ 18, 25, 26, 30, 35, 50 ], "ok" : 1 }

group
mongos> db.runCommand({"group":{"ns":"usr","key":"age"}}) --分片的表,不能进行group
{ "ok" : 0, "errmsg" : "can't do command: group on sharded collection" }

>db.runCommand({"group":{"ns":"stocks","key":"day","initial":{"time":0},
"$reduce":function(doc,prev){
if(doc.time>prev.time){
prev.price=doc.price;
prev.time=doc.time;}}}})


--貌似mongodb字段随便存储都行,不需要对应都可以
mongos> db.blog.find()
{ "_id" : ObjectId("541bceb83351616d81bb0398"), "name" : "a", "date" : "Fri Sep 19 2014 14:35:36 GMT+0800 (CST)" }
{ "_id" : ObjectId("541bcebf3351616d81bb0399"), "name" : "a", "date" : ISODate("2014-09-19T06:35:43.627Z") }
{ "_id" : ObjectId("541bcec53351616d81bb039a"), "name" : "a", "date" : ISODate("2014-09-19T06:35:49.891Z") }
{ "_id" : ObjectId("541ffca6821e918f723f8dc0"), "title" : "my post", "content" : "this is tina's blog", "date" : ISODate("2014-09-22T10:39:47.987Z") }


mongodb常用命令
1、buildInfo
{"buildInfo":1}
2、collStats
{"collStats":collection}
3、distinct
{"distinct":collection,"key":key,"query":query}
4、drop
{"drop":collection}
5、dropDatabase
{"dropDatabase":1}
6、dropIndexes
{"dropIndexes":collection,"index":name}
7、getLastError
{"getLastError":1}
8、isMaster
{"isMaster":1}
9、listCommands
{"listCommands":1}
10、listDatabase
{"listDatabase":1}
11、ping
{"ping":1}
12、renameCollection
{"renameCollection":a,"to":b}
13、repairDatabase
{"repairDatabase":1}
14、serverStatus
{"serverStatus":1}

创建固定集合
>db.createCollection("my_collection",{capped:true,size:10000,max:100});

自然排序
>db.my_collection.find).sort({"$natural":1})
>db.my_collection.find).sort({"$natural":-1}) --反向插入的顺序查询


开始使用GridFS:mongofiles
帮助:mongofiles --help
# echo "Hello,world">foo.txt
# mongofiles -h 192.168.12.107 --port 27021 put foo.txt
connected to: 192.168.12.107:27021
added file: { _id: ObjectId('54212e3f4a5a5815c959fb3e'), filename: "foo.txt", chunkSize: 262144, uploadDate: new Date(1411460671339), md5: "76f8a9c79f2a8a2c0d3395e0bdbf662b", length: 12 }
done!
列出文件
# mongofiles -h 192.168.12.107 --port 27021 list
connected to: 192.168.12.107:27021
foo.txt 12

# mongofiles -h 192.168.12.107 --port 27021 get foo.txt
connected to: 192.168.12.107:27021
done write to: foo.txt

使用mongofiles的3个基本操作,put、list、get
put--将文件系统中的一个文件添加到GridFS中
list--把所有添加到GridFS中的文件列出来
get--将GridFS中的文件写入到文件系统中
search--用来按文件名查找GridFS中的文件
delete--从GridFS中删除一个文件。

块将使用fs.chunks集合
mongos> db.fs.chunks.find()
{ "_id" : ObjectId("54212e3e4f9e3a27697b9b93"), "files_id" : ObjectId("54212e3f4a5a5815c959fb3e"), "n" : 0, "data" : BinData(0,"SGVsbG8sd29ybGQK") }
_id--文件唯一的id
length--文件内容总的字节数
chunkSize--每个块的大小,以字节为单位
uploadDate--文件存入GridFS的时间戳
md5--文件内容的md5校验和,由服务器端生成。


mongodb管理:
mongodb就是一个普通的命令行程序,用mongod调用
mongodb提供了内置的管理接口和监控功能,易于与第三方监控包集成。
mongodb支持基本的、数据库级别的用户认证,包括制度用户,以及独立的管理员权限。

启动和停止mongodb
mongod --help
--dbpath (windows下是c:\data\db)
每个mongod进程都需要独立的数据目录,所以要是有3个mongod实例,必须要用3个独立的数据目录。
当mongod启动时,会在数据目录中创建mongod.lock文件,这个文件用于放置其他mongod
进程使用该数据目录。

--port 服务器监听的端口号,默认端口是27017,是个其他进程不怎么用的端口
要是运行多个mongod进程,则要给每个指定不同的端口号
如果启动mongod时端口被占用,则报错:
“address already in use for socket :0.0.0.0:27017"

--fork 以守护进程的方式运行mongodb,创建服务器进程

--logpath
指定日志输出路径,而不是输出到命令行。如果对文件夹有写权限的话。

文档经常会包含多个键/值对:
{"foo":3,"word":"hello"}
{"word":"hello","foo":3}
两种是完全不同的,mongod不但区分类型,也区分大小写。

1、键不能包含\0空符号,这个符号用来表示键的结尾。
2、以下划线开头的是保留的。
3、键值不能重复

集合就是一组文档,文档就类似行,集合就类似表。
集合命名:
不能是空字符串”“
不能包含\0字符(空字符)
不能以”system.”开头。

数据库命名:
不能是空字符串”“
不能含有空格,点,$,/,\ 和\0
应该全部小写
最多64个字节

admin 从权限的角度看,这是root数据库,要是将一个用户添加到这个数据库,这个用户自动继承所有数据库的权限。
一些特定的服务器端命令也只能从这个数据库运行,比如列出所有的数据库或者关闭服务器。

local这个数据永远不会被复制,可以用来存储限于本地单台服务器的任意集合。

config当mongo用于分片设置时,config数据库在内部使用,用户保存分片的相关信息。

启动mongodb
./mongod --在没有参数的情况下,会使用默认数据目录
./mongod --port 27021 --fork --logpath mongodb.log


配置文件
指定配置文件可以用-f 或者 --config选项 配置文件一般存放在/etc/mongodb.config
运行:mongod --config ~/.mongodb.conf

停止mongodb
use admin
db.shutdownServer();

mongos> db.shutdownServer();
assert failed : unexpected error: "shutdownServer failed: unauthorized: this command must run from localhost when running db without auth"
Error: Printing Stack Trace
at printStackTrace (src/mongo/shell/utils.js:37:15)
at doassert (src/mongo/shell/assert.js:6:5)
at assert (src/mongo/shell/assert.js:14:5)
at DB.shutdownServer (src/mongo/shell/db.js:346:9)
at (shell):1:4
Tue Sep 23 16:46:51.328 assert failed : unexpected error: "shutdownServer failed: unauthorized: this command must run from localhost when running db without auth" at src/mongo/shell/assert.js:7

mongo localhost:27021/admin 登录进去
mongos> use admin
switched to db admin
mongos> db.shutdownServer();
Tue Sep 23 16:47:51.595 DBClientCursor::init call() failed
server should be down...
Tue Sep 23 16:47:51.599 trying reconnect to localhost:27021
Tue Sep 23 16:47:51.599 reconnect localhost:27021 failed couldn't connect to server localhost:27021

--
Tue Sep 23 17:03:10.406 [conn18] terminating, shutdown command received
Tue Sep 23 17:03:10.406 [conn18] dbexit: shutdown called rc:0 shutdown called

分片(12.104)那边显示如下:
Tue Sep 23 16:47:50.443 [conn3] end connection 192.168.12.107:47030 (5 connections now open)
Tue Sep 23 16:47:50.445 [conn5] end connection 192.168.12.107:47042 (5 connections now open)
Tue Sep 23 16:47:50.447 [conn6] end connection 192.168.12.107:47048 (3 connections now open)


启动config
mongod --dbpath=/mongo --port 27018

启动mongos
mongos --port 27021 --configdb=192.168.12.107:27018

启动mongod分片
# mongod --dbpath=/mongo1 --port 27017
# mongod --dbpath=/mongo2 --port 27019
# mongod --dbpath=/mongo3 --port 27023


安全与认证
如果开启了安全性检查,则只有数据库认证用户才能执行读或写操作。
mongodb会将普通的数据作为admin数据库处理,admin数据库中的用户被视为超级用户。
认证之后,管理员可以读写所有数据库,执行特定的管理命令,如listDatabase和shutdown
mongos> use admin
switched to db admin
mongos> db.addUser("root","abc")
{
"user" : "root",
"readOnly" : false,
"pwd" : "9ebcc685b65db596c75fd6128803440f",
"_id" : ObjectId("54213c293a41399ef4a3d60e")
}
use test
mongos> db.addUser("test_user","def");
{
"user" : "test_user",
"readOnly" : false,
"pwd" : "2d7ad5ae62d41e5253c32831a63079b1",
"_id" : ObjectId("54213c5f3a41399ef4a3d60f")
}
mongos> db.addUser("read_only","ghi",true);
{
"user" : "read_only",
"readOnly" : true,
"pwd" : "67e8e24078ad4a487c54cb16527af13a",
"_id" : ObjectId("54213c7d3a41399ef4a3d610")
}
mongos> db.system.users.find();
{ "_id" : ObjectId("54213c293a41399ef4a3d60e"), "user" : "root", "readOnly" : false, "pwd" : "9ebcc685b65db596c75fd6128803440f" }
{ "_id" : ObjectId("54213c5f3a41399ef4a3d60f"), "user" : "test_user", "readOnly" : false, "pwd" : "2d7ad5ae62d41e5253c32831a63079b1" }
{ "_id" : ObjectId("54213c7d3a41399ef4a3d610"), "user" : "read_only", "readOnly" : true, "pwd" : "67e8e24078ad4a487c54cb16527af13a" }
上面添加了管理员root,在test数据库添加了两个普通账号,其中一个有只读权限,不能对数据库写入
调用addUser必须有相应数据库的写权限。

addUser不仅能添加用户,还能修改用户口令或者只读窗台。
重启服务器,加入--auth命令行选项,开启安全检查。跟刚刚一样的,需要

# openssl rand
Usage: rand [options] num
where options are
-out file - write to file
-engine e - use engine e, possibly a hardware device.
-rand file:file:... - seed PRNG from files
-base64 - base64 encode output
-hex - hex encode output
# openssl rand -base64 753
WYPee/+hqZZ+SLJuTtupYklSJRGWS0sH7yZ1qxSa9ErBsriyHRsaKVCKN7Ngu/BS
B0D1QKyBGZ3A+JXugLu+n7d319AKwQkX+6SBff1KS4KWL6biaX9oURuQMgb6HUty
e6ep4kFqpt2zkE6SqY8Rg7Cuouhs/CQsrgpU7af5CfSLXMBGED284wKaE+wh7yls
QVznZCoIjR2mrb68P0r4bgivynRyRq49TgdxmiQmXCXKXNNyVPG8MjzWtJaTazR6
/lvSfDWqk+VDC0b97MFEdPEsQiLNa6EAv8xqN9aycaJH+bQANtysala0wxsqDv8q
YuT9EmnTJM6F5L3Z8PK7EZKtSOlXnJaEX2jvLfwpYNTH4nhHkQV9KZJXD6lGx7Wn
ifnqIl3pVyxDBGDe9PBmgeh0+sgaK8jHP386h4gU5MkX/bEHDV8TL60kU+cARJ0C
xeHNy8IQer1c9E5n1D6iT9JqdidSgfKWOYGB7l937hTjdVQG9ektxGGM39Y04ObV
FpB87QZFoVhOobb/TTbV5tl64rmjBlN0bhH0kDmBvIvFT3GfTyYa92+1QxAAXC4k
3ge0fDKoocxw0LmQdJpE7n6Zn8HBzC20EzxJ4yqCmY1kfhRjS1X6zMk1Xi2nOoIY
myGqEvkrkWybBWh83WSSlhMtBgvFzgo8rnT+ru/r3BaFXkRRM1DX1Cvw5G9+qG8i
7AvJdYZpheqLR71GFjZc+PSRiBD+PKlkZJjYe8fJM9VXO/ZZGcShasrr+A3rHLb8
j92AmLbIU8nZwW54gcs8lZC0ufOrhauxNhCay7k8YXhxqr/+3HQvAH8OZYWRZfdG
gXMo0fOk8HsmuahMN9U3TiAmBoh6MhOHyGefJF8O7g6iP0TkQxWWDQLuUqWJZnzu
lLwK8Na4B7gnoHlRt7lWBYLuUM1PTcKF7RtVuARkvlmE6FsrRIpc5RL8k25Ed67H
NSZ7PpP2xLmHvC27CBCw5A9gux3r

在testdb中创建一个普通用户
mongos> use testdb
switched to db testdb
mongos> db.addUser('putong','putong');
{
"user" : "putong",
"readOnly" : false,
"pwd" : "9bef249456e43c9b819a4ff08f66c744",
"_id" : ObjectId("542142ea2f4e2dc152cf19c0")
}
mongos> db.system.users.find();
{ "_id" : ObjectId("542142ea2f4e2dc152cf19c0"), "user" : "putong", "readOnly" : false, "pwd" : "9bef249456e43c9b819a4ff08f66c744" }


# openssl rand -base64 753 >keyfile1
# chmod 600 keyfile1
# scp keyfile1 192.168.12.107:/root
keyfile1 100% 1020 1.0KB/s 00:00
# mongod --dbpath=/mongo1 --port 27017 --rest --keyFile=/root/keyfile1 --分配和config每个都这么启动

# mongos --port 27021 --configdb=192.168.12.107:27018 --keyFile=/root/keyfile1 --mongos这么启动


使用mongo 192.168.12.107:27021/admin 登录进来后,可以看到,权限已经变更,需要认证才能进行操作。
> db.usr.find().limit(10);
error: { "$err" : "not authorized for query on testdb.usr", "code" : 16549 }

# mongo 192.168.12.107:27021/admin -u root -p 使用用户root 密码abc进来
MongoDB shell version: 2.4.6
Enter password:
connecting to: 192.168.12.107:27021/admin
mongos>

use testdb
> db.auth('putong','putong');
1
> show collections;
address
blog
games
people
system.indexes
system.users
testa
usr

> use admin
switched to db admin
> show dbs
Tue Sep 23 18:19:10.744 listDatabases failed:{
"note" : "not authorized for command: listDatabases on database admin",
"ok" : 0,
"errmsg" : "unauthorized"
} at src/mongo/shell/mongo.js:46
> db.auth('root','abc');
1
---只有在admin中定义的用户,才能在admin中认证通过,在testdb中都不行
备注:在普通库中切换成超级用户失败,超级用户需要在 admin 库中切换才能生效
mongos> db.addUser('putong','putong',true); --这里是进的admin,是超级用户,将putong用户改成readonly
{
"_id" : ObjectId("542142ea2f4e2dc152cf19c0"),
"user" : "putong",
"readOnly" : true,
"pwd" : "9bef249456e43c9b819a4ff08f66c744"
}

只读用户的登录
# mongo 192.168.12.107:27021/testdb -u putong -p
MongoDB shell version: 2.4.6
Enter password:
connecting to: 192.168.12.107:27021/testdb
>

> db.testa.insert({"name":"haha"});
not authorized for insert on testdb.testa --putong用户只有只读权限

修改testdb中普通用户的readonly
mongos> db.system.users.update({"user":"putong","readOnly":true,"pwd":"9bef249456e43c9b819a4ff08f66c744"},{"user":"putong","readOnly":false,"pwd":"9bef249456e43c9b819a4ff08f66c744"})
mongos> db.system.users.find()
{ "_id" : ObjectId("542142ea2f4e2dc152cf19c0"), "user" : "putong", "readOnly" : false, "pwd" : "9bef249456e43c9b819a4ff08f66c744" }
-----但要注意的是:添加了这个认证之后,root用户怎么都无法通过分片库的认证,颇为揪心啊~~~


备份和恢复 mongodump mongorestore
# mongodump -h 192.168.12.107 --port 27021 -u putong -p -d testdb -o mongobak
connected to: 192.168.12.107:27021
Enter password:
Tue Sep 23 18:46:22.782 DATABASE: testdb to mongobak/testdb
Tue Sep 23 18:46:22.783 testdb.system.indexes to mongobak/testdb/system.indexes.bson
Tue Sep 23 18:46:22.785 10 objects
Tue Sep 23 18:46:22.785 testdb.usr to mongobak/testdb/usr.bson
Tue Sep 23 18:46:25.019 Collection File Writing Progress: 245800/1211002 20% (objects)
Tue Sep 23 18:46:28.132 Collection File Writing Progress: 722900/1211002 59% (objects)
Tue Sep 23 18:46:30.860 1211002 objects
Tue Sep 23 18:46:30.860 Metadata for testdb.usr to mongobak/testdb/usr.metadata.json
Tue Sep 23 18:46:30.861 testdb.games to mongobak/testdb/games.bson
Tue Sep 23 18:46:30.863 0 objects
Tue Sep 23 18:46:30.863 Metadata for testdb.games to mongobak/testdb/games.metadata.json
Tue Sep 23 18:46:30.863 testdb.testa to mongobak/testdb/testa.bson
Tue Sep 23 18:46:30.864 0 objects
Tue Sep 23 18:46:30.864 Metadata for testdb.testa to mongobak/testdb/testa.metadata.json
Tue Sep 23 18:46:30.865 testdb.address to mongobak/testdb/address.bson
Tue Sep 23 18:46:30.866 1 objects
Tue Sep 23 18:46:30.866 Metadata for testdb.address to mongobak/testdb/address.metadata.json
Tue Sep 23 18:46:30.866 testdb.people to mongobak/testdb/people.bson
Tue Sep 23 18:46:30.867 0 objects
Tue Sep 23 18:46:30.867 Metadata for testdb.people to mongobak/testdb/people.metadata.json
Tue Sep 23 18:46:30.868 testdb.system.users to mongobak/testdb/system.users.bson
assertion: 11010 count fails:{ note: "not authorized for command: count on database testdb", ok: 0.0, errmsg: "unauthorized" }

root用户只能导出admin数据库
# mongodump -h 192.168.12.107 --port 27021 -u root -p -d admin -o mongobak
connected to: 192.168.12.107:27021
Enter password:
Tue Sep 23 18:48:22.921 DATABASE: admin to mongobak/admin
Tue Sep 23 18:48:22.925 admin.system.indexes to mongobak/admin/system.indexes.bson
Tue Sep 23 18:48:22.926 2 objects
Tue Sep 23 18:48:22.926 admin.system.users to mongobak/admin/system.users.bson
Tue Sep 23 18:48:22.927 3 objects
Tue Sep 23 18:48:22.927 Metadata for admin.system.users to mongobak/admin/system.users.metadata.json


fsync和锁
虽然用mongodump、mongorestore能不停机备份,但是我们失去了获取实时数据视图的能力。
mongodb的fsync命令能在mongodb运行时复制数据目录还不会损坏数据

fsync命令会强制服务器将所有缓冲区写入磁盘。还可以选择上锁阻止对数据库的进一步写入,直到释放锁为止。
use admin
db.runCommand({"fsync":1,"lock":1})
强制执行了fsync并获得了写入锁。,至此,数据目录的数据都是一致的,且为数据的实时快照。因为上了写入锁,
可以安全地将数据目录副本用来备份。要是数据库运行在有快照功能的文件系统上时,比如LVM或者EBS,这个就有用了,
因为拍个数据库目录快照非常之快。

备份好了,就要解锁:
db.$cmd.sys.unlock.findOne();
db.currentOp();

有了fsync命令,就能非常灵活地本分,不用停掉服务器,也不用牺牲备份的实时特性。要付出的代价就是一些写入操作
被暂时阻塞了。唯一不耽误读写还能保证实时快照的备份方式就是通过从服务器备份。

主从复制
一主多从
主 mongod--master
从 mongod--slave

利用192.168.12.229上的3个目录,实现一主二从
主库:
# mongod --dbpath=/mongo_master/ --port 27030 --master
从库:
# mongod --dbpath=/mongo_slave --port 27031 --slave --source=192.168.12.229:27030

登录:
mongo 192.168.12.229:27030/admin --在主库添加数据库,创建表
mongo 192.168.12.229:27031/admin --发现从库已经同步

再添加一个从库
# mongod --dbpath=/mongo_slave1 --port 27032 --slave --source=192.168.12.229:27030
--也是立马就同步。


MongoDB基本概念
1.文档是MongoDB中数据的基本单元,类似于关系型数据库的行(但比行复杂的多)
2.集合可以看成没有模式的表
3.MongoDB的单个实例可以容纳多个独立的数据库,每一个都有自己的集合和权限
4.MongoDB自带简洁但功能强大的javascript shell,这个工具对于管理MongoDB实例和操作数据非常有用
5.每一个文档都有一个特殊的键"_id",它在文档所处的集合中是唯一的.


=================================================================================================================================
二、DB shell数据操作
shell命令操作语法和JavaScript很类似,其实控制台底层的查询语句都是用JavaScript脚本完成操作的。
Ø 数据库
1、Help查看命令提示
> help
> db.help();
> db.yourColl.help();
> db.youColl.find().help();
> rs.help();

2、切换/创建数据库
> use yourDB;
当创建一个集合(table)的时候会自动创建当前数据库

3、查询所有数据库
> show dbs;

4、删除当前使用数据库
> db.dropDatabase();

5、从指定主机上克隆数据库
> db.cloneDatabase(“127.0.0.1”);
将指定机器上的数据库的数据克隆到当前数据库

6、从指定的机器上复制指定数据库数据到某个数据库
> db.copyDatabase("mydb", "temp", "127.0.0.1");
将本机的mydb的数据复制到temp数据库中

7、修复当前数据库
> db.repairDatabase();

8、查看当前使用的数据库
> db.getName();
> db;
db和getName方法是一样的效果,都可以查询当前使用的数据库

9、显示当前db状态
> db.stats();

10、当前db版本
> db.version();

11、查看当前db的链接机器地址
> db.getMongo();
Ø Collection聚集集合

1、创建一个聚集集合(table)
> db.createCollection(“collName”, {size: 20, capped: 5, max: 100});

2、得到指定名称的聚集集合(table)
> db.getCollection("account");

3、得到当前db的所有聚集集合
> db.getCollectionNames();

4、显示当前db所有聚集索引的状态
> db.printCollectionStats();

Ø 用户相关
1、添加一个用户
> db.addUser("name");
> db.addUser("userName", "pwd123", true);
添加用户、设置密码、是否只读

2、数据库认证、安全模式
> db.auth("userName", "123123");

3、显示当前所有用户
> show users;

4、删除用户
> db.removeUser("userName");
Ø 其他

1、查询之前的错误信息
> db.getPrevError();
mongos> db.getPrevError();
{
"ok" : 0,
"errmsg" : "getpreverror not supported for sharded environments"
}

2、清除错误记录
> db.resetError();

三、Collection聚集集合操作
Ø 查看聚集集合基本信息
1、查看帮助
> db.yourColl.help();

2、查询当前集合的数据条数
> db.yourColl.count();

3、查看数据空间大小
> db.userInfo.dataSize();

4、得到当前聚集集合所在的db
> db.userInfo.getDB();

5、得到当前聚集的状态
> db.userInfo.stats();

6、得到聚集集合总大小
> db.userInfo.totalSize();

7、聚集集合储存空间大小
> db.userInfo.storageSize();

8、Shard版本信息
> db.userInfo.getShardVersion()

9、聚集集合重命名
> db.userInfo.renameCollection("users");
将userInfo重命名为users

10、删除当前聚集集合
> db.userInfo.drop();

Ø聚集集合查询
1、查询所有记录
> db.userInfo.find();
相当于:select * from userInfo;
默认每页显示20条记录,当显示不下的情况下,可以用it迭代命令查询下一页数据。注意:键入it命令不能带“;”
但是你可以设置每页显示数据的大小,用DBQuery.shellBatchSize = 50;这样每页就显示50条记录了。

2、查询去掉后的当前聚集集合中的某列的重复数据
> db.userInfo.distinct("name");
会过滤掉name中的相同数据
相当于:select distict name from userInfo;

3、查询age = 22的记录
> db.userInfo.find({"age": 22});
相当于:select * from userInfo where age = 22;

4、查询age > 22的记录
> db.userInfo.find({age: {$gt: 22}});
相当于:select * from userInfo where age > 22;

5、查询age < 22的记录
> db.userInfo.find({age: {$lt: 22}});
相当于:select * from userInfo where age < 22;

6、查询age >= 25的记录
> db.userInfo.find({age: {$gte: 25}});
相当于:select * from userInfo where age >= 25;

7、查询age <= 25的记录
> db.userInfo.find({age: {$lte: 25}});

8、查询age >= 23 并且 age <= 26
> db.userInfo.find({age: {$gte: 23, $lte: 26}});

9、查询name中包含 mongo的数据
> db.userInfo.find({name: /mongo/});
相当于:select * from userInfo where name like ‘%mongo%’;

10、查询name中以mongo开头的
> db.userInfo.find({name: /^mongo/});
select * from userInfo where name like ‘mongo%’;

11、查询指定列name、age数据
> db.userInfo.find({}, {name: 1, age: 1});
相当于:select name, age from userInfo;
当然name也可以用true或false,当用ture的情况下河name:1效果一样,如果用false就是排除name,显示name以外的列信息。

12、查询指定列name、age数据, age > 25
> db.userInfo.find({age: {$gt: 25}}, {name: 1, age: 1});
相当于:select name, age from userInfo where age > 25;

13、按照年龄排序
升序:
> db.userInfo.find().sort({age: 1});
降序:
> db.userInfo.find().sort({age: -1});

14、查询name = zhangsan, age = 22的数据
> db.userInfo.find({name: 'zhangsan', age: 22});
相当于:select * from userInfo where name = ‘zhangsan’ and age = ‘22’;

15、查询前5条数据
> db.userInfo.find().limit(5);
相当于:select top 5 * from userInfo;

16、查询10条以后的数据
> db.userInfo.find().skip(10);
相当于:select * from userInfo where id not in ( select top 10 * from userInfo );

17、查询在5-10之间的数据
> db.userInfo.find().limit(10).skip(5);
可用于分页,limit是pageSize,skip是第几页*pageSize

18、or与 查询
> db.userInfo.find({$or: [{age: 22}, {age: 25}]});
相当于:select * from userInfo where age = 22 or age = 25;

19、查询第一条数据
> db.userInfo.findOne();
相当于:select top 1 * from userInfo;
> db.userInfo.find().limit(1);

20、查询某个结果集的记录条数
> db.userInfo.find({age: {$gte: 25}}).count();
相当于:select count(*) from userInfo where age >= 20;

21、按照某列进行排序
> db.userInfo.find({sex: {$exists: true}}).count();
相当于:select count(sex) from userInfo;

Ø 索引
1、创建索引
> db.userInfo.ensureIndex({name: 1});
> db.userInfo.ensureIndex({name: 1, ts: -1});

2、查询当前聚集集合所有索引
> db.userInfo.getIndexes();

3、查看总索引记录大小
> db.userInfo.totalIndexSize();

4、读取当前集合的所有index信息
> db.users.reIndex();

5、删除指定索引
> db.users.dropIndex("name_1");

6、删除所有索引索引
> db.users.dropIndexes();

Ø 修改、添加、删除集合数据
1、添加
> db.users.save({name: ‘zhangsan’, age: 25, sex: true});
添加的数据的数据列,没有固定,根据添加的数据为准

2、修改
> db.users.update({age: 25}, {$set: {name: 'changeName'}}, false, true);
相当于:update users set name = ‘changeName’ where age = 25;
> db.users.update({name: 'Lisi'}, {$inc: {age: 50}}, false, true);
相当于:update users set age = age + 50 where name = ‘Lisi’;
> db.users.update({name: 'Lisi'}, {$inc: {age: 50}, $set: {name: 'hoho'}}, false, true);
相当于:update users set age = age + 50, name = ‘hoho’ where name = ‘Lisi’;

3、删除
> db.users.remove({age: 132});

4、查询修改删除
> db.users.findAndModify({
... query: {age: {$gte: 25}},
... sort: {age: -1},
... update: {$set: {name: 'a2'}, $inc: {age: 2}},
... remove: true
... });

> db.runCommand({ findandmodify : "users",
... query: {age: {$gte: 25}},
... sort: {age: -1},
... update: {$set: {name: 'a2'}, $inc: {age: 2}},
... remove: true
... });
update 或 remove 其中一个是必须的参数; 其他参数可选。
参数
详解
默认值
query
查询过滤条件
{}
sort
如果多个文档符合查询过滤条件,将以该参数指定的排列方式选择出排在首位的对象,该对象将被操作
{}
remove
若为true,被选中对象将在返回前被删除
N/A
update
一个 修改器对象
N/A
new
若为true,将返回修改后的对象而不是原始对象。在删除操作中,该参数被忽略。
false
fields
All fields
upsert
创建新对象若查询结果为空。 示例 (1.5.4+)
false

1、简单Hello World
> print("Hello World!");
这种写法调用了print函数,和直接写入"Hello World!"的效果是一样的;

2、将一个对象转换成json
> tojson(new Object());
> tojson(new Object('a'));

3、循环添加数据
> for (var i = 0; i < 30; i++) {
... db.users.save({name: "u_" + i, age: 22 + i, sex: i % 2});
... };
这样就循环添加了30条数据,同样也可以省略括号的写法
> for (var i = 0; i < 30; i++) db.users.save({name: "u_" + i, age: 22 + i, sex: i % 2});
也是可以的,当你用db.users.find()查询的时候,显示多条数据而无法一页显示的情况下,可以用it查看下一页的信息;

4、find 游标查询
>var cursor = db.users.find();
> while (cursor.hasNext()) {
... printjson(cursor.next());
... }
这样就查询所有的users信息,同样可以这样写
>var cursor = db.users.find();
>while (cursor.hasNext()) { printjson(cursor.next); }
同样可以省略{}号

5、forEach迭代循环
>db.users.find().forEach(printjson);
forEach中必须传递一个函数来处理每条迭代的数据信息

6、将find游标当数组处理
> var cursor = db.users.find();
> cursor[4];
取得下标索引为4的那条数据
既然可以当做数组处理,那么就可以获得它的长度:cursor.length();或者cursor.count();
那样我们也可以用循环显示数据
> for (var i = 0, len = c.length(); i < len; i++) printjson(c[i]);

7、将find游标转换成数组
> var arr = db.users.find().toArray();
> printjson(arr[2]);
用toArray方法将其转换为数组

8、定制我们自己的查询结果
只显示age <= 28的并且只显示age这列数据
> db.users.find({age: {$lte: 28}}, {age: 1}).forEach(printjson);
> db.users.find({age: {$lte: 28}}, {age: true}).forEach(printjson);
排除age的列
> db.users.find({age: {$lte: 28}}, {age: false}).forEach(printjson);

9、forEach传递函数显示信息
> db.things.find({x:4}).forEach(function(x) {print(tojson(x));});
上面介绍过forEach需要传递一个函数,函数会接受一个参数,就是当前循环的对象,然后在函数体重处理传入的参数信息。
那么关于mongodb的shell操作的讲解就先到这了,基本涵盖了mongodb最为常用的操作方法,那么下一节我们会讲述用java如何驱动mongodb,即所谓的CRUD。


--第二次启动的时候,报错,改了27021端口就行。 --其实直接把27020那个kill掉就可以了。
# mongos --port 27020 --configdb=192.168.12.107:27018
Fri Sep 19 10:20:25.215 warning: running with 1 config server should be done only for testing purposes and is not recommended for production
Fri Sep 19 10:20:25.216 [mongosMain] MongoS version 2.4.6 starting: pid=27294 port=27020 64-bit host=viptest2 (--help for usage)
Fri Sep 19 10:20:25.216 [mongosMain] git version: nogitversion
Fri Sep 19 10:20:25.216 [mongosMain] build info: Linux buildvm-04.phx2.fedoraproject.org 3.12.8-300.fc20.x86_64 #1 SMP Thu Jan 16 01:07:50 UTC 2014 x86_64 BOOST_LIB_VERSION=1_41
Fri Sep 19 10:20:25.216 [mongosMain] options: { configdb: "192.168.12.107:27018", port: 27020 }
Fri Sep 19 10:20:25.223 [Balancer] about to contact config servers and shards
Fri Sep 19 10:20:25.223 [websvr] ERROR: listen(): bind() failed errno:98 Address already in use for socket: 0.0.0.0:28020
Fri Sep 19 10:20:25.223 [websvr] ERROR: addr already in use
Fri Sep 19 10:20:25.223 [mongosMain] ERROR: listen(): bind() failed errno:98 Address already in use for socket: 0.0.0.0:27020
Fri Sep 19 10:20:25.223 [mongosMain] ERROR: addr already in use
Fri Sep 19 10:20:25.223 [mongosMain] dbexit: rc:48


修改分片key


按照道理,删除表,那么与它相关的分片信息也会相应的消失,但是update_log却因为反复搭建,环境混乱,而出现如下报错
mongos> db.update_log.drop()
Tue Mar 24 15:43:51.759 drop failed: {
"code" : 16338,
"ok" : 0,
"errmsg" : "exception: Dropping collection failed on the following hosts: 121.42.169.128:27020: { ok: 0.0, errmsg: \"ns not found\" }"
} at src/mongo/shell/collection.js:383


这带来一个问题是我们测开发测试的时候,往往希望通过观察数据在迁移来证明分片是否成功,但64M200M显然过大,解决方法是我们可以在启动mongos的时候用—chunkSize来制定块的大小,单位是MB。

1. ./bin/mongos --port 20000 --configdb 192.168.32.13:10000 --logpath log/mongos.log --fork --chunkSize 1
我指定1MB的块大小重新启动了一下mongos进程。

或者向下面这要修改chunkSize大小:

1. mongos> use config
2. mongos> db.settings.save( { _id:"chunksize", value: 1 } )


正确关闭mongodb
首先MongoDB使用mmap方式进行数据文件管理,也就是说写操作基本是在内存中进行,写操作会被每隔60秒(syncdelay设定)的flush到磁盘里。如果在这60秒内flush处于停止事情我们进行kill -9那么从上次flush之后的写入数据将会全部丢失。
如果在flush操作进行时执行kill -9则会造成文件混乱,可能导致数据全丢了,启动时加了repair也无法恢复。

避免方法
第一种 使用正确的关闭方法
官方文档说明要使用 kill -15,killall mongod或者在client的shell里,use admin,执行db.shutdownServer()即可!
第二种
通过replication 使用 一个slave,或者用replica set 来避免单结点数据丢失。
第三种 1.7以后出现类似于binlog的功能
在 MongoDB 1.7 版本的最新分支上,就出现了一个新的数据可靠性选项(–dur)的支持。并且在数据文件修复工具上也有了一些改进。

如果在启动MongoDB时加上–dur 则MongoDB 会在进行写操作前记一份日志,这和在其他一些数据库中的binlog 类似,在MongoDB 数据文件损坏的情况下,可以使用此日志来进行恢复。据说其对性能的影响不大。[/size]

你可能感兴趣的:(mongodb)