索引

假设索引为:{a:1,b:1,c:1,…,z:1}: 实际上是有了:{a:1},{a:1,b:1},{a:1,b:1,c:1}…等索引的。
但是使用{b:1}、{a:1,c:1}等索引的查询是会被优化的,只有使用索引前部的查询才能使用该索引。

正则表达式

对于/ Jon Skeet / regex,mongo将完全扫描索引中的键,然后获取匹配的文档,这可能比收集扫描更快。 对于/ ^ Jon Skeet / regex,mongo将只扫描索引中以正则表达式开始的范围,这将更快。 但其他的即使有索引,也不会命中索引,比说:需要查询Mail中的user中含有z的: /.z./ /^.z./ 这种查询是不会命中到索引的,当数据量很大,速度很慢,总之,^后的条件必须明确,不能^.* ^[a-z]之类开头的。

随着集合的增长,如果查询中有用到排序时,就要创建索引了。如果对没有索引的键用sort,mongodb需要将所有的数据提到内存中进行排序,这个是很影响性能的。

操作接口

  • 查看集合的所有索引: getIndexes();
  • 查看集合索引的总大小: totalIndexSize();
  • 查看数据库中所有索引信息:db.system.indexes.find();
  • 对DB启用分片: sh.enableSharding();
  • 对集合进行分片: sh.shardCollection();
  • db.collectionName.find();
  • db.collection.find(query).explain(); 返回的信息如下,
{“cursor” : “BasicCursor”,//命中的索引,当为BasicCursor时表示没有命中任何索引
“indexBounds” : [ ],//所使用的索引,被设置为表示为索引扫描的关键边界。
“nscanned” : 57594,// 扫描的数据条数。
“nscannedObjects” : 57594,//扫描对象的数。
“nYields” : 2 ,//查询所产生的锁的个数。
“n” : 3 ,//返回文档的数量
“millis” : 108,// 数据库中执行查询的时间
“indexOnly” : false//是否使用了covered index。
}

DB操作

show dbs //展示服务器上的db列表,只有进行过相关数据操作的数据库才会被创建并显示
use testdb1 //切换至testdb1,进入shell默认在’test’数据库下
show collections //展示数据库下的集合列表
db.dropDatabase() //删除当前数据库
db.testcoll1.drop() //删除当前集合
testdb1=db.getSiblingDB(‘testdb1’) //获取testdb1的引用,切换到其它库时就可以通过testdb1.xxx来跨库操作
coll=db.testcoll1 //获取集合应用,可用于简化后续集合操作
db.stats() //查看当前数据库状态
db.testcoll1.status() //查看集合状态:索引、分片等
db.testcoll1.getIndexes() //查看集合的索引列表
db.testcoll1.createIndex({a:1,b:-1},{name:’a_1_b_-1’,unique:true,background:true}) //在集合’testcoll1’上创建a顺序b倒序的复合唯一索引,语法createIndex(keys,options),复合索引中keys不能使用hash索引,options中name表示索引名称,示例就是mongo索引名称的默认规则,uniqe指定是否唯一索引,默认非唯一,background指定在后台逐步创建索引,这样不会阻塞其它db操作,在已有大数据量的集合中应使用该选项
db.repairDatabase() //当仅删除集合时,MongoDB不会 同时释放已占用的磁盘空间,因此需要执行repair手动释放
DBQuery.shellBatchSize = 100 //设置每页查询数量

分片操作

sh.enableSharding(“testdb1”) //在指定数据库上启用分片
sh.shardCollection(collection,key,{a:”hashed”}) //进行集合分片,片键为a字段的hash索引,可选的还有普通索引和范围索引
db.runCommand({shardCollection:"testdb1.testcoll1",key:{a:"hashed"},numInitialChunks:1024}) //通过在admin库执行该命令建立分片集合可以预创建指定数量的chunk块,用以在生产环境减少chunk块初始分裂频率
sh.status() //展示所有集合的分片信息,包括chunks情况,当集群较庞大时填入{"verbose":1}参数来显示所有信息
db.testcoll1.getShardDistribution() //展示集合的每个分片大小状态,可用于查看集合是否均匀分布
use config;db.collections.find({_id:"testdb.testcoll"}) //展示集合的分片基本信息,包括片键,是否分布均匀等
//手动均衡时用到的命令
db.adminCommand({'moveChunk' : coll, 'find' : find, 'to' : to_shard});//迁移chunks
db.adminCommand({movePrimary:database,to:to_shard});//修改主分片

集合操作

db.coll.insert({a:1,b:2}) //插入文档
db.coll.find().limit().pretty();
db.coll.find({a:1,b:2},{a:1,b:1})  //select a,b from coll where a=1 and b=2,属性为非嵌套文档字段时可以省略引号,第二个参数指定要返回的字段,默认全部字段(_id字段除非指定不返回,否则默认都会返回)。返回结果是cursor对象,可以继续使用sort()、limit()、count()等方法进一步处理,下面介绍常用的查询函数符号
db.coll.find({$or:[{a:{$lt:10,$gte:1}},{b:{$exists:true}}]}) //where (a<10 and a>=1) or b<>’’,类似的函数符号还有$eq,$ne,$in,$nin,$and,$not,$nor等等
db.coll.find({a:{$regex:/test.*/}}) //where a like ‘test%’,通过正则表达式实现模糊匹配
db.coll.find({$where:"this.c!=null && this.c.length>3"}) //通过js表达式查询,可以使用js中的原生函数,但注意$where需要遍历所有文档并转换为json去匹配js表达式,使用时一定要考虑性能问题
db.coll.update({a:1},{$set:{b:2},$inc:{c:1}},{multi:false,upsert:false}) //update coll set b=2,c=c+1 where a=1,可选参数upsert(无匹配文档时是否组合条件插入:{a:1,b:2,c:1})、multi(是否更新所有匹配文档)。其他更新函数有$unset,$setOnInsert,$ currentDate,$min,$max。与update类似的还有findAndModify用于实现原子操作

复杂操作

$match:用于按条件筛选文档,语法与普通的find查询条件相同
$project:用于指定要返回的字段或者添加新字段,例:{$project:{a:1,b:{$eq:[“$a”,”test”]}}},上述指定返回字段”a”并添加字段”b”计算字段“a”的值是否等于”test”
$group:与sql中的group by类似,例:{$group:{_id:”$a”,count:{$sum:”$b”},c:{$first:”$c”}}},上述指定以”a”字段分组,字段”count”计算每个分组下字段“b”数值总和,并在分组中返回第一个”c”字段的值。(在3.2版本中由于不支持$count管道命令,因此只能使用{$group:{_id:null,count:{sum:1}}}的格式来计算管道某一阶段的文档总数)
$unwind:展开数组字段,当需要聚合一个数组字段的值时,可以先使用$unwind拆分数组然后使用$addToSet合并
$sort:文档排序,与cursor.sort()类似
$skip:跳过指定个数的文档
$limit:限制文档返回数量
$out:将聚合的结果写入指定的集合中,当我们需要通过导出聚合后的数据时可以使用该操作先转存至其他集合,再使用mongoexport普通导出

数据导出

nohup /usr/bin/mongodump --host 192.xxx --port 40xx -d dbName -c collectionName  -o /dump/mongo >dump.log 2>&1 &

DUMP数据解析

    FileInputStream sourceFileIn = new FileInputStream(this.sourceFile);
    BufferedInputStream sourceBuffIn = new BufferedInputStream(sourceFileIn);
    while (sourceBuffIn.available() > 0) {
        // while ((line = sourceFileBr.readLine()) != null) {
        // 任务停止
        if (-1 == status) {
            break;
        }

        // Bson格式 mongo数
        BSONObject bsonObject = bsonDecoder.readObject(sourceBuffIn);
        //BSONObject bsonObject = bsonDecoder.readObject(line.getBytes(StandardCharsets.UTF_8));
        // 当前行数
        count++;
        if (count <= startLine) {// 实际从 startLine+1 行开始处理
            continue;
        }

        Map item = bsonObject.toMap();

        item.remove("_id");
        docs.add(item);

        // 达到批量值,开始处理
        if (docs.size() == batchSize) {
            try {
                doMigrate(type, date, docs);
            } catch (Exception e) {
                throw new ServiceException(e);
            }
            // 记录整体偏移量
            FileWriter storeFileWrite = new FileWriter(storeFile, false);
            storeFileWrite.write(count.toString());
            storeFileWrite.flush();
            storeFileWrite.close();
            // 重置缓存
            docs = new ArrayList<>(batchSize);
            LOG.info("do batch migrate {}", count);
        }
    }    

    sourceBuffIn.close();
    sourceFileIn.close();

查询计划

`MongoDB中通过cursor.explain()和aggregate([],{explain:true})来展示查询计划。

executionStats 描述获胜计划完整的查询执行信息。必须在 executionStats 或 allPlansExecution 详细模式下运行explain才显示executionStats 相关信息(cursor.explain(true)/cursor.explain("executionStats”)/cursor.explain(“allPlansExecution”))。对于写入操作,是指将要执行修改的信息,但不会真实修改数据库。

nReturned 查询条件匹配到的文档数量。
executionTimeMillis 查询计划选择和查询执行所需的总时间(以毫秒为单位)。executionTimeMillis 对应于早期版本的MongoDB中cursor.explain() 返回的millis字段。
totalKeysExamined 扫描的索引条目数。totalKeysExamined 对应于早期版本的MongoDB中cursor.explain() 返回的 nscanned字段。
totalDocsExamined 扫描的文档数。totalDocsExamined 对应于早期版本的MongoDB中cursor.explain() 返回的 nscannedObjects字段。
executionStages 阶段树形式展示获胜计划完整的执行信息。即,一个阶段可以有一个inputStage或多个inputStages。
works 查询执行阶段执行的“工作单元”的数量。 查询执行阶段将其工作分为小单元。 “工作单位”可能包括检查单个索引键,从集合中提取单个文档,将投影应用于单个文档或执行内部记账。
advanced 返回到父阶段的结果数。
needTime 未将中间结果返回给其父级的工作循环数。 例如,索引扫描阶段可以花费一个工作周期来寻找索引中的新位置而不是返回索引关键字; 这个工作周期将计入needTime而不是advanced。
needYield 存储层请求查询系统产生锁定的次数。
isEOF 执行阶段是否已到达流的结尾。如果为true或1,则执行阶段已到达流末尾;如果为false或0,则阶段可能仍会返回结果。 例如,有限制的查询,其执行阶段包含LIMIT阶段,其中查询的输入阶段为IXSCAN。 如果查询返回超过指定的限制,LIMIT阶段将报告isEOF:1,但其基础IXSCAN阶段将报告isEOF:0。
inputStage.keysExamined 对于扫描索引的查询执行阶段(例如IXSCAN),keysExamined是在索引扫描过程中检查的入站和越界键的总数。 如果索引扫描由单个连续范围的键组成,则只需要检查入站键。 如果索引边界由若干键范围组成,则索引扫描执行过程可以检查越界键,以便从一个范围的末尾跳到下一个范围的开头。
inputStage.docsExamined 在查询执行阶段扫描的文档数。
allPlansExecution 包含在计划选择阶段为获胜和拒绝计划捕获的部分执行信息。仅当explain在allPlansExecution详细模式下运行时,该字段才会出现。