Elasticsearch简介及与MySQL查询原理对比

开头

Elasticsearch是一个被广泛使用的搜索引擎,通过本文你可以了解:

1.Elasticsearch适合做什么,不适合做什么,对于你判断是否需要使用Elasticseaerch给出一个参考。

2.Elasticsearch与MySQL在索引查询上的原理分别是什么,告诉你Elasticsearch为什么查询比MySQL快。

3.如果你要使用Elasticsearch,应该如何做架构设计,Elasticsearch与MySQL如何相互配合。

4.如果你的搜索很慢,应该如何排查Elasticsearch的性能问题。

你不能了解:

1.如何安装、配置、部署Elasticsearch。

2.如何使用Elasticsearch。

Elastichsearch的数据存储特性

ES是一个分布式的搜索引擎。但是,我认为ES首先是一个数据库,它具有持久化数据的能力。ES在数据存储上有2个重要特点:分片和副本。分片是指将一份完整的数据集(索引)分成若干片段的数据;副本是分片数据的备份。分片使es保存的数据具有很强的横向扩展能力,可以支持海量数据的高性能存储和查询。副本使数据具有高可用和容灾能力,即使部分ES数据节点挂掉仍然可以正常运行。

补充一些注意点吧:

1.每块分片都是一个独立完整的搜索引擎,只是数据是完整数据集的子集。

2.你可以设置副本的份数,也可以设置为0,即没有副本。副本数越高可用性越高(可以让更多的数据节点挂掉),并且副本分片可以服务于读请求,增加副本数还可以提升查询性能,代价是降低了写入性能且需要更多的存储空间。

3.如果持有主分片的节点挂掉了,一个副本分片就会晋升为主分片的角色。在索引写入时,副本分片做着与主分片相同的工作。新文档首先被索引进主分片然后再同步到其它所有的副本分片。

4.同一个分片和它对应的副本应该分散在不同的节点上,否则起不到提升可用性的作用。你看下图,有一个分片数为3,副本数为1的索引分布在2个数据节点上,无论哪个节点挂掉,剩下的节点上总能保持完整的数据信息。当然,ES会根据你设置的分片和副本数,以及集群中的数据节点数自动分配,作为使用者无须额外操作,但你应该知道这个原理。

同一分片及其对应的副本要尽可能散落在不同的节点上

你可能已经想到一种特殊情况了:单机部署。很明显,副本对单机部署模式毫无意义,所以如果你需要单机部署的话,请把索引的副本数设置为0,以节省空间和提升写入性能。

ES不适合做什么

以上,你会感觉ES既能满足高性能读写,又能保证高可用,再加上ES最擅长的各种花式查询搜索,作为数据库好像还不错。那么,ES到底是否适合作为产品的数据库选型呢?我个人认为,大多数业务场景下是不适合的。原因如下:

1.ES不支持事务。没有传统关系型数据库的事务和锁,难以应付数据一致性要求高的场景,比如订票系统,商品秒杀系统,银行交易系统。

2.ES的查询是近实时的,而非实时的。这意味着,当你写入一条数据到ES中,需要等待一段时间(默认是1秒)才能被查询到。换句话说,如果你刚提交了数据,ES也告诉你提交成功了,但在提交后的1秒内ES突然停机,那你提交的数据仍然是有可能丢失的(可能你会觉得这很严重,如果是单机模式确实如此,但别忘了ES是支持集群部署的,集群专治宕机停电)。这是因为ES为了提升搜索性能做出的牺牲。持久化一条数据需要对磁盘进行io操作以真正的避免数据丢失,但是磁盘io操作比较耗时,所以它不能在每索引一条数据后就执行一次磁盘写操作。为了避免数据提交对磁盘的高频写操作,从数据提交到持久化之间,还有一层称为FileSystem Cache的系统缓存。ES会定期对FileSystem Cache中的数据刷新到磁盘,以达到批量写入的效果,从而减少对磁盘的io操作。这个刷新周期可以手动设置,最快可以设置为1毫秒,但从本质上讲仍然属于定期刷新。前面我提到,通过集群部署可以避免突然停机带来的数据丢失问题,那你可能会有疑问,没有分布式特性的mysql是如何避免数据更新丢失的?由于本篇主要围绕ES,不再继续展开讨论,感兴趣的同学可以自行搜索:"mysql两阶段提交","mysql redo log","mysql binlog"等内容。

3.数据更新效率低。ES本质上没有更新操作,所谓的ES更新实际上是删除+新增两步操作,高频率的数据增删很容易触发段(segment)文件合并。段合并是对文件的重新组装,相对内存操作而言更耗时。因此如果你需要高频的更新数据,比如你需要设计一个计数器,也许redis会更适合。

4.安全性不足。开源版本的ES本身不带权限认证模块,商业版的xpark插件支持用户身份认证。但不管怎样,和mysql细致且健全的权限认证体系相比,ES的数据让人总感觉是在裸奔。顺便介绍一点提升ES安全性的办法:首先是配置ES节点为内网ip,避免外部请求直接访问;其次要设置防火墙,限制指定ip可以访问es节点;如果需要通过kibana等工具访问数据,可以通过反向代理设置访问密码,并结合vpn使用。

通过以上分析,你可以判断出自己的系统是否适合用ES作为数据库选型。ES并非不能当作数据库来使用,只要你能充分发挥它的优点,避免它的不足。但不管怎样,以上问题都是ES不擅长的领域。

Elasticsearch适合做什么

ES就像它的名字,最适合它的工作当然是搜索,这得益于ES的全文检索能力。关于搜索,我补充一点,ES除了可以实现文档关键字匹配度搜索以外,也支持按一定业务逻辑干涉的"智能搜索"。例如你在电商网站搜索"苹果"的时候,结果是优先展示手机还是水果;或者你在外卖APP上搜索"烧烤",首页展示的门店总是在配送范围之内而且评论口碑都不错。这些场景都可以用ES通过全文检索结合推荐算法来实现。除了全文检索以外,对于各种条件查询,数据计算、聚合等查询类的任务,ES同样是非常适合的。例如,假设你要实现一个千万级用户的附近的人功能,需要在千万用户中找出离你最近的200个人,并加上一些社交属性的可选条件,比如年龄范围,性别,最近上线时间等,甚至排序也是一个复杂的计算规则。如果你通过mysql这样的传统数据库来查询,那么在多表连接,条件过滤,复杂排序这几方面都存在巨大的挑战。然而,ES却非常擅长做这样的事。我给你一个建议就是,当你觉得一个查询接口用mysql实现效率太低了的时候,除了死磕SQL优化以外,还可以了解一下ES。

总结一下,ES适合做全文检索、数据统计与聚合、海量数据的复杂查询。当下一种流行的用法是,传统数据库+ES配合,核心业务数据流转在传统数据库,ES的数据来源于传统数据库的数据同步。传统数据库负责主要业务的CRUD,ES负责对数据精确度不高的复杂条件查询及搜索。

Elasticsearch与MySQL查询原理分析

MySQL的索引原理

在介绍elasticsearch的索引原理之前,我们简单了解一下mysql的索引知识。

假如我们有如下数据:

原始数据

在mysql的InnoDB引擎中,主键默认建立聚集索引(索引里包含row的全部信息),数据结构为B+树。

mysql InnoDB聚簇索引数据结构

B+树的具体定义本文不介绍,感兴趣的同学点此了解,但有几个特点我提一下:

1.B+树是一种多路查找树,其每一个节点的孩子数可以多于两个,且每一个节点处可以存储多个元素。

2.每个节点到叶子节点的高度都是相同的,这样可以保证B树的查询是稳定的。

3.每一个节点存储的元素是经过排序的,节点的子树满足:左子树元素≤父节点元素;右子树元素>父节点元素

4.非叶子节点只保存key,叶子节点保存key和data。

为了方便对年龄进行查询,我们需要对年龄字段单独建立索引,数据结构仍然为B+树:

对年龄建立索引

开发者自己建立的索引叫做二级索引,二级索引里存储的内容是主键。这就意味着,如果你想查询满足age=18这个条件的完整信息,数据库查询会经历两个过程:

1.InnoDB首先搜索age key索引,找出age=18的id;

2.搜索主键索引,找出对应id的完整信息。

通过查询条件找到主键,再通过主键查询完整信息的过程,叫做回表。很明显,回表增加了查询次数,降低了查询效率,你可以自己了解一下如何避免回表。

我再留一个问题给你思考一下:平衡二叉树是二分查找效率最高的数据结构,为什么MySQL不使用平衡二叉树,而要使用B+树这种多叉树来存储索引呢?

倒排索引

倒排索引是elasticseach实现快速搜索的核心。在elasticsearch中,记录(es中称为"文档")的每个字段都会建立索引,无需手动配置。上一节中的原始数据,如果用elasticsearch保存,索引结构如下:

倒排索引

17,18,20这些叫做term,而[100,300,500]叫做posting list。Posting list就是一个int的数组,存储了所有符合某个term的文档id。

Term Dictionary

为了能快速找到某个term,将所有的term排个序,二分法查找term,logN的查找效率,就像通过字典查找一样,这就是Term Dictionary。现在再看起来,似乎和传统数据库的方式类似啊,为什么说查询更快呢?

Term Index

假设我们有很多个term,比如:

Carla,Sara,Elin,Ada,Patty,Kate,Selena

如果按照这样的顺序排列,找出某个特定的term一定很慢,因为term没有排序,需要全部过滤一遍才能找出特定的term。排序之后就变成了:

Ada,Carla,Elin,Kate,Patty,Sara,Selena

这样我们可以用二分查找的方式,比全遍历更快地找出目标的term。这个就是 term dictionary。有了term dictionary之后,可以用 logN 次磁盘查找得到目标。但是磁盘的随机读操作仍然是非常昂贵的(一次random access大概需要10ms的时间)。所以尽量少的读磁盘,有必要把一些数据缓存到内存里。但是整个term dictionary本身又太大了,无法完整地放到内存里。于是就有了term index。term index有点像一本字典的大的章节表。比如:

A开头的term ……………. Xxx页

C开头的term ……………. Xxx页

E开头的term ……………. Xxx页

如果所有的term都是英文字符的话,可能这个term index就真的是26个英文字符表构成的了。但是实际的情况是,term未必都是英文字符,term可以是任意的byte数组。而且26个英文字符也未必是每一个字符都有均等的term,比如x字符开头的term可能一个都没有,而s开头的term又特别多。实际的term index是一棵树:

term index

这棵树不会包含所有的term,它包含的是term的一些前缀。通过term index可以快速地定位到term dictionary的某个offset,然后从这个位置再往后顺序查找。

所以term index不需要存下所有的term,而仅仅是他们的一些前缀与Term Dictionary的block之间的映射关系,再结合FST(Finite State Transducers)的压缩技术,可以使term index缓存到内存中。

现在我们可以回答为什么Elasticsearch检索可以比MySQL快了。Mysql只有term dictionary这一层,是以B+树的方式存储在磁盘上的。检索一个term需要若干次的random access的磁盘操作。而Elasticsearch在term dictionary的基础上添加了term index来加速检索,term index以树的形式缓存在内存中。从term index查到对应的term dictionary的block位置之后,再去磁盘上找term,大大减少了磁盘的random access次数。

额外值得一提的两点是:term index在内存中是以FST(finite state transducers)的形式保存的,其特点是非常节省内存。Term dictionary在磁盘上是以分block的方式保存的,一个block内部利用公共前缀压缩,比如都是Ab开头的单词就可以把Ab省去。这样term dictionary可以更节约磁盘空间。

总结

Elasticsearch就是尽量将磁盘里的东西搬进内存,减少磁盘随机读取次数(同时也利用磁盘顺序读特性),结合各种压缩算法,高效使用内存,从而达到快速搜索的特性。

使用Elasticsearch的基本架构设计

Elasticsearch一般不作为主数据源,因此在系统中应该包含一个主库和Elasticsearch。主库负责主要业务的数据流转,Elasticsearch负责对数据精确度不高的复杂条件查询及搜索。下图是一个普通的系统架构模型。


使用ES的常见系统架构

如图上蓝色线所示,表示同步写过程,同步写结束代表主要业务完成。

然后,如图中绿色线所示,有一个异步写服务,监听 MQ 的消息,继续完成辅助数据的更新操作。注意点:

MQ 消息不一定包含完整的数据,甚至可能只包含一个最新数据的主键 ID,我们需要根据 ID 从查询服务查询到完整的数据。

ES 不适合在各索引之间做连接(Join)操作,适合保存扁平化的数据。比如,我们可以把订单下的用户、商户、商品列表等信息,作为内嵌对象嵌入整个订单 JSON,然后把整个扁平化的 JSON 直接存入 ES。

对于查询服务,如图中红色线所示,我们需要根据一定的上下文条件(比如查询一致性要求、时效性要求、搜索的条件、需要返回的数据字段、搜索时间区间等)来把请求路由到合适的数据库,并且做一些聚合处理:需要根据主键查询单条数据,可以从 MySQL Sharding 集群或 Redis 查询,如果对实时性要求不高也可以从 ES 查询。按照多个条件搜索订单的场景,可以从 MySQL 索引表查询出主键列表,然后再根据主键从 MySQL Sharding 集群或 Redis 获取数据详情。各种后台系统需要使用比较复杂的搜索条件,甚至全文搜索来查询订单数据,或是定时分析任务需要一次查询大量数据,这些场景对数据实时性要求都不高,可以到 ES 进行搜索。此外,MySQL 中的数据可以归档,我们可以在 ES 中保留更久的数据,而且查询历史数据一般并发不会很大,可以统一路由到 ES 查询。

Elasticsearch与MySQL的数据同步方案

Elasticsearch不适合作为主数据源,而更适合作为数据库的"备份",因此如果你已经确定要使用Elasticseach,你要面对的第一个问题是,如何维护Elasticsearch中的数据。总的来讲,Elasticsearch的数据维护包括:数据全量同步,数据增量同步,数据正确性校验。

数据全量同步

一般来讲,数据全量同步只触发于初始化阶段,目的是将数据库中的历史数据同步到Elasticsearch中。对于全量同步,最简单的做法是写一个数据迁移脚本,如果嫌麻烦,也可以使用datax这类数据同步工具,总体来讲,实现难度都不大。

数据增量同步

数据增量同步是在程序长期运行中,业务数据更新后自动同步到elasticsearch,我总结了4种Elasticsearch与MySQL的数据增量同步策略:

1.代码级同步:利用代码的顺序执行,在MySQL的增、删、改操作执行后,立即操作Elasticsearch进行增、删、改操作。这种方案的优点是实现简单,缺点也很明显:1.容易写漏而引发bug。2.每处操作都要写两遍数据操作的代码,增加了代码复杂度,后期的可维护性不高。3.如果是同步写,不仅会增加代码的执行时间,还会增加因同步Elasticsearch失败而导致的主流程的失败率。总的来讲,这种方案过于简单粗暴,一般场景不推荐使用。

2.利用消息队列解耦:你需要一个专门用于操作Elasticsearch数据的服务。当代码执行MySQL的增、删、改操作执行后,通过消息队列将改变的数据发送到Elasticsearch数据服务,由该服务对Elasticsearch进行增、删、改操作。这种方案的优点是业务流程代码只需要发送消息队列,无需和Elasticsearch产生直接关系,Elasticsearch的同步操作也不会影响主流程。缺点是你需要单独维护一个Elasticsearch数据服务。

3.利用logstash-jdbc-input同步:logstash-jdbc-input同步的原理是,logstash-jdbc-input会定时(最小频率是每分钟)向MySQL发出指定的查询语句,并将查询结果按照你的配置写入到elasticsearch。此方案和方案2相比,实现了彻底的业务解耦(方案2还是需要写发消息的逻辑),无需另写一个Elasticsearch数据服务;不足之处是只能定时同步,无法做到近实时同步,适用于对查询即时性要求不高的场景。

4.利用canal同步:canal的同步原理是,1.canal模拟mysql slave的交互协议,伪装自己为mysql slave,向mysql master发送dump协议;2.mysql master收到dump请求,开始推送binary log给slave(也就是canal);3.canal解析binary log对象,还原数据并写入elasticsearch。此方案和方案3相比,二者都做到了很好的解耦,canal同步是近实时的,即时性比logstash-jdbc-input强,但canal只针对mysql同步,logstash-jdbc-input适用于泛SQL系列。


canal同步数据原理

总结一下,如果你的数据源是MySQL,那么方案4是我认为最佳的选择;如果你的数据源是Oracle,SQL Server建议用方案2或方案3。

数据正确性校验

正确性校验的目的主要是对增量同步失败后进行补救,可以提升数据的准确性,一般来说校验是用定时器触发的。常见的校验方案有:

1.逐条校验。逐条校验就是将MySQL中查询的数据和elasticsearch中的数据一一对比,如果发现elasticsearch中的数据和数据库不一样,就触发这条数据的同步。逐条校验的好处是实现简单,缺点是数据量大的情况下校验效率低。

2.分页批量校验。将MySQL中查询的数据和elasticsearch中的数据按分页大小批量比较。和逐条校验的区别是,MySQL查询和和elasticsearch查询都进行了批量读,加快了校验效率。

3.计算摘要。.将MySQL和elasticsearch中相同条件查询的数据进行md5或hash加密,如果两者计算出的摘要信息一致则表示数据正确;如果摘要不一致,就需要进行数据更新。此方案的优点是利用摘要算法避免了大量的比对操作,是三种方案里效率最高的。

Elasticsearch搜索性能问题排查思路

1.用kibana操作elasticsearch。kibana就是开发人员观察elasticsearch的眼睛,kibana的dev tool是操作elasticsearch最方便的工具没有之一,你应该用kibana来操作和调试elasticsearch,如同你用navicat而非命令行来操作MySQL一样。需要小心的是,kibana和elasticsearch都是不需要密码就能使用的,所以为了数据安全,你应该仅开放指定ip来访问kibana,再配合vpn和反向代理的密码访问。

kibana的dev tool

2.开启elasticsearch的慢日志。elasticsearch的慢日志包括了慢索引日志和慢搜索日志。你可以自定义慢索引和慢搜索的阀值,当写索引或搜索的时间超过设置阀值后elasticsearch就会将本次索引或搜索的语句记录到对应的日志里,以便后续分析问题。

3.利用性能监控工具查看elasticsearch的实时运行情况。Elasticsearch本身提供了一组性能监控的接口,理论上通过接口就可以获取elasticsearch的运行状态,如心跳检测,集群健康情况等。但是最方便的办法是使用性能监控工具,其实很多ES的性能监控工具本质上也是调用的elasticsearch接口来获取状态。我推荐一个免费的工具:metricbeat。它可以和kibana集成,实时展示elasticsearch的各项性能指标和资源占用情况。

从图中可以看出iowait占比非常高,优化办法是提升磁盘性能

4.使用profileApi。通过这个功能,可以看到一个搜索聚合请求,是如何拆分成底层的 Lucene 请求,并且显示每部分的耗时情况,类似于MySQL的explain。profileApi结合慢搜索日志,可以很快定位到搜索慢的最终原因。

使用profileApi可以显示查询语句每个部分的执行时间

总结来说,elasticsearch排查问题,要依靠日志分析,profileApi分析,并结合有效的性能监控工具。

总结

本文介绍了ES的存储特性,包括分片和副本。

ES虽然不适合作为主数据库,但非常适合海量数据的复杂条件查询。

MySQL的索引原理是利用B+树作为检索的数据结构,擅长查询最左前缀匹配的条件和范围查询;ES的索引原理是倒排索引,通过Term Index和压缩算法将索引的前缀放入内存从而减少磁盘查询。

使用ES作为数据存储系统,要重点考虑数据与主库同步和数据正确性校验的方案。比较推荐的同步方案是利用消息队列同步,或者使用logstash-jdbc-input或者使用canal;比较推荐的数据正确性校验方案是hash校验。

出现ES查询性能低时,首先要开启慢搜索和慢索引日志,结合性能监控工具查看各项性能指标并配合kibana及profile API辅助调试。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,456评论 5 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,370评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,337评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,583评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,596评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,572评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,936评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,595评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,850评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,601评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,685评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,371评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,951评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,934评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,167评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,636评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,411评论 2 342