前面的知识点里, 我们学习了 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 的使用非常灵活和强大.
好啦, 到这里呢, 分享内容就结束了, 各位如果感觉不错的话, 请多多点赞支持哦...
来源: https://blog.csdn.net/luyaran/article/details/79850518