相关推荐
Elasticsearch简介及与MySQL查询原理对比(ELK架构入门必读)
2024-11-03 00:02

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

Elasticsearch简介及与MySQL查询原理对比(ELK架构入门必读)

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

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

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

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

你不能了解

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

2.如何使用Elasticsearch。

1.数据库(全系统表存储

2. 工具:分布式全文搜索 (对数据集合进行全文索引【倒排索引】)

ES是一个分布式的搜索引擎。但是,我认为ES首先是一个数据库,它具有持久化数据的能力。

ES在数据存储上有2个重要特点:分片和副本。

分片是指将一份完整的数据集(索引)分成若干片段的数据;副本是分片数据的备份。分片使es保存的数据具有很强的横向扩展能力,可以支持海量数据的高性能存储和查询。

副本使数据具有高可用和容灾能力,即使部分ES数据节点挂掉仍然可以正常运行。

补充一些注意点吧

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

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

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

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

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

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

以上,你会感觉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会更适合。

(通过队列进行文档document数据内容更新

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

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

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

(高级查询、全字段排序

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

在介绍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缓存到内存中。

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负责对数据精确度不高的复杂条件查询及搜索。下图是一个普通的系统架构模型。

使用ES的常见系统架构

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

绿色线所示,表示异步写服务,监听 MQ 的消息,继续完成辅助数据的更新操作。注意点:MQ 消息不一定包含完整的数据,甚至可能只包含一个最新数据的主键 ID,我们需要根据 ID 从查询服务查询到完整的数据。

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

红色线所示,对于查询服务,我们需要根据一定的上下文条件(比如查询一致性要求、时效性要求、搜索的条件、需要返回的数据字段、搜索时间区间等)来把请求路由到合适的数据库,并且做一些聚合处理:需要根据主键查询单条数据,可以从 MySQL Sharding 集群或 Redis 查询,如果对实时性要求不高也可以从 ES 查询。按照多个条件搜索订单的场景,可以从 MySQL 索引表查询出主键列表,然后再根据主键从 MySQL Sharding 集群或 Redis 获取数据详情。

各种后台系统需要使用比较复杂的搜索条件,甚至全文搜索来查询订单数据,或是定时分析任务需要一次查询大量数据,这些场景对数据实时性要求都不高,可以到 ES 进行搜索。此外,MySQL 中的数据可以归档,我们可以在 ES 中保留更久的数据,而且查询历史数据一般并发不会很大,可以统一路由到 ES 查询。

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加密,如果两者计算出的摘要信息一致则表示数据正确;如果摘要不一致,就需要进行数据更新。此方案的优点是利用摘要算法避免了大量的比对操作,是三种方案里效率最高的。

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和压缩算法将索引的前缀放入内存从而减少磁盘查询。

    以上就是本篇文章【Elasticsearch简介及与MySQL查询原理对比(ELK架构入门必读)】的全部内容了,欢迎阅览 ! 文章地址:http://mip.xhstdz.com/quote/244.html 
     栏目首页      相关文章      动态      同类文章      热门文章      网站地图      返回首页 物流园资讯移动站 http://mip.xhstdz.com/mobile/ , 查看更多   
发表评论
0评