[root@localhost bin]# ./mongo
MongoDB shell version: 2.2.4
connecting to: test
默认连接是test,但是show dbs时只显示local (empty),test数据库是如何而来的哪?mongodb是隐式的创建数据库和表,当插入数据时会自动创建表
退出:exit
Mongodb的体系结构
关系型数据库Mysql:数据库(database)、表(table)、记录(rows)三个层次组成
非关系型数据库Mongodb:数据库(database)、集合(collection)、文档对象(document)三个层次组成
MongoDB里的集合对应于关系型数据库里的表,但是集合中没有列、行和关系的概念,集合中只有文档,一个文档就相当于一条记录,这体现了模式自由的特点。
数据存储结构
mysql的数据存储结构:每个数据库存放在一个与数据库同名的文件夹中,如果使用MyISAM存储引擎,数据库文件类型就包括.frm、.MYD、.MYI。
mongodb的数据存储结构:默认数据目录是/data/db,它负责存储所有的MongoDB的数据文件。在MongoDB内部,每个数据库都包含一个.ns文件和一些数据文件,而且这些数据文件会随着数据量的增加而变得越来越多。
所以如果系统中有一个叫做mydb的数据库,那么构成mydb这个数据库的文件就会由mydb.ns,mydb.0,mydb.1组成
-rwxr-xr-x 1 root root 6 05-09 12:59 mongod.lock
-rw——- 1 root root 16M 05-09 07:03 test.0
-rw——- 1 root root 32M 05-09 07:03 test.1
-rw——- 1 root root 16M 05-09 07:03 test.ns
show dbs查看数据库
db当前数据库
show tables/show collections显示集合
db.dropDatabase()删除当前数据库
db.集合名.drop(),删除集合
use 数据库名,定义新的数据库名
db.数据库名.remove(),清空集合
想知道更多命令,可以直接输入help
mongodb操作
插入 insert/save
Insert 如果主键相同则插入不成功,save则是更新这个文档
//定义文档
>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.c1.insert(doc);
删除:db.c1.remove();c1中的文档全部删除
Db.c1.remove({name:”user1”});删除name是user1的
查询
db.c1.find()查询c1下所有
db.users.find({name: ‘user1′})
只返回某些特定的字段值:
//返回除了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字段始终都会被返回,哪怕没有明确指定,如果不想返回要明确指定{_id:0}
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:5,$lte:10}});
//不等于
db.c1.find({_id:{$ne:5}});
排序,按照last_name 属性进行升序排序返回所有文档:
//1表示升序,-1表示降序
db.users.find({}).sort({last_name: 1});
count()
count()方法返回查询记录的总数目
> db.user.find().count();
> db.user.find({_id:{$lt:20}}).count();
limit()
> db.user.find().limit(5);
注意,下面和mysql不一样
> db.c1.find().limit(2,5); //只取出2条,要相达到mysql结果需要结合skip();
> db.c1.find().skip(2).limit(5);
//先执行排序再limit,count没有参数只会返回db.c1.find()的查询的条数
> db.c1.find().sort({age:-1}).skip(2).limit(5).count();
> db.c1.find().sort({age:-1}).skip(2).limit(5).count(1); //5
$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]}});
$exists操作检查一个字段是否存在,
//包含_id,索引
> db.user.find({_id:{$exists:1}})
$mod操作可以进行取模操作,而不需要用到where子句,如
//where子句
> db.user.find(“this._id%10==1″).limit(5);
//$mod操作
> db.user.find({_id:{$mod:[10,1]}}).limit(5);
$in操作类似于传统关系数据库中的IN
//数据库中有所有数组对应的记录
> db.user.find({_id:{$in:[2,3,4,5,6]}}).limit(5);
{ “_id” : 2, “name” : “user2″, “userid” : 2, “age” : 20 }
{ “_id” : 6, “name” : “user6″, “userid” : 6, “age” : 20 }
$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 }
$or 和$nor例外,魔术方法在外侧
> 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 }
$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=2,name=user3和userid=4的记录都被过滤了
$size操作将会查询数组长度等于输入参数的数组,例子:
> db.things.find({a:{$size:3}});
{ “_id” : ObjectId(“4de73360059e7f4bdf907cfe”), “a” : [ 1, 2, 3 ] }
正则表达式:遵循常规正则的语法
//检索name属性是以u开头,4结尾的所有用户
> db.user.find({name:/u.*4$/i}).limit(5);
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 ]
$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 } } )
$slice
db.posts.find({}, {comments:{$slice: 5}}) // 前5条评论
db.posts.find({}, {comments:{$slice: -5}}) //后5条评论
db.posts.find({}, {comments:{$slice: [20, 10]}}) // skip 20, limit 10
db.posts.find({}, {comments:{$slice: [-20, 10]}}) // 20 from end, limit 10
NULL查询
//包含NULL情况的查询
> db.foo.insert( { x : 1, y : 1 } )
> db.foo.insert( { x : 2, y : “string” } )
> db.foo.insert( { x : 3, y : null } )
> db.foo.insert( { x : 4 } )
// Query #1
> db.foo.find( { “y” : null } )
{ “_id” : ObjectId(“4dc1975312c677fc83b5629f”), “x” : 3, “y” : null }
{ “_id” : ObjectId(“4dc1975a12c677fc83b562a0″), “x” : 4 }
// Query #2
> db.foo.find( { “y” : { $type : 10 } } )
{ “_id” : ObjectId(“4dc1975312c677fc83b5629f”), “x” : 3, “y” : null }
// Query #3
> db.foo.find( { “y” : { $exists : false } } )
{ “_id” : ObjectId(“4dc1975a12c677fc83b562a0″), “x” : 4 }
$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主要数据类型映射:
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
更改操作:
db.collection.update( criteria, objNew, upsert, multi )
参数说明:
Criteria:用于设置查询条件的对象
Objnew:用于设置更新内容的对象
Upsert:如果记录已经存在,更新它,否则新增一个记录 一般设置为0
Multi:如果有多个符合条件的记录,全部更新 一般设置为1
注意:默认情况下,只会更新第一个符合条件的记录
> for(i=1;i<10;i++){ db.c1.insert({name:”user”+i,age:i}); }
> db.c1.find();
{ “_id” : ObjectId(“518c452a644bcedf5c9be88c”), “name” : “user1″, “age” : 1 }
{ “_id” : ObjectId(“518c452a644bcedf5c9be88d”), “name” : “user2″, “age” : 2 }
{ “_id” : ObjectId(“518c452a644bcedf5c9be88e”), “name” : “user3″, “age” : 3 }
{ “_id” : ObjectId(“518c452a644bcedf5c9be88f”), “name” : “user4″, “age” : 4 }
{ “_id” : ObjectId(“518c452a644bcedf5c9be890″), “name” : “user5″, “age” : 5 }
{ “_id” : ObjectId(“518c452a644bcedf5c9be891″), “name” : “user6″, “age” : 6 }
{ “_id” : ObjectId(“518c452a644bcedf5c9be892″), “name” : “user7″, “age” : 7 }
{ “_id” : ObjectId(“518c452a644bcedf5c9be893″), “name” : “user8″, “age” : 8 }
{ “_id” : ObjectId(“518c452a644bcedf5c9be894″), “name” : “user9″, “age” : 9 }
> db.c1.update({name:”user5″},{sex:”m”});
更改后name为user5的那条文档变为下面这样:
{ “_id” : ObjectId(“518c452a644bcedf5c9be890″), “sex” : “m” }
> db.c1.update({name:”user1″},{name:”ljf”});//原来的记录会变为下面的
{ “_id” : ObjectId(“518c452a644bcedf5c9be88c”), “name” : “ljf” }//age没有了
> db.c1.update({name:”kitty”},{name:”ljf”}); //没有name为kitty的记录,所以不成功
> db.c1.update({name:”kitty”},{name:”ljf”},1);//会增加这一条记录
如果想要达到类似mysql的update效果,应该用$set
{ $set : { field : value } }
功能: 把field的值设置成value,当field不存在时,增加一个字段,类似SQL的set操
作,value支持所有类型
> db.c1.update({name:”user2″},{$set:{name:”ljf”,age:22,sex:”f”}},0,1); //则这条记录变为以下
{ “_id” : ObjectId(“518c452a644bcedf5c9be88d”), “age” : 22, “name” : “ljf”, “sex” : “f” }
$inc
{ $inc : { field : value } }
功能: 把field的值加一个value,如果没有这个field则会自动创建
例子:我要对一个_id=0的user的年龄进行加1,普通的做法如下:
> var u = db.user.findOne({_id:0});
> u
{ “_id” : 0, “name” : “user0″, “userid” : 0, “age” : 21 }
> u.age++
21
> db.user.save(u);
> var u = db.user.findOne({_id:0});
> u
{ “_id” : 0, “name” : “user0″, “userid” : 0, “age” : 22 }
>
但是有了$inc操作符呢,就不需要这么麻烦:
> db.user.update({_id:0},{$inc:{age:1}});
> var u = db.user.findOne({_id:0});
> u
{ “_id” : 0, “name” : “user0″, “userid” : 0, “age” : 23 }
$set与$inc自动创建字段却不能删除,这时可以用$unset
{ $unset : { field : 1} }
功能: 删除给定的字段field
//删除上一步增加的sex字段
> db.user.findOne({_id:0});
{ “_id” : 0, “age” : 20, “name” : “user0″, “sex” : “boy”, “userid” : 0 }
> db.user.update({_id:0},{$unset:{sex:1}});
> db.user.findOne({_id:0});
{ “_id” : 0, “age” : 20, “name” : “user0″, “userid” : 0 }
$push
语法: { $push : { field : value } }
功能: 如果filed是一个已经存在的数组,那么把value追加给field;如果field原来不存在,那么新增field字段,把value的值赋给field;如果field存在,但是不是一个数组,将会出错;
$pushAll
语法: { $pushAll : { field : value_array } }
功能: 功能同$push,只是这里的value是数组,相当于对数组里的每一个值进行$push操作
索引:索引信息存储在system.indexes中,mongodb中_id字段在创建时候,默认已经建立了索引,这个索引不能被删除。
建立普通索引:db.c1.ensureIndex({name:1});
当系统已有大量数据时,创建索引非常耗时,要在后台执行,只需执行加上”background:true”执行即可。
db.c1.getIndexKeys();//查看c1索引的字段
db.c1.getIndexes();//查看c1索引的详细信息
db.c1.find({name:”user5″}).explain();//查看此查询语句索引使用情况
添加唯一索引:db.c1.ensureIndex({age:1},{unique:1});
删除索引:
db.c1.dropIndex({age:1});
db.c1.dropIndexes();//不会删除_id索引
慢查询
MongoDB Database Profiler是一种慢查询日志功能,可以作为我们优化数据库的依据。profile的级别可以取0,1,2三个值,它们表示:
0- 不开启
1- 记录慢命令(默认为>100ms)
2- 记录所有命令
db.getProfilingLevel();//查看是否开启了慢查询
db.setProfilingLevel(1);
启动时可以修改默认慢查询参数100ms:db.setProfilingLevel(1,100);
性能监控
Mongosniff
此工具可从底层监控到底有哪些命令发送给MongoDB去执行。
[root@localhost bin]# ./mongosniff –source net lo
[root@localhost bin]# ./mongostat
数据导出/导入:mongoexport、mongoimport
mongoexport -d test -c collection -o 导出的文件名
参数说明:
-d 数据库名称
-c 集合名称
-o 导出的文件名称
./mongoimport -d test -c c1 /tmp.c1.out
数据备份 数据恢复mongodump、mongorestore
mongodump -d test
此时会在当前目录下默认创建一个dump目录,用于存放备份出来的文件也可以。单个集合的备份是可以成功,但单个集合的恢复不成功
mongodump -d test -c c5 -o mydump/c5
这样写才对
mongorestore -d test mydump/c5/test OK
用户授权
每个MongoDB实例中的数据库都有许多用户,如果启用了安全性认证后,只有数据认证的用户才可以进行读写操作
MongoDB默认的启动是不验证用户名和密码的,启动MongoDB后,可以直接用mongo连接上来,对所有的库具有root权限,所以启动的时候指定参数,可以阻止客户端的访问和连接,只需要在启指定–auth参数即可
mongod –auth
添加数据库管理员
use admin;
db.addUser(“root”:”123″);
管理员分为系统管理员和数据库管理员
系统管理员必须添加到admin数组库中,
use admin;
db.addUser(“root”,”123456″);
然后用
mongo -uroot -p123456 localhost:27017/admin
然后为其它数据库设置管理员
use test
db.addUser(“user1″,”123456″);
现在就能用user1来管理test数据库了
可以通过db.system.users.find();来查看数据库中的所用管理员信息
mongo -uuser1 -p123456 localhost:27017/test
为什么还可以登陆呢?在最初始的时候MongoDB都默认有一个admin数据库(默认是空的)admin.system.users中将会保存具有管理员权限的用户
注意: 当admin.system.users中没有添加任何用户时,即使MongoDB启动时添加–auth参数,如果有除admin数据库中添加了用户,此时不进行任何认证依然可以使用任操作,直到你在admin.system.users中添加了一个用户
建立一个系统root用户
在admin库中新添一个用户root
use admin
db.addUser(‘root’,’123′);
db.auth(“root”,’123′);
建立指定权限用户
mongoDB也支持为某个特定的数据库来设置用户
如我们为test库设一个只读的用户user_reader
db.addUser(“user1″,”pass1″,true);
只要在db.addUser函数的第三个参数国中true即可为只读,只有在test库,有只读的权限