前面的知识点里,我们学习了MongoDB中索引的运用。但是我们知道,无论在那个数据库中,索引的使用都是有限制的。接下来我们就看一下,在MongoDB中,索引的限制。首先就是额外的开销。每个索引占据一定的存储空间,在进行插入,更新和删除操作时也需要对索引进行操作。所以,如果你很少对集合进行读取操作,建议不使用索引。
再来就是内存(RAM)的使用。由于索引是存储在内存(RAM)中,你应该确保该索引的大小不超过内存的限制。如果索引的大小大于内存的限制,MongoDB会删除一些索引,这将导致性能下降。
还有就是通常意义下的查询限制了。在MongoDB中,索引不能被以下的查询使用:
- 正则表达式及非操作符,如 $nin, $not, 等。
- 算术运算符,如 $mod, 等。
- $where 子句
所以,检测我们的语句是否使用索引是一个好的习惯,可以用explain来查看。
再来就是索引键限制了。从2.6版本开始,如果现有的索引字段的值超过索引键的限制,MongoDB中不会创建索引。还有插入文档超过索引键限制。如果文档的索引字段值超过了索引键的限制,MongoDB不会将任何文档转换成索引的集合。与mongorestore和mongoimport工具类似。
最后我们再来看一下在MongoDB中,缩印的最大范围:
- 集合中索引不能超过64个
- 索引名的长度不能超过128个字符
- 一个复合索引最多可以有31个字段
好啦,索引限制到这里就介绍完毕了。接下来我们看一下ObjectId。在前面的记录中,我们已经使用了MongoDB 的对象 Id(ObjectId)。接下来就是看一下ObjectId的结构。首先ObjectId 是一个12字节 BSON 类型数据,有以下格式:
- 前4个字节表示时间戳
- 接下来的3个字节是机器标识码
- 紧接的两个字节由进程id组成(PID)
- 最后三个字节是随机数。
我们都知道,在MongoDB中存储的文档必须有一个"_id"键。这个键的值可以是任何类型的,默认是个ObjectId对象。在一个集合里面,每个文档都有唯一的"_id"值,来确保集合里面每个文档都能被唯一标识。MongoDB采用ObjectId,而不是其他比较常规的做法(比如自动增加的主键)的主要原因,因为在多个服务器上同步自动增加主键值既费力还费时。接下来,我们就来尝试创建新的ObjectId。使用以下代码生成新的ObjectId:
>newObjectId = ObjectId()
上面的语句返回以下唯一生成的id:
ObjectId("5349b4ddd2781d08c09890f3")
我们也可以使用生成的id来取代MongoDB自动生成的ObjectId:
>myObjectId = ObjectId("5349b4ddd2781d08c09890f4")
然后呢,我们再来尝试创建文档的时间戳。由于 ObjectId 中存储了 4 个字节的时间戳,所以你不需要为你的文档保存时间戳字段,你可以通过 getTimestamp 函数来获取文档的创建时间:
>ObjectId("5349b4ddd2781d08c09890f4").getTimestamp()
以上代码将返回 ISO 格式的文档创建时间:
ISODate("2014-04-12T21:49:17Z")
然后呢,在某些情况下,我们可能需要将ObjectId转换为字符串格式。所以我们可以使用下面的代码,尝试将 ObjectId 转换为字符串:
>new ObjectId().str
以上代码将返回Guid格式的字符串:
5349b4ddd2781d08c09890f3
最后呢,我们来看一下MAP Reduce。首先呢,Map-Reduce是一种计算模型,简单的说就是将大批量的工作(数据)分解(MAP)执行,然后再将结果合并成最终结果(REDUCE)。MongoDB提供的Map-Reduce非常灵活,对于大规模数据分析也相当实用。废话不多说,我们先来看一下它的基本语法结构:
>db.collection.mapReduce( function() {emit(key,value);}, //map 函数 function(key,values) {return reduceFunction}, //reduce 函数 { out: collection, query: document, sort: document, limit: number } )
我们要知道,使用 MapReduce 要实现两个函数 Map 函数和 Reduce 函数,Map 函数调用 emit(key, value),遍历 collection 中所有的记录, 将 key 与 value 传递给 Reduce 函数进行处理。Map 函数必须调用 emit(key, value) 返回键值对。然后呢,咱们再来看一下参数说明:
- map :映射函数 (生成键值对序列,作为 reduce 函数参数)。
- reduce 统计函数,reduce函数的任务就是将key-values变成key-value,也就是把values数组变成一个单一的值value。。
- out 统计结果存放集合 (不指定则使用临时集合,在客户端断开后自动删除)。
- query 一个筛选条件,只有满足条件的文档才会调用map函数。(query。limit,sort可以随意组合)
- sort 和limit结合的sort排序参数(也是在发往map函数前给文档排序),可以优化分组机制
- limit 发往map函数的文档数量的上限(要是没有limit,单独使用sort的用处不大)
下面有一个实例,在集合 orders 中查找 status:"A" 的数据,并根据 cust_id 来分组,并计算 amount 的总和:
接下来我们来使用它,看看效果,我们来考虑以下文档结构存储用户的文章,文档存储了用户的 user_name 和文章的 status 字段:
>db.posts.insert({ "post_text": "luyaran,best girl。", "user_name": "mark", "status":"active" }) WriteResult({ "nInserted" : 1 }) >db.posts.insert({ "post_text": "luyaran,best girl。", "user_name": "mark", "status":"active" }) WriteResult({ "nInserted" : 1 }) >db.posts.insert({ "post_text": "luyaran,best girl。", "user_name": "mark", "status":"active" }) WriteResult({ "nInserted" : 1 }) >db.posts.insert({ "post_text": "luyaran,best girl。", "user_name": "mark", "status":"active" }) WriteResult({ "nInserted" : 1 }) >db.posts.insert({ "post_text": "luyaran,best girl。", "user_name": "mark", "status":"disabled" }) WriteResult({ "nInserted" : 1 }) >db.posts.insert({ "post_text": "luyaran,best girl。", "user_name": "luyaran", "status":"disabled" }) WriteResult({ "nInserted" : 1 }) >db.posts.insert({ "post_text": "luyaran,best girl。", "user_name": "luyaran", "status":"disabled" }) WriteResult({ "nInserted" : 1 }) >db.posts.insert({ "post_text": "luyaran,best girl。", "user_name": "luyaran", "status":"active" }) WriteResult({ "nInserted" : 1 })
接下来,我们将在 posts 集合中使用 mapReduce 函数来选取已发布的文章(status:"active"),并通过user_name分组,计算每个用户的文章数:
>db.posts.mapReduce( function() { emit(this.user_name,1); }, function(key, values) {return Array.sum(values)}, { query:{status:"active"}, out:"post_total" } )
运行上述 mapReduce 输出结果为:
{ "result" : "post_total", "timeMillis" : 23, "counts" : { "input" : 5, "emit" : 5, "reduce" : 1, "output" : 2 }, "ok" : 1 }
结果表明,共有 5 个符合查询条件(status:"active")的文档,在map函数中生成了 5 个键值对文档,最后使用reduce函数将相同的键值分为 2 组。我们来看一下具体参数说明:
- result:储存结果的collection的名字,这是个临时集合,MapReduce的连接关闭后自动就被删除了。
- timeMillis:执行花费的时间,毫秒为单位
- input:满足条件被发送到map函数的文档个数
- emit:在map函数中emit被调用的次数,也就是所有集合中的数据总量
- ouput:结果集合中的文档个数(count对调试非常有帮助)
- ok:是否成功,成功为1
- err:如果失败,这里可以有失败原因,不过从经验上来看,原因比较模糊,作用不大
然后呢,我们使用 find 操作符来查看 mapReduce 的查询结果:
>db.posts.mapReduce( function() { emit(this.user_name,1); }, function(key, values) {return Array.sum(values)}, { query:{status:"active"}, out:"post_total" } ).find()
以上查询显示如下结果,两个用户 luyaran 和 mark 有两个发布的文章:
{ "_id" : "mark", "value" : 4 } { "_id" : "luyaran", "value" : 1 }
用类似的方式,MapReduce可以被用来构建大型复杂的聚合查询。Map函数和Reduce函数可以使用 JavaScript 来实现,使得MapReduce的使用非常灵活和强大。
好啦,到这里呢,分享内容就结束了,各位如果感觉不错的话,请多多点赞支持哦。。。