导语
ES 现在已经越来越火,很多公司会把 MySQL 里面的数据导入到 ES,用 ES 来做海量数据的实时查询。那为什么 MySQL 做不了海量数据的实时查询呢?为什么 ES 可以?本文将从原理上来回答这个问题。
基本分析
经过分析和对比,得出结论如下:
1、ES 天生分布式架构,天然支持海量数据的分片和查询,而 MySQL 不行;
2、MySQL 和 ES 底层索引结构导致即便是单片数据查询,ES 也更适合做查询引擎。
# 为什么 MySQL 做不了海量数据的实时查询?
MySQL 的架构就不是分布式的架构,就算基于一些开源组件或者自研组件做了分库分表,也只能解决数据的存储问题,解决不了问题的查询问题。
目前所有的分库分表中间件都会要求必须带上分库分表 id 去查询,也就是分库分表之后,你的查询,最终还是要落到某个库某张表去做单库单表的实时查询。
# ES 为什么能够做海量数据的实时查询?
ES 天生就是分布式架构,假如有一个学生表超过 10 亿条数据,导入 ES。首先第一件事情就是你要给这些数据做分片,假如分了 10 个片后,用户想基于 name 去查询,ES 是怎么做的?
这里我们假设是最简单的部署架构,ES 的节点 A 接收到查询请求后,会把请求转发到其他 datanode,然后每个 datanode 接收到数据后,会在本机进行数据查询,然后将查询结果 id,score 返回节点 A,然后由节点 A 对所有返回结果再进行排序和分页,最后节点 A 再去各个 datanode 依据 id 拿到原始数据合并后返回给用户。
# 为什么 MySQL 的分库分表中间件不像 ES 这样用一个节点去合并和处理数据呢?
个人理解,不是 MySQL 或者分库分表中间件不想做,而是根本做不了。原因是单库单表 MySQL 也支持不了复杂查询条件下的实时查询,比如任意列的相互组合查询。
想要快速查询,避免全表扫描,就需要索引,MySQL 可以对单列增加索引,也可以对多列增加索引,或者按照一定顺序建立多个字段的组合索引。但 MySQL 的索引存在很多问题:
-
MySQL 单列索引的问题是通常用户查询条件不会仅仅只有一个;
-
MySQL 多列索引问题是 MySQL 引擎执行是只会选择最严格那一个索引执行,然后在其“中间结果集”上进行其他条件的过滤,这意味着建多列索引实际上可能是无用的;
-
MySQL 组合索引的问题是建立组合索引,是要按照顺序的,而且执行的时候是从左到右匹配执行的,一旦查询条件不符合索引组合次序,或者查询条件多变,组合索引就会毫无用处。
大家都知道 MySQL 的默认索引数据结构是 B+tree,每增加一个索引,都是增加一个 B+tree,就是因为这种数据结构导致了上面种种问题。
ES索引结构
ES 的底层是 Lucene,Lucene的索引叫倒排索引(Inverted Index),下面是一个倒排索引的示例图。
倒排索引示例图
倒排索引(Inverted Index):倒排索引是实现“单词-文档矩阵”的一种具体存储形式,通过倒排索引,可以根据单词快速获取包含这个单词的文档列表。
比如上图中,lucene 这个单词对应的 documentId list 就是 2,3,10,35,92;倒排索引主要由两个部分组成:“单词词典”和“倒排列表”。
单词词典(Lexicon):搜索引擎的通常索引单位是单词,单词词典是由文档集合中出现过的所有单词构成的字符串集合,单词词典内每条索引项记载单词本身的一些信息以及指向“倒排列表”的指针。
倒排列表(PostingList):倒排列表记载了出现过某个单词的所有文档的文档列表及单词在该文档中出现的位置信息,每条记录称为一个倒排项(Posting),根据倒排列表,即可获知哪些文档包含某个单词。
倒排文件(Inverted File):所有单词的倒排列表往往顺序地存储在磁盘的某个文件里,这个文件即被称之为倒排文件。关于这些概念之间的关系,通过下图可以比较清晰的看出来。
FST数据结构
怎么从词典里面快速找到单词呢。这里可以使用的数据结构有很多,lucene 使用了一种叫 FST 的数据结构:
FST 数据结构
上面讲的单个字段的搜索,假如是多个字段的搜索,Lucene 是如何对多个字段的搜索结果进行合并的呢?
假如我们有下面三个倒排列表需要进行合并。
在 Lucene 中会采用下列顺序进行合并:
1、在 termA 开始遍历,得到第一个元素 docId=1;
2、在 termB 的倒排列表中,查找 docId=1 是否存在。由于倒排表在 Lucene 是用 skiplist 数据结构存储的,所以定位某个 docid 是否存在,速度会非常快,不需要一个一个遍历;
3、如果在 termB 的倒排列表不存在,执行步骤 5;
4、如果在 termB 的倒排表中存在,继续在 termC 中查找是否存在,如果 termC 中存在,则该文档放入结果集;如果 termC 中不存在,执行步骤 5;
5、从 termA 中获取下一个元素,然后重复过程 2-4。
整个合并步骤我可以发现,如果某个链很短,拿它作为第一遍历 list,会大幅减少比对次数,可以很快的返回最终的结果。
综上所述从倒排索引的定位、查询、合并整个流程和 MySQL 的 B+tree 索引相比,Lucene 的倒排索引明显更适合做数据的查询,此外倒排索引的合并灵活性也解决了 MySQL B+tree 索引难以支持多条件查询的问题。
正文完
作者:湛蓝鹭白
https://me.csdn.net/zhanlanlubai
本文编辑:筷子
本文地址: https://www.xiongge.club/biancheng/elk-biancheng/1583.html
转载请注明:熊哥club → Elasticsearch和MySQL查询原理分析与对比
©熊哥club,本站推荐使用的主机:阿里云,CDN建议使用七牛云。
关注微信公众号『熊哥club』
免费提供IT技术指导交流
关注博主不迷路~