Mongo基本操作

Posted slowdownthenrunfast

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Mongo基本操作相关的知识,希望对你有一定的参考价值。

基本操作:

查看所有库: show dbs;

添加记录:

db.hello.save(name : "zhaoliu" );
db.hello.save(name : "zhangsan", age : 50 );
db.hello.save(name : "wangwu", age : 30, gender : "male" );
db.hello.save(name : "lisi", age : 40, gender: "male" );
db.hello.save(name : "qianqi", age : 40, gender : "female", email : [  "123@qq.com",  "456@qq.com" ]);

查询:

去重某列(分组)
db.hello.distinct("id");
查询结果指定列
db.hello.find(,name:1); (只显示name列)
db.hello.find(,name:1,id:1); (显示name和id两列)

查询出age大于10的所有记录:

db.hello.find("age":$gt:40);

只查第一列 :
db.hello.findOne();

limit来限制查询的最大行数

db.hello.find().limit(4); // 只显示出前四条

查询最大值

db.hello.find().sort(age:-1).limit(1);

利用“.”(点号)进行对象间的导航 
db.hello.find(, “x.y” : 1); 

匹配所有$all:

db.hello.find( a: $all: [ 2, 3 ] ); 

存在性判断$exists: 
db.hello.find( a : $exists : true ); // 如果存在则返回 
db.hello.find( a : $exists : false ); // 如果不存在则返回 

求余$mod:
db.hello.find( "this.a % 10 == 1"),也可写成如下形式: 
db.hello.find( a : $mod : [ 10 , 1 ] ) ;

不等于$ne:
db.hello.find( x : $ne : 3 ); 
IN子句$in:
db.hello.find(j:$in: [2,4,6]); 
Not In子句$nin:
db.hello.find(j:$nin: [2,4,6]); 
OR条件$or:
db.hello.find( $or : [ a : 1 , b : 2 ] ) 

利用正则表达式进行复杂匹配 
db.hello.find( name : /xxx*/i ); //i表示大小写不敏感 
db.hello.find( name : $regex : "xxx*", $options: "i" );
db.hello.find( name : $regex : /xxx*/i, $nin : ["xxxxxxxx"] );

取反操作$not:

db.hello.find( name : $not : /acme.*corp/i ); 

分页 limit是pageSize,skip是第几页*pageSize
db.songs.find().sort("id":-1).limit(1);
db.songs.find().sort("id":-1).skip(1).limit(1);

排序1:asc,-1:desc   sort():
db.hello.find().sort(id:1);
db.hello.find().sort(id:-1);
db.hello.find().sort("id":1,"name":-1);

选择特定列(0=false,!0=true)
db.hello.find("age":$in:[12,22,33],age:0);
db.hello.find("age":$in:[12,22,33],age:1);

主键自增长:

db.ids.save(name:"user", id:0);
db.ids.find();

db.user.save(
               uid: db.ids.findAndModify(update:$inc:'id':1,query:"name":"user",new:true).id,
               name : "zhaoba",
               age : 50, 
               gender : "male", 
               email : [  "fd@qq.com",  "2r3@qq.com" ]
               );


索引操作

索引的限制

1)一个表上的索引不能超过64个;

2)索引的键不能超过1024bytes();

3)索引的名字(包含命名空间)必须少于128个字符;

1查看索引
1)查看某个库上的所有索引
db.system.index.find()
2)查看某个表上的所有索引
db.hello.getIndexes() 
2.删除索引
1)删除表上的某个索引
db.hello.dropIndex(a:1)
2)删除表上的所有索引
db.hello.dropIndexes()
3.重建索引
db.hello.reIndex()
以上操作会删除表上的所有索引(包含_id)并重建所有索引。

利用索引提高查询速度 
首先我们在x字段上建立索引:db.hello.ensuerIndex(x : 1); 
然后再查询:db.helo.find(x:8, _id: 0, x: 1).explain(); 会有如下输出: 

db.hello.find(_id:0).explain();

        "cursor" : "BtreeCursor _id_",
        "isMultiKey" : false,
        "n" : 0,
        "nscannedObjects" : 0,
        "nscanned" : 1,
        "nscannedObjectsAllPlans" : 0,
        "nscannedAllPlans" : 1,
        "scanAndOrder" : false,
        "indexOnly" : false,
        "nYields" : 0,
        "nChunkSkips" : 0,
        "millis" : 7,
        "indexBounds" : 
                "start" : 
                        "_id" : 0
                ,
                "end" : 
                        "_id" : 0
                
        ,
        "server" : "vm-09b7-b009:27017"


其中"indexOnly" : true代表着此次查询启用了索引。 
一般的查询不会利用索引,除非满足以下条件, 
A) 必须列出要返回的字段名,这样才能决定是否需要启用索引 
B) 必须显示的指出不显示_id字段 _id : 0 


管道和聚合

MongoDB的聚合管道将MongoDB文档在一个管道处理完毕后将结果传递给下一个管道处理。管道操作是可以重复的。

表达式:处理输入文档并输出。表达式是无状态的,只能用于计算当前聚合管道的文档,不能处理其它的文档。

这里我们介绍一下聚合框架中常用的几个操作:
$project:修改输入文档的结构。可以用来重命名、增加或删除域,也可以用于创建计算结果以及嵌套文档。
$match:用于过滤数据,只输出符合条件的文档。$match使用MongoDB的标准查询操作。
$limit:用来限制MongoDB聚合管道返回的文档数。
$skip:在聚合管道中跳过指定数量的文档,并返回余下的文档。
$unwind:将文档中的某一个数组类型字段拆分成多条,每条包含数组中的一个值。
$group:将集合中的文档分组,可用于统计结果。
$sort:将输入文档排序后输出。
$geoNear:输出接近某一地理位置的有序文档。

管道操作符实例
$project实例

db.article.aggregate(
     $project : 
        title : 1 ,
        author : 1 ,
    
 );
这样的话结果中就只还有_id,tilte和author三个字段了,默认情况下_id字段是被包含的,如果要想不包含_id话可以这样:
db.article.aggregate(
     $project : 
        _id : 0 ,
        title : 1 ,
        author : 1
    );

$match实例
db.articles.aggregate( [
                         $match :  score :  $gt : 70, $lte : 90   ,
                         $group:  _id: null, count:  $sum: 1   
                       ] );
$match用于获取分数大于70小于或等于90记录,然后将符合条件的记录送到下一阶段$group管道操作符进行处理。

$skip实例
db.article.aggregate(
     $skip : 5 );
经过$skip管道操作符处理后,前五个文档被"过滤"掉。


聚合:

统计hello集合中age的和按gender分组:

hello集合结构为:

db.hello.find();
"_id" : ObjectId("540429243086331d7e0de6aa"), "name" : "zhaoliu"
"_id" : ObjectId("540429243086331d7e0de6ac"), "name" : "wangwu", "age" : 30, "gender" : "male"
"_id" : ObjectId("540429243086331d7e0de6ad"), "name" : "lisi", "age" : 40, "gender" : "male"
"_id" : ObjectId("540429263086331d7e0de6ae"), "name" : "qianqi", "age" : 40, "gender" : "female", "email" : [  "123@qq.com",  "456@qq.com" ]
"_id" : ObjectId("540429243086331d7e0de6ab"), "name" : "zhangsan", "age" : 50, "gender" : "female"
查询统计:

db.hello.aggregate([$group : _id : "$gender", sum_age : $sum : "$age"]);

输出结果为:


        "result" : [
                
                        "_id" : "male",
                        "sum_age" : 70
                ,
                
                        "_id" : "female",
                        "sum_age" : 90
                ,
                
                        "_id" : null,
                        "sum_age" : 0
                
        ],
        "ok" : 1
以上实例类似sql语句: select gender, count(age) from mycol group by gender;


同理类似的求平均数,最大值,最小值...的表达式如下:

表达式描述实例
$sum计算总和。db.mycol.aggregate([$group : _id : "$gender", num_tutorial : $sum : "$xxxx"])
$avg计算平均值db.mycol.aggregate([$group : _id : "$gender", num_tutorial : $avg : "$xxxx"])
$min获取集合中所有文档对应值得最小值。db.mycol.aggregate([$group : _id : "$gender", num_tutorial : $min : "$xxxx"])
$max获取集合中所有文档对应值得最大值。db.mycol.aggregate([$group : _id : "$gender", num_tutorial : $max : "$xxxx"])
$push在结果文档中插入值到一个数组中。db.mycol.aggregate([$group : _id : "$gender", xxxx : $push: "$xxxx"])
$addToSet在结果文档中插入值到一个数组中,但不创建副本。db.mycol.aggregate([$group : _id : "$gender", xxxx : $addToSet : "$xxxx"])
$first根据资源文档的排序获取第一个文档数据。db.mycol.aggregate([$group : _id : "$gender", first_xxxx : $first : "$xxxx"])
$last根据资源文档的排序获取最后一个文档数据db.mycol.aggregate([$group : _id : "$gender", last_xxxx : $last : "$xxxx"])


删除:

将删除所有记录

db.hello.remove(),

将删除指定记录

db.hello.remove(name:"Rod")

利用java操作MongoDB 

从官网下载java版本的驱动并将其加入classpath。 
3.1,建立连接 
Mongo m = new Mongo(); // 连接本地的27017端口
// or
Mongo m = new Mongo( "localhost" ); // 默认端口27017
// or
Mongo m = new Mongo( "localhost" , 27017 ); // 指定主机与端口
DB db = m.getDB( "mydb" ); // 取得指定的库
 
3.2,用户认证(可选) 
在登录数据库时,在数据库启动时可以指定是否需要认证(mongod --auth .....), 
此时连接时就需要用户名和密码(--serviceUser xx --servicePassword xx),如下: 
boolean auth = db.authenticate(myUserName, myPassword);

3.3,得到指定库的所有集合 
Set<String> colls = db.getCollectionNames();
    for (String s : colls)
           System.out.println(s);
       

3.4,得到指定集合 
DBCollection coll = db.getCollection("user");

3.5,插入文档 
BasicDBObject doc = new BasicDBObject();   
doc.put("name", "MongoDB");
doc.put("type", "database");
doc.put("count", 1);

BasicDBObject info = new BasicDBObject();
info.put("x", 203);
info.put("y", 102);

doc.put("info", info);
coll.insert(doc); // 保存
   
3.6,查询集合中的第一份文档 
DBObject myDoc = coll.findOne();

3.7,增加多份文档 
for (int i=0; i < 100; i++)
           coll.insert(new BasicDBObject().append("i", i));
       

3.8,统计集合中文档的条数 
System.out.println(coll.getCount());

3.9,利用游标得到所有记录 
DBCursor cur = coll.find();
       while(cur.hasNext())
           System.out.println(cur.next());
       

3.10,查询指定文档
BasicDBObject query = new BasicDBObject();
query.put("x", 8);
cur = coll.find(query);
while(cur.hasNext())
       System.out.println(cur.next());
 
有时个为了方便我们也可以写成这种形式: 
query.put("i", new BasicDBObject("$gt", 20).append("$lte", 30)); 
// i.e. 20 < i <= 30
   
3.11,创建一个索引 
coll.createIndex(new BasicDBObject("i", 1)); 
// create index on "i", ascending

3.12,查询集合中的索引列表 
List<DBObject> list = coll.getIndexInfo();

3.13,得到所有的库名 
Mongo m = new Mongo();
m.getDatabaseNames();

3.14,删除一个库 
m.dropDatabase("my_new_db");

4,MongoDB类型与java类型  
4.1,自动成生主键 
ObjectId id = new ObjectId();
ObjectId copy = new ObjectId(id);


4.2,正则表达式的运用 
Pattern john = Pattern.compile("joh?n", CASE_INSENSITIVE);
BasicDBObject query = new BasicDBObject("name", john);
// finds all people with "name" matching /joh?n/i
DBCursor cursor = collection.find(query);

4.3,日期类型 
java.util.Date now = new java.util.Date();
BasicDBObject time = new BasicDBObject("ts", now);
collection.save(time);

4.4, 数组 
ArrayList x = new ArrayList();
x.add(1);
x.add(2);
x.add(new BasicDBObject("foo", "bar"));
x.add(4);
BasicDBObject doc = new BasicDBObject("x", x);
  
4.5,二进制数据 
字节数组(byte[])会被自动包装成二进制类型。 
   
4.6,时间 
Timestamp会被转换成BSONTimestamp。 
   
4.7,源代码数据 
源代码数据代表了javascript代码。CodeWScope类代表了这种数据。



安全认证功能(不推荐)

为了使MongoDB安全生效,必须以--auth参数启动,同时还要在db.system.users文档中增加用户信息 

如果在db.system.users中无数据,即使以--auth启动,对于本地的访问也是允许的。 

增加一个新的用户:db.addUser("foo", "bar"); 
查询刚才增加的用户:db.system.users.find(); 
删除用户:db.removeUser("foo"); 
为了提高查询性能和安全性,也可增加只读用户:db.addUser("foo", "bar", true); 

此时虽然用户已经有了,但是怎么去测试是否生效了呢?先进行认证测试一下,如下: 
db.auth("foo", "bar"); 
如果在服务端打出1则表示认证通过,客户端可以使用了,如果是0表示不通过 

那么在客户端可以通过如下代码进行认证: 
db.authenticate("foo", "bar".toCharArray()); 
由此可知,所有的用户信息是加在数据库上面的,每个库都有自己的用户信息。 
如此便可增加安全认证功能了.


http://www.cnblogs.com/dennisit/archive/2013/01/29/2881006.html

Memcached和Mongodb一些看法,以及结合应用有什么好处

  Memcached

  Memcached的优势我觉得总结下来主要体现在:

  1) 分布式。可以由10台拥有4G内存的机器,构成一个40G的内存池,如果觉得还不够大可以增加机器,这样一个大的内存池,完全可以把大部分热点业务数据保存进去,由内存来阻挡大部分对数据库读的请求,对数据库释放可观的压力。

  2) 单点。如果Web服务器或App服务器做负载均衡的话,在各自内存中保存的缓存可能各不相同,如果数据需要同步的话,比较麻烦(各自自己过期,还是分发数据同步?),即使数据并不需要同步,用户也可能因为数据的不一致而产生用户体验上的不友好。

  3) 性能强。不用怀疑和数据库相比确实是,根源上还是内存的读写和磁盘读写效率上几个数量级的差距。有的时候我们在抱怨数据库读写太差的情况下可以看看磁盘的IO,如果确实是瓶颈的话装啥强劲的数据库估计也档不了,强不强无非是这个数据库多少充分的利用了内存。

  但是也不太建议在任何情况下使用Memcached替代任何缓存:

  1) 如果Value特别大,不太适合。因为在默认编译下Memcached只支持1M的Value(Key的限制到不是最大的问题)。其实从实践的角度来说也 不建议把非常大的数据保存在Memcached中,因为有序列化反序列化的过程,别小看它消耗的CPU。说到这个就要提一下,我一直觉得 Memcached适合面向输出的内容缓存,而不是面向处理的数据缓存,也就是不太适合把大块数据放进去拿出来处理之后再放进去,而是适合拿出来就直接给输出了,或是拿出来不需要处理直接用。

  2) 如果不允许过期,不太适合。Memcached在默认情况下最大30天过期,而且在内存达到使用限制后它也会回收最少使用的数据。因此,如果我们要把它当 作static变量的话就要考虑到这个问题,必须有重新初始化数据的过程。其实应该这么想,既然是缓存就是拿到了存起来,如果没有必定有一个重新获取重新缓存的过程,而不是想着它永远存在。

  在使用Memcached的过程中当然也会有一些问题或者说最佳实践:

  1) 清除部分数据的问题。Memcached只是一个Key/Value的池,一个公共汽车谁都可以上。我觉得对于类似的公共资源,如果用的人都按照自己的规 则来的话很容易出现问题。因此,最好在Key值的规范上上使用类似命名空间的概念, 每一个用户都能很明确的知道某一块功能的Key的范围,或者说前缀。带来的好处是我们如果需要清空的话可以根据这个规范找到我们自己的一批Key然后再去 清空,而不是清空所有的。当然有人是采用版本升级的概念,老的Key就让它过去吧,到时候自然会清空,这也是一种办法。不过Key有规范总是有好处的,在 统计上也方便一点。

  2) Value的组织问题。也就是说我们存的数据的粒度,比如要保存一个列表,是一个保存在一个键值还是统一保存为一个键值,这取决于业务。如果粒度很小的话最好是在获取的时候能批量获取,在保存的时候也能批量保存。对于跨网络的调用次数越少越好,可以想一下,如果一个页面需要输出100行数据,每一个数据都需要获取一次,一个页面进行上百次连接这个性能会不会成问题。

  那么Memcached主要用在哪些功能上呢?

  其实我觉得平时能想到在内存中做缓存的地方我们都可以考虑下是不是可以去适用分布式缓存,但是主要的用途还是用来在前端或中部挡一下读的需求来释放Web服务器App服务器以及DB的压力。

下面讲讲Mongodb。

Mongodb

  Mongodb是一款比较优良的非关系型数据库的文档型的数据库。它的优势主要体现在:

  1) 开源。意味着即使我们不去改也可以充分挖掘它,MS SQL除了看那些文档,谁又知道它内部如何实现。

  2) 免费。意味着我们可以在大量垃圾服务器上装大量的实例,即使它性能不怎么高,也架不住非常多的点啊。

  3) 性能高。其它没比较过,和MS SQL相比,同样的应用(主要是写操作)一个撑500用户就挂了,一个可以撑到2000。在数据量上到百万之后,即使没索引,MS SQL的插入性能下降的也一塌糊涂。其实任何事物都有相对性的,在变得复杂变得完善了之后会牺牲一部分的性能,MS SQL体现的是非常强的安全性数据完整性,这点是Mongodb办不到的。

  4) 配置简单并且灵活。在生产环境中对数据库配置故障转移群集和读写分离的数据库复制是很常见的需求,MS SQL的配置繁琐的步骤还是很恐怖的,而Mongodb可以在五分钟之内配置自己所需要的故障转移组,读写分离更是只需要一分钟。灵活性体现在,我们可以配置一个M一个S,两个M一个S(两个M写入的数据会合并到S上供读取),一个M两个S(一个M写入的数据在两个S上有镜像),甚至是多个M多个S(理论上可以创建10个M,10个S,我们只需要通过轮询方式随便往哪个M上写,需要读的时候也可以轮训任意一个S,当然我们要知道不可能保证在同一时间所有的 S都有一致的数据)。那么也可以配置两个M的对作为一套故障转移群集,然后这样的群集配置两套,再对应两个S,也就是4个M对应2个S,保证M点具有故障 转移。

  5) 使用灵活。在之前的文章中我提到甚至可以通过SQL到JS表达式的转换让Mongodb支持SQL语句的查询,不管怎么说Mongodb在查询上还是很方便的。

  之前也说过了,并不是所有数据库应用都使用采用Mongodb来替代的,它的主要缺点是:

  1) 开源软件的特点:更新快,应用工具不完善。由于更新快,我们的客户端需要随着它的更新来升级才能享受到一些新功能,更新快也意味着很可能在某一阶段会缺乏某个重要功能。另外我们知道MS SQL在DEV/DBA/ADM多个维度都提供了非常好的GUI工具对数据库进行维护。而Mongodb虽然提供了一些程序,但是并不是非常友好。我们的 DBA可能会很郁闷,去优化Mongodb的查询。

  2) 操作事务。Mongodb不支持内建的事务(没有内建事务不意味着完全不能有事务的功能),对于某些应用也就不适合。不过对于大部分的互联网应用来说并不存在这个问题。

  在使用Mongodb的过程中主要遇到下面的问题:

  1) 真正的横向扩展?在使用Memcached的过程中我们已经体会到这种爽了,基本可以无限的增加机器来横向扩展,因为什么,因为我们是通过客户端来决定键值保存在那个实例上,在获取的时候也很明确它在哪个实例上,即使是一次性获取多个键值,也是同样。而对于数据库来说,我们通过各种各样的方式进行了 Sharding,不说其它的,在查询的时候我们根据一定的条件获取批量的数据,怎么样去处理?比如我们按照用户ID去分片,而查询根本不在乎用户ID, 在乎的是用户的年龄和教育程度,最后按照姓名排序,到哪里去取这些数据?不管是基于客户端还是基于服务端的Sharding都是非常难做的,并且即使有了 自动化的Sharding性能不一定能有保障。最简单的是尽量按照功能来分,再下去就是历史数据的概念,真正要做到实时数据分散在各个节点,还是很困难。

  2) 多线程,多进程。在写入速度达不到预期的情况下我们多开几个线程同时写,或者多开几个Mongodb进程(同一机器),也就是多个数据库实例,然后向不同 的实例去写。这样是否能提高性能?很遗憾,非常有限,甚至可以说根本不能提高。为什么使用Memcached的时候多开线程可以提高写入速度?那是因为内 存数据交换的瓶颈我们没达到,而对于磁盘来说,IO的瓶颈每秒那么几十兆的是很容易达到的,一旦达到这个瓶颈了,无论是开多少个进程都无法提高性能了。还 好Mongodb使用内存映射,看到内存使用的多了,其实我对它的信心又多了一点(内存占用多了我觉得CPU更容易让它不闲着),怕就怕某个DB不使用什 么内存,看着IO瓶颈到了,内存和CPU还是吃不饱。

  Memcached和Mongodb的配合

  其实有了Memcached和Mongodb我们甚至可以让80%以上的应用摆脱传统关系型数据库。我能想到它们其实可以互相配合弥补对方的不足:

  Memcached适合根据Key保存Value,那么有的时候我们并不知道需要读取哪些Key怎么办呢?我在想是不是可以把Mongodb或 说数据库当作一个原始数据,这份原始数据中分为需要查询的字段(索引字段)和普通的数据字段两部分,把大量的非查询字段保存在Memcached中,小粒 度保存,在查询的时候我们查询数据库知道要获取哪些数据,一般查询页面也就显示20-100条吧,然后一次性从Memcached中获取这些数据。也就是 说,Mongodb的读的压力主要是索引字段,而数据字段只是在缓存失效的时候才有用,使用Memcached挡住大部分实质数据的查询。反过来说,如果我们要清空Memcached中的数据也知道要清空哪些Key。



以上是关于Mongo基本操作的主要内容,如果未能解决你的问题,请参考以下文章

java操作mongodb(连接池)(转)

mongo索引

线程间通信:Queue

拓扑排序(入门)

mongoDB实战mongo集群---主从复制篇

mongo系列mongo基本概念及操作