关于后端:从正则搜索重新认识索引

45次阅读

共计 2965 个字符,预计需要花费 8 分钟才能阅读完成。

最近一些查问接口加载工夫逐步变长,发现都是因为在筛选条件中有正则搜寻,然而这些字段咱们也都依照惯例的形式设置了索引。而非正则搜寻则没有影响,所以最直观的想法是:正则搜寻在应用索引的中央不分明,须要补一补。

什么是索引

数据库的外围性能是为了保留数据还有与之对应的对数据操作的能力:增删改查。当数据量逐步增大,比方用户数量:百万、千万、甚至数亿的时候,咱们须要从外面找到一些复合条件的数据就显得很简单了,当然能够一个一个找,然而置信没人会这么做,因为会十分慢。这个时候怎么办呢?

其实与大家学习数据库时的例子 字典 ,是一样的。咱们查找这些数据通常是依据某些 特定的条件 。比方对于字典,咱们通常是通过拼音查找,而不是通过:那些字的词语蕴含 掘金

那么字典是怎么解决的呢?把咱们最罕用的搜寻条件 拼音 独自列出来,放在最后面,这样在查字典的时候,就只须要翻前几页就好了,找到须要的字再 跳转 到对应的页数就能够了。索引是什么呢?

索引就是:将局部字段值取出来,独自保护成一个查问很快的数据结构

索引原理

这个查问很快的数据结构简略的来说就是创立一颗树(树直观点就比拟像嵌套很多的 json)。当然这颗树为了查问数据,做了很多优化,其模样就像这样下方图片一样。具体的算法名称:B+ Tree,大家能够去理解一下。这样咱们在查问的时候,就相当于二分法猜数字一样,很快就能定位到数据,即使上亿的数据也很快。

那为什么正则搜寻加了索引还是很慢呢?

正则搜寻过程

如果咱们认真想一下,就会发现其实很简略。因为索引的原理就是将这个字段的值,独自拿进去,依照更快的查找格局存储起来。索引查找的过程就是,间接对新建设的数据比拟就好了。然而 正则不是简略的比拟大小,还须要运算 才晓得最终的后果。所以对于加了索引的正则匹配(含糊匹配相似),也还是会将所有内容一个一个的进行正则运算完才会有后果,这天然十分慢了。

论断大略就是这样,上面我应用 mongodb 看看理论过程。个别的查问索引都是 B+ Treemongodbsql也是,所以是想通的。

mongo 的数据测试

# 查看测试数据总数
db.game.count();
# 50407 条数据

# mongo 取得查问过程的办法是增加 explain 就好了
db.game.find({"name":"麻辣英雄"}).explain("allPlansExecution");

数据结构

{
  "_id": "5590b3f9bac548696b8b45cf",
  "des": "《麻辣英雄》是一款历史大乱斗题材的半即时制 RPG 手游。高精度还原长城、皇宫等场景,百类 Q 萌武将,千种英雄组合,独创配角技能,更有巨型 BOSS 战、攻城战等特色玩法,让手游辞别无趣",
  "name": "麻辣英雄",
  "download": 1
  // 还有很多其余字段
}

防止后果太长,删除了大部分过程解释。只留下了要害的几个数据。查问命令如下:

  1. 一般查问 db.game.find({"name":"麻辣英雄"}).explain("allPlansExecution");
  2. 正则查问 db.game.find({"name":/ 麻辣英雄 /}).explain("allPlansExecution");

各种状况测试后果

不加索引 + 一般查问

[
  {
    "executionStats": {
      "executionSuccess": true,
      "nReturned": 1,
      "executionTimeMillis": 20,
      "totalKeysExamined": 0,
      "totalDocsExamined": 50407,
    }
  }
]

不加索引 + 正则查问

[
  {
    "executionStats": {
      "executionSuccess": true,
      "nReturned": 1,
      "executionTimeMillis": 31,
      "totalKeysExamined": 0,
      "totalDocsExamined": 50407,
    }
  }
]

增加索引 + 一般查问

[
  {
    "executionStats": {
      "executionSuccess": true,
      "nReturned": 1,
      "executionTimeMillis": 0,
      "totalKeysExamined": 1,
      "totalDocsExamined": 1,
    }
  }
]

增加索引 + 正则查问

[
  {
    "executionStats": {
      "executionSuccess": true,
      "nReturned": 1,
      "executionTimeMillis": 40,
      "totalKeysExamined": 50407,
      "totalDocsExamined": 1,
    }
  }
]

汇总

由解释后果的 executionSuccessnReturned 可知,均胜利返回了一条数据。而后其次要过程别离是:查问过程中索引与文档扫描的数量。

题目 索引扫描行数 文档扫描行数 执行耗时
无索引一般查问 0 50407 20
无索引正则查问 0 50407 30
有索引一般查问 1 1 0
有索引正则查问 50407 1 40
  1. 没有增加索引就会全表扫描,一个一个去读取原始数据 - 文档。个别可认为是寄存在硬盘中的,硬盘读取可是比内存慢超多的。此处没有特地慢可能是数据太少,缓存在内存中了。
  2. 加了索引之后就会发现,文档扫描局部都是1,所以索引匹配过程都是在独自构建的数据结构下面,升高了大量的硬盘读写。
  3. 然而加了索引,正则还是扫描了全副字段,只不过此时就是应用的索引这个构造保留的字段,而不是从磁盘读取。因为正则只有匹配了才晓得后果,所以还是会很慢,底层一切都是简略编程操作,没有黑魔法。

索引大小

上方是测试数据的状况,所有数据大小 127Mdesc 的索引(形容文本)就有 42.8M。而 download 数字类型的索引只有 221.2KB

从大小咱们也能够看出,索引建设是须要独自寄存一次数据的,所以对于文本字段建设所以肯定须要思考分明。对于大文本建设索引有一下问题:

  1. 索引自身可能是意义不大,因为对于大文本个别都是为了含糊匹配
  2. 会导致索引过大,可能内存都不能一次加载实现,那么速度会更加慢

有什么播种呢

索引自身没有什么黑魔法,简略的来讲就是冗余查问条件的局部数据,将其结构成更加复合查问的构造,这样就能解决个别状况下超大数据的查问性能问题。

正则查问或者含糊匹配之类,也没有其余优化,数据库查问也就像写一般代码一样,只有当代码真正执行才晓得后果,所以须要谨慎应用,特地是对于利用端的接口!那须要搜寻怎么办呢?

含糊搜寻权重排序这些个别都属于:全文搜寻。全文搜寻的数据库会采纳齐全不同的计划去解决含糊搜寻,其基根本逻辑是:

  1. 对所有的内容进行分词,而后值自身去关联这些分词的后果。比方:我爱北京 能够分成: 我爱北京,wo,爱,北京 等几个词语
  2. 查找的时候先把查找内容进行分词,失去一些列的可能组合,而后再去匹配,这样就只是分词与关联的过程了,所以很快

当然一些数据库也提供全文索引(比方 mongo 就有针对文本类型 text 索引,能够实现搜寻,然而目前不反对中文。

不同的需要肯定须要正当切换不同的技术计划。切勿执着的在一个数据库,一个语言,一个框架上死磕。正当的使用与取舍才是最好的。

对于 mongo 的收费在线体验

想学习理解 mongo 的能够登录 mongo 的官方网站,而后就会主动跳转到其云服务,能够收费创立一个 2 个正本集mongo 集群 供应用。

正文完
 0