MongoDB学习04–性能分析及索引操作

MongoDB中的性能分析及索引操作

  1. 性能分析函数explain();
    1. 语法:cursor.explain(verbose),verbose为true或者1的时候会输出所有的执行计划和旧的执行计划;
    2. 一般是跟在find()操作后使用(eg:db.mycoll.find().explain()),它的执行速度决定于实际查询的速度,尽管explain()会额外生成一系列候选的执行计划,但是跟实际查询的性能相差不大;
    3. explain函数的输出结果;                                                          
    4. explain()函数输出结果含义:
      1. cursor:查找使用的cursor类型和索引名称;
      2. isMultiKey:是否是联合索引;
      3. n:返回记录的个数;
      4. nscannedObjects:遍历的文档对象的个数;
      5. nscanned:遍历的文档的个数;
      6. nscannedObjectsAllPlans: <num>;
      7. nscannedAllPlans: <num>;
      8. scanAndOrder: <boolean>;
      9. indexOnly: <boolean>;
      10. nYields: <num>;
      11. nChunkSkips: <num>;
      12. millis:消耗的时间,单位是ms;
      13. indexBounds:索引的范围,如果为空表示没有用到索引;
      14. llPlans:产生的所有的候选的执行计划;
      15. oldPlan:旧的执行计划;
      16. server:主机名和端口号<host:port>;
  2. 创建/删除索引,及前后效率的对比;
    1. 创建索引语法:db.mycoll.ensureIndex(keypattern[,options]);
      1. keypattern:是索引的键;
      2. options:可以取name,unique,dropDups,sparse等值;
      3. 只有在某个列上没有索引才会创建,如果存在索引的话就不再创建;
      4. 默认创建的是b-tree索引;
      5. 可以在子文档上创建索引:db.mycoll.ensureIndex({“object.attritude”:1});
    2. 没有创建索引时,查找name=index10000的效率:db.index.find({“name”:”index10000″}).explain();使用的是BasicCursor即没有索引,扫描了10w条记录后返回1条记录,消耗了192ms;              
    3. 在name列创建一个名为idx_index_name的索引:db.index.ensureIndex({name:1}, {name:”idx_index_name”}),如果不指定第二个参数中的name的话,则系统会自动生成一个索引的名称;          
    4. 查看创建索引后的效率,发现使用了Btree索引,一共扫描一条记录,返回1条记录,并且瞬间返回;                                            
    5. 删除索引语法:db.mycoll.dropIndex(index),可以根据名称删除索引,或者根据创建时指定的键值;
      1. db.mycoll.dropIndex(“indexName”):根据索引名称删除;
      2. db.mycoll.dropIndex({ “indexKey” : 1 }):根据key值删除索引;
      3. 如果要删除某个集合下所有的索引,则使用db.mycoll.dropIndexes(),但是无法删除系统自动创建的基于_id列的索引,它由数据库自己维护;
    6. 重建索引:db.mycoll.reIndex(),是把原来的索引删掉后重建;                                                        
  3. 唯一索引:
    1. 语法:db.mycoll.ensureIndex(keypattern, {unique:true});
    2. 创建一个基于name列的唯一索引unq_index_name:db.index.ensureIndex({name:1}, {name:”unq_index_name”, unique:true});
    3. 如果某一个列上已经存在的重复值,在这样的列上创建唯一索引的话可以使用dropDups选项,这样系统会保留第一条记录,删除剩下重复的记录:db.mycoll.ensureIndex(keypattern, {unique:true, dropDups:true});
    4. 如果某一个列是唯一列,插入数据时没有插入此列的话会保存一个null值,一个唯一列中只能有一个null值;(这点与关系型数据库不同,关系型数据库中唯一列中可以保存null值,但是在mongodb中null值作为了一个值来比较)
  4. 组合索引:
    1. 语法与普通索引相同,只是需要多指定几个列:db.mycoll.ensureIndex({key1:value1, key2:value2, …});
    2. 键后面的数字表示索引的存储顺序,其中1表示升序,-1表示降序.在随机访问的时候意义不大,但是在排序或者是范围查询时很重要;
    3. 如果在a,b,c三列上创建了这索引,则以a,ab,abc开头的条件都可以使用到此索引;
  5. 稀疏索引:
    1. Sparse indexes only contain entries for documents that have the indexes field, any document that is missing the field is not indexes;
    2. By contrast, non-sparse indexes contrain all documents in collection, and store null values for documents that do not contrain the indexes field;
    3. 语法:db.mycoll.ensureIndex(keypattern, {sparse:true});
    4. 稀疏索引与非稀疏索引的对比;
  6. hint:
    1. 查询优化器会选择最优的执行计划,而且第一次执行后的执行计划将会被保存;如果需要使用自己指定的查询方案的话可以使用hint;
    2. 语法:cursor.hint(index);
      1. db.mycoll.find().hint(“index_name”):通过指定索引名称来实现hint;
      2. db.mycoll.find().hint({key:value}):通过指定创建索引时的key的顺序来实现hint,可以之前通过db.mycoll.getIndexes()查看;
    3. 查看hint的性能:db.mycoll.find().hint(index).explain();
  7. 注意事项:
    1. MongoDB中的索引是大小写敏感的;
    2. 当更新对象时,只有在索引上的这些key发生变化才更新,所以提高了性能;当对象增长了或者移动时,所有的索引都必须更新,会很慢;
    3. 索引的信息会保存在system.indexes集合中,运行db.system.indexes.find()可以查看这些数据;
    4. 索引的字段的大小限制目前是800bytes,大于之后可以在这个字段上创建索引,但是该字段不会被索引;
    5. 如果数据集合比较小(4M),可以联合使用limit()和sort()函数而不需要创建索引来查询数据;
———————- 10w条测试数据 ———————-
db.index.remove()
for(var i = 0; i < 100000; i++){
     db.index.insert({name:”index”+i, age:i})
}
———————- 10w条测试数据 ———————-
———————- 稀疏索引与非稀疏索引的对比 ———————-
— 1.测试数据;
db.sparse.insert({name:”sparse1″, age:20})
db.sparse.insert({name:”sparse2″})
db.sparse.find()
— 2.在age列创建一个稀疏索引;
db.sparse.ensureIndex({age:1}, {name:”idx_sparse_age”, sparse:true})
db.sparse.getIndexes()
— 3.查看数据,稀疏索引中不包含列为null的文档,只有稀疏索引中的文档才会被返回;
db.sparse.find()
db.sparse.find().sort({age:1})
db.sparse.find({name:{$ne:0}})
— 4.删除稀疏索引,并创建一般的索引;
db.sparse.dropIndex(“idx_sparse_age”)
db.sparse.ensureIndex({age:1}, {name:”idx_sparse_age”})
db.sparse.getIndexes()
— 查看数据,非稀疏索引中包含了列为null的文档;
———————- 稀疏索引与非稀疏索引的对比 ———————-

MongoDB学习03–聚合操作,游标的使用及排序分页操作

聚合操作,游标的使用及排序分页操作

  1. 聚合操作:
    1. count:查看符合某些条件的集合中记录的个数;
      1. 查看集合user的记录数:db.user.count();                                        
      2. 查看集合user中年龄大于20的记录的个数:db.user.count({“age”:{$gt:20}});                                  
    2. distinct:查看集合中某个属性的独立存在的个数;
      1. 获得不同年龄的数组:db.user.distinct(“age”);                               
      2. 获得不同年龄值的个数:db.user.distinct(“age”).length;                           
    3. group:对集合按照某一属性分组,形成一个K-V模型;
      1. 语法:db.mycoll.group( { key : …, initial: …, reduce : … [, finalize: …] [, condition: …] } );
        1. key:分组的键;
        2. initial:每个分组使用的初始化函数;
        3. reduce:此函数的第一个参数是当前的文档对象,第二个参数是上次函数(即initial函数)操作的累计对象,有多少个文档,$reduce就会调用多少次;
        4. finalize:每一组文档执行完之后,会触发执行的一个函数,参数是上次函数(即initial函数)操作的累计对象;
        5. condtion:过滤条件;
      2. 按照年龄分组的例子:db.user.group({key:{age:true}, initial:{user:[]}, reduce:function(cur, prev){prev.user.push(cur.name);}}),push函数是把一个值加入到数组中;       
      3. 按照年龄分组,过滤掉年龄大于25的记录,并且添加一个用户数量的属性count:db.user.group({key:{age:true}, initial:{user:[]}, reduce:function(cur, prev){prev.user.push(cur.name);}, finalize:function(prev){prev.count=prev.user.length;}, condition:{age:{$lte:25}}});                               
    4. mapReduce:其实是一种编程模型,用在分布式计算中.可以把问题划分为多个不同的部分并分发到不同的服务器并行处理,每台服务器都把分配给自己的一部分处理完成后把结果集返回给主服务器,主服务器汇总结果后完成问题的处理;
      1. 语法:db.mycoll.mapReduce( mapFunction , reduceFunction , <optional params> );
        1. mapFunction:映射函数,里面会调用emit(key, value),集合会按照指定的key进行映射分组;
        2. reduceFunction:;简化函数,会对map分组后的数据进行分组简化.其中reduce(key, value)中的key就是emit中的key,value为emit分组后的emit(value)的集合,它有一个或者多个对应于键的文档组成;
        3. 可选参数;
      2. 原理:map首先将文档映射到集合并操作文档,这一步可能产生多个键和多个值或者什么也没有(文档中要处理的值为空).而后按照键分组,并将产生的值组成列表放到对应的键中,reduce则把列表中的值化简为一个值,这个值被返回,而后继续按键分组,进行化简,直到每个键在列表中只有一个值为止,这个值也就是最终结果;
      3. 例子;
  2. 游标的使用:
    1. 使用while循环遍历输出:var cursor=db.user.find(); while(cursor.hasNext()) { printjson(cursor.next()); };(其中hasNext()函数判断是否有记录,next()函数返回下一条记录)             
    2. pringjson()函数是内置函数,能够将结果输出为json格式;
    3. 得到数据集集合,遍历完之后游标便销毁;
    4. 使用forEach()函数遍历:db.user.find().forEach(printjson);                          
    5. 像访问数组一样使用游标:var cursor=db.user.find();printjson(cursor[0]);(会把访问的数据都加载ram中,如果数据量很大的话非常消耗内存)                
    6. 直接转换成数组访问:var array=db.user.find().toArray();array[0];                            
  3. 排序操作:
    1. 语法:db.mycoll.find().sort({col:value, …});
      1. col:表示按照排序的列;
      2. value:可以取1和-1,1表示升序,-1表示降序;
      3. 查找所有记录,并按照年龄升序,名称降序显示:db.user.find().sort({“age”:1, “name”:-1}),select name, age from user order by age desc, name asc;              
    2. 还可以通过其它方法实现,db.mycoll.find([query], [fields])操作是根据query过滤记录,先后显示fields里面指定的列,如果query给默认的话({}就是默认的参数),只需要指定列名及排序方式即可:select name, age from user order by age desc, name asc;                                 
    3. 先按年龄升序,然后按照名称升序排序:select name from user order by age asc, name asc;                      
  4. 分页操作:
    1. 使用db.mycoll.find().skip(n)操作和db.mycoll.find().limit(n)实现,前者是跳过之前多少条记录,后者是显示多少条记录:skip((pageIndex-1) * pageSize).limit(pageSize);
    2. 排序后的结果,每页两条记录,显示第二页:db.user.find().sort({“age”:1, “name”:-1}).skip(2).limit(2);                 
— 测试数据;
db.user.drop()
db.user.insert({“name”:”jack”, “age”:20})
db.user.insert({“name”:”joe”, “age”:22})
db.user.insert({“name”:”mary”, “age”:26})
db.user.insert({“name”:”kobe”, “age”:20})
db.user.insert({“name”:”wade”, “age”:22})
db.user.insert({“name”:”yi”, “age”:22})
db.user.find()
— mapReduce操作;
1.创建map函数;
function map(){ emit(this.age, {count:1}); }
2.创建reduce函数;
function reduce(key, value){
     var result = {count:0};
     for (var i = 0; i < value.length; i++){
          result.count += value[i].count;
     }
     return result;
}
3.执行mapReduce操作,并把结果集存放在集合collection中;
db.user.mapReduce(map, reduce, {out:”collection”})
result:存放集合名;
timeMillis:执行的时间,单位是毫秒;
input:传入文档的个数;
emit:emit函数被调用的次数;
reduce:reduce函数被调用的次数;
output:最后返回文档的个数;
4.查看collection中的结果;
db.collection.find()

MongoDB学习02–增删改查操作

MongoDB的增删改查操作

  1. INSERT操作:
    1. 单条插入(可以使用js的语法);                                          
    2. 批量插入:mongodb中并没有提供批量插入的语法,但是高级语言中提供了与mongodb批量插入的接口,也可以通过for循环来模拟;
    3. insert和save的区别:
      1. 如果不使用_id列,两个函数没有区别都是插入数据;
      2. 如果使用了_id列,insert插入_id重复的话会报错;save插入_id重复的话会调用upsert,即存在就更新,不存在就插入;
  2. FIND操作:
    1. 根据条件查询:[>, >=, <, <=, !=, =]分别对应[$gt, $gte, $lt, $lte, $ne, 无]关键字;                    
    2. 多条件查询:[and, or, in, notin]分别对应[无, $or, $in, $nin]关键字;                      
    3. 正则表达式查询;                                                       
    4. 使用$where子句查询;                                                   
    5. findOne()函数返回满足条件的第一条记录或者是null:printjson(db.user.findOne({name:”joe”}));                                 
  3. UPDATE操作:
    1. 整体更新;                                                            
    2. 局部更新,使用修改器$inc和$set来更新某一个字段;
      1. $inc:increase的缩写,即在原来值的基础上增加$inc指定的值,可以用于自增的主键;                                
      2. $set:即设置某个列的值为$set指定的值;                                         
    3. upsert操作:update操作的第一个参数是查询条件,第二个参数是要更新的值,如果设置第三个参数为true的话,就表示如果存在记录就更新,如果不存在就插入;                           
    4. multi操作:批量更新,如果update操作匹配到了多条记录,默认情况下只更新第一条,如果需要全部更新的话,需要把update操作中第四个参数设置为true;                           
  4. REMOVE操作:
    1. 根据条件删除:db.mycoll.remove(query);                                       
    2. 全部删除:db.mycoll.remove();                                                        
— insert操作;
var user={“name”:”jack”, “password”:”12345″, “age”:20, “address”:{“province”:”shanghai”, “city”:”shanghai”}, “favourite”:[“apple”, “banana”]}
db.user.insert(user);
user.name=”joe”
user.age=21
user.address={“province”:”beijing”, “city”:”beijing”}
user.favourite=[“sanguosha”, “dota”]
db.user.insert(user)
db.user.find()
— find操作;
1.查找年龄大于等于21岁的集合;
db.user.find({“age”:{$gte:21}})
2.查找年龄等于20岁的集合;
db.user.find({“age”:20})
3.查找名称为jack而且省份为shanghai的集合:name=’jack’ and province=’shanghai’;
db.user.find({“name”:”jack”, “address.province”:”shanghai”})
4.查找省份为上海或者北京的集合:province=’shanghai’ or province=’beijing’;
db.user.find({$or:[{“address.province”:”shanghai”}, {“address.province”: “beijing”}]})
5.查找省份在上海或者北京的集合:province in (‘shanghai’, ‘beijing’);
db.user.find({“address.province”:{$in:[“shanghai”, “beijing”]}})
6.查找省份不在上海的集合:province not in (‘shanghai’);
db.user.find({“address.province”:{$nin:[“shanghai”]}})
7.查找姓名以’j’开头并且以’e’结尾的集合:name like ‘j%e’;
db.user.find({“name”:/^j/, “name”:/e$/})
8.条件复杂的时候,可以使用$where子句,其实就是js的函数,查找名称为’jack’的集合:name=’jack’;
db.user.find({$where:function(){return this.name == ‘jack’}})
— update操作;
1.整体更新一条记录;
db.user.find({“name”:”jack”})
var model=db.user.findOne({“name”:”jack”})
model.age=30
db.user.update({“name”:”jack”}, model)
db.user.find({“name”:”jack”})
2.使用$inc修改器更新一个字段;
db.user.find({“name”:”jack”})
db.user.update({“name”:”jack”}, {$inc:{“age”:-5}})
db.user.find({“name”:”jack”})
3.使用$set修改器更新一个字段;
db.user.find({“name”:”jack”})
db.user.update({“name”:”jack”}, {$set:{“age”:20}})
db.user.find({“name”:”jack”})
4.upsert操作:查找到就更新,没有就插入;
db.user.find({“name”:”kobe”})
db.user.update({“name”:”kobe”}, {$set:{“age”:20}}, true)
db.user.find({“name”:”kobe”})
5.multi操作:批量更新记录;
db.user.find()
db.user.update({“age”:{$gte:20}}, {$inc:{“age”:5}}, false, true)
db.user.find()
— remove操作;
1.根据条件删除;
db.user.find()
db.user.remove({“name”:”kobe”})
db.user.find()
2.全部删除;
db.user.remove()
db.user.find()
db.user.count()