当前位置: 首页 > news >正文

吃透那些面试:MongoDb的索引

一、说句闲话

        一个月以前失业了,对于做了十几年程序的人来说。 本来算不上什么问题继续找合适的工作就是了。但是经历了一个月的找工作,才发现自己掉入了一个前所未有的行业衰退期。几乎所有大厂都在裁员,小厂可能直接死掉了。据说地铁人多少了,虽然我有几年没有坐地铁了。但是可以想像得到地铁上疏松的人,甚至有座。。

        面试是很卷的, 首先面试机会就很少。面试问的问题又很尖锐。有些曾经了如指掌的问题,背不下来,就直接挂了。面试官甚至比自己年龄小十几岁,或许他并没有那么多的经验或者无法理解背下来意义不如理解思想来的有价值。但是在那一刻,人家是“官”。

        有同样遭遇的朋友,私信聊聊吧~~

二、是这么问的

 MongoDB 用过吧?用在什么场景呢?

我根据业务实际使用场景描述了使用的场景,以及当时选择mongo 的原因。接下来就问

MongoDB的索引有哪些? 是怎么实现的?

我的确有点懵, 因为我确实不知道具体有哪些,更不知道实现原理。没有背到这块八股文。就根据使用过程中的一些经验,比如唯一索引, 复合索引,进行了简单描述。得到的反馈是不好的,感觉有点看不起我了。

今天就来整理一下关于MongoDB 的索引吧。

三、MongoDB 索引

1、Mongo的索引必要性

首先mongo 是文档型的数据库。 在没有索引的情况下会和mysql 一样扫描全表,进行查询。因为mongo不是行存储的, 所以在文档较大, 深度较深的情况下, 同样没有索引, 同样数据量的情况下, mongo的效率是远低于mysql 的,mysql 在几十万数据时, 即使没有索引,也回比较快,但是mongo 就会很慢。 所以在mongo的使用过程中,必须使用合适的索引。

2、Mongo索引类型

MongoDB支持多种类型的索引,包括单字段索引、复合索引、多key索引、文本索引等,每种类型的索引有不同的使用场合。

下面分别举例。并且以如下数据进行说明

mongo-9552:PRIMARY> db.person.find()
{ "_id" : ObjectId("571b5da31b0d530a03b3ce82"), "name" : "jack", "age" : 19 }
{ "_id" : ObjectId("571b5dae1b0d530a03b3ce83"), "name" : "rose", "age" : 20 }
{ "_id" : ObjectId("571b5db81b0d530a03b3ce84"), "name" : "jack", "age" : 18 }
{ "_id" : ObjectId("571b5dc21b0d530a03b3ce85"), "name" : "tony", "age" : 21 }
{ "_id" : ObjectId("571b5dc21b0d530a03b3ce86"), "name" : "adam", "age" : 18 }
{ "_id" : ObjectId("571b5da31b0d530a03b3ce82"), "name" : "jack", "age" : 19 }
{ "_id" : ObjectId("571b5dae1b0d530a03b3ce83"), "name" : "rose", "age" : 20 }
{ "_id" : ObjectId("571b5db81b0d530a03b3ce84"), "name" : "jack", "age" : 18 }
{ "_id" : ObjectId("571b5dc21b0d530a03b3ce85"), "name" : "tony", "age" : 21 }
{ "_id" : ObjectId("571b5dc21b0d530a03b3ce86"), "name" : "adam", "age" : 18 }

  (1)单字段索引

db.person.createIndex( {age: -1} ) 

上述语句针对age创建了单字段索引,其能加速对age字段的各种查询请求,是最常见的索引形式,MongoDB默认创建的id索引也是这种类型。

{age: 1} 代表升序索引,也可以通过{age: -1}来指定降序索引,对于单字段索引,升序/降序效果是一样的。

可以直接类比mysql 的单子段索引。 使用B-tree 实现索引的存储。

(2)复合索引

复合索引是Single Field Index的升级版本,它针对多个字段联合创建索引,先按第一个字段排序,第一个字段相同的文档按第二个字段排序,依次类推,如下针对age, name这2个字段创建一个复合索引。

db.person.createIndex( {age: 1, name: 1} ) 

上述索引对应的数据组织类似下表,与{age: 1}索引不同的时,当age字段相同时,在根据name字段进行排序

复合索引 在使用中,也是左匹配原则,如果只根据  name 进行查询 , 则该索引无效。

在实际使用过程中。 建立如下索引效率会更高

db.person.createIndex( { name: 1,age: 1} ) 

因为age 的区分度并不高, 假如年龄从1-120岁, 那么区分度也就120个值。 假如总数据量12000000万 那么,在 根据age 锁定范围之后, 依然有100000个文档需要根据name 扫描。 如果反过来, name 区分度比较大。 可以更快的锁定名字范围, 甚至一下就查到。 

(3)多key索引 

当索引的字段为数组时,创建出的索引称为多key索引,多key索引会为数组的每个元素建立一条索引,比如person表加入一个habbit字段(数组)用于描述兴趣爱好,需要查询有相同兴趣爱好的人就可以利用habbit字段的多key索引。

{"name" : "jack", "age" : 19, habbit: ["football, runnning"]}
db.person.createIndex( {habbit: 1} )  // 自动创建多key索引
db.person.find( {habbit: "football"} )

(4)其他索引

哈希索引(Hashed Index)是指按照某个字段的hash值来建立索引,目前主要用于MongoDB Sharded Cluster的Hash分片,hash索引只能满足字段完全匹配的查询,不能满足范围查询等。

地理位置索引(Geospatial Index)能很好的解决O2O的应用场景,比如『查找附近的美食』、『查找某个区域内的车站』等。

文本索引(Text Index)能解决快速文本查找的需求,比如有一个博客文章集合,需要根据博客的内容来快速查找,则可以针对博客内容建立文本索引。

四、索引优化

1、DB Profiler


MongoDB支持对DB的请求进行profiling,目前支持3种级别的profiling。

0: 不开启profiling
1: 将处理时间超过某个阈值(默认100ms)的请求都记录到DB下的system.profile集合 (类似于mysql、redis的slowlog)
2: 将所有的请求都记录到DB下的system.profile集合(生产环境慎用)
通常,生产环境建议使用1级别的profiling,并根据自身需求配置合理的阈值,用于监测慢请求的情况,并及时的做索引优化。

如果能在集合创建的时候就能『根据业务查询需求决定应该创建哪些索引』,当然是最佳的选择;但由于业务需求多变,要根据实际情况不断的进行优化。索引并不是越多越好,集合的索引太多,会影响写入、更新的性能,每次写入都需要更新所有索引的数据;所以你system.profile里的慢请求可能是索引建立的不够导致,也可能是索引过多导致。

​2、查询计划

mongo-9552:PRIMARY> db.person.find({age: 18}).explain()

可以得到执行计划, 类似于 mysql 的 explain sql 

{"queryPlanner" : {"plannerVersion" : 1,"namespace" : "test.person","indexFilterSet" : false,"parsedQuery" : {"age" : {"$eq" : 18}},"winningPlan" : {"stage" : "FETCH","inputStage" : {"stage" : "IXSCAN","keyPattern" : {"age" : 1},"indexName" : "age_1","isMultiKey" : false,"isUnique" : false,"isSparse" : false,"isPartial" : false,"indexVersion" : 1,"direction" : "forward","indexBounds" : {"age" : ["[18.0, 18.0]"]}}},"rejectedPlans" : [ ]},"serverInfo" : {"host" : "localhost","port" : 9552,"version" : "3.2.3","gitVersion" : "b326ba837cf6f49d65c2f85e1b70f6f31ece7937"},"ok" : 1
}

相关文章:

  • 【MATLAB源码-第84期】基于matlab的802.11a标准的OFDM系统误码仿真对比QPSK,16QAM。
  • Linux网络编程:传输层协议|UDP
  • yolox-何为EMA?
  • JAVA生成随机姓名(小白也能看得懂)
  • IDEA2023.2单击Setting提示报错:Cannot get children Easy Code
  • 【论文解读】A Progress Report: The Alliance for Open Media and the AV1 Codec
  • odoo16版本的render变更
  • 学习Uni-app开发小程序Day23
  • 06.部署jpress
  • Sublime Text 基础教程(个人总结)
  • 机器学习之爬山算法(Hill Climbing Algorithm)
  • 【OpenVINO™】在C#中使用 OpenVINO™ 部署 YOLOv10 模型实现目标
  • 多线程笔记
  • 浙江大学数据结构MOOC-课后习题-第九讲-排序3 Insertion or Heap Sort
  • git多人开发,不用merge的操作方法,阿里codeup
  • 时间复杂度分析经典问题——最大子序列和
  • 【391天】每日项目总结系列128(2018.03.03)
  • docker python 配置
  • idea + plantuml 画流程图
  • Iterator 和 for...of 循环
  • Javascript 原型链
  • Linux编程学习笔记 | Linux多线程学习[2] - 线程的同步
  • PHP变量
  • Python中eval与exec的使用及区别
  • tweak 支持第三方库
  • WinRAR存在严重的安全漏洞影响5亿用户
  • 高度不固定时垂直居中
  • 深度学习中的信息论知识详解
  • 新手搭建网站的主要流程
  •  一套莫尔斯电报听写、翻译系统
  • 一些基于React、Vue、Node.js、MongoDB技术栈的实践项目
  • 说说我为什么看好Spring Cloud Alibaba
  • ​​​​​​​​​​​​​​汽车网络信息安全分析方法论
  • ​2020 年大前端技术趋势解读
  • #define 用法
  • $jQuery 重写Alert样式方法
  • (C++17) std算法之执行策略 execution
  • (Note)C++中的继承方式
  • (补充):java各种进制、原码、反码、补码和文本、图像、音频在计算机中的存储方式
  • (第30天)二叉树阶段总结
  • (附源码)springboot宠物医疗服务网站 毕业设计688413
  • (附源码)ssm基于jsp的在线点餐系统 毕业设计 111016
  • (过滤器)Filter和(监听器)listener
  • (机器学习-深度学习快速入门)第一章第一节:Python环境和数据分析
  • (亲测成功)在centos7.5上安装kvm,通过VNC远程连接并创建多台ubuntu虚拟机(ubuntu server版本)...
  • (十六)Flask之蓝图
  • (四)图像的%2线性拉伸
  • (学习总结)STM32CubeMX HAL库 学习笔记撰写心得
  • .net 8 发布了,试下微软最近强推的MAUI
  • .NET C# 操作Neo4j图数据库
  • .NET Core中如何集成RabbitMQ
  • .NET 回调、接口回调、 委托
  • .NET 解决重复提交问题
  • .NET 应用启用与禁用自动生成绑定重定向 (bindingRedirect),解决不同版本 dll 的依赖问题
  • .NET8使用VS2022打包Docker镜像