暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

【Elasticsearch】看不懂你来打我的入门篇

考拉和她的朋友们 2022-03-10
564


略长文预警(已经拆两篇了不然更长别打我xdm

文末惯例漫长考拉唠嗑不要错过!


01

Elasticsearch的前世今生


Elasticsearch,你会在技术博客中看到它被如此描述:一个开源的分布式、RESTful 风格的搜索和数据分析引擎,它的底层是开源库 Apache Lucene。


如果你觉得这段话过于超出你的理解范畴,只需要带着一个问题看下去:我是如何通过一个搜索框获取到那么多搜索结果的?


我们先来介绍今天的主角 Elasticsearch 的根基。Lucene 可以说是当下最先进、高性能、全功能的搜索引擎库——但无论是开源还是私有,它也仅仅只是一个库。为了充分发挥其功能,常常需要使用 Java 并将 Lucene 直接集成到应用程序中,Lucene 的使用相对来说十分复杂,需要了解很多搜索领域的专业知识才可能明白它的原理。


为了解决 Lucene 的易用性问题,Elasticsearch 应运而生。


Elasticsearch 并不仅仅是对 Lucene 做了简单的封装,它本身是一个可以独立部署的高可用的分布式系统,可弹性伸缩支持多至上百个节点,且有配套的可视化管理平台(Kibana),极大地降低 Elasticsearch 的运维成本。


除此之外,它还封装了一套统一而又简洁的 REST 接口,对外提供搜索能力,所以依赖的系统可以是任何语言实现的,只需要通过HTTP请求与 Elasticsearch 交互即可。


除此之外,依托于分布式的优势,Elasticsearch 的性能十分优秀,可轻松支持数亿量级的文档搜索。由于 Elasticsearch 的功能强大和使用简单,维基百科、卫报、Stack Overflow、GitHub 等都纷纷采用它来做搜索。现在,Elasticsearch 已成为全文搜索领域的主流软件之一。


Elastic Search 的应用场景非常广泛,由于它是一个搜索引擎,所有依赖于搜索能力的场景都可以用到 Elastic Search ,包括但不限以下所列举的场景:


  1. 日志检索:最常用的场景,事实上,Elasticsearch + Logstash + Kibana 已经快要成为日志检索的标准模版技术栈了;

  2. 指标的度量和观测:Elasticsearch 拥有强大的数据聚合能力,可以针对给定的数据做各种维度的聚合操作(求平均/求和/求最大值等),且响应速度很快,适合做一些系统指标的监控和度量(如CPU的负载变化趋势,最大值/最小值/平均值等);

  3. 地理位置搜索:Elasticsearch 支持地址位置搜索,基于这个强大的能力我们可以实现一些有趣的能力,例如搜索附近的人/店铺/停车场等信息;

  4. 任何需要用到全文搜索的场景:如媒体网站(文章搜索),社区问答(搜索答案或者问题)等等。



02

Elasticsearch基础打开方式



🍊基础概念

Elasticsearch 本质上也是一种 NoSQL 数据库,很多概念其实可以和关系型数据库通用。


  • index:索引,类似于MySQL中的 Database,即一个索引就是一个库。一般具有相同特性的 Document 会存放在一个 index 里,index 需要定义对应的 setiings,如配置索引的刷新时间/分片数量,分析器等配置信息。

  • type:type 定义于 index 之下,类似于数据库中的表。一个索引下可以有多个 type。type 必须确定一个 mapping 信息,即定一个这个 type 有哪些字段,分别是什么类型,索引时如何处理这个字段。

    • 注:type在6.0版本后已经逐渐被废弃了,即一个index对应一张表。

  • document:文档,是 Elasticsearch 中可被索引的最小单位,类似于数据库中的一行记录,以 json 格式序列化保存,每个 document 都会有一个 ID 主键和一个版本号。document是不可更改的,每次更新都需要将之前的记录删除,再保存新的记录,并需要将版本号+1。

  • shard:分片,Elasticsearch 是分布式的,分片主要用来定义索引的数据是如何分布的,每个index索引都需要定义主分片/和副本分片的数量,Elasticsearch 会根据分片的数量来将索引的数据均匀的分发到集群的节点中,充分利用集群的优势。但需要注意主分片的数量是不可改变的,所以我们在使用Elasticsearch 的时候需要做好容量规划,如果后续需要增加分片时,则需要重新索引数据。


🍊倒排索引

现在假设我们要搜索某段文本中是否包含某个关键字,如果是关系型数据库(例如MySQL),则需要用到like模糊匹配查询,即⬇️

    select * from table where conent like'%keyword%'

    我们都知道这样的模糊匹配是无法索引的,这个SQL会导致全表扫描,当数据量很大时,速度会变得 非 常 之 慢。


    而Elasticsearch却能很快搜索出对应的文档,这是因为Elasticsearch采用了一种叫“倒排索引”的数据结构。(小声bb:其实我觉得“倒排索引”这个翻译并不是很准确,叫“反向索引”可能更合适。


    如上述所说,MySQL 的模糊匹配查询非常慢是因为搜索的关键字没有对应的索引,那么,假如在存储的时候,将文本内容中的关键字提取出来作为索引,理论上我们就可以通过关键字快速定位到某段文本内容了。


    事实上Elasticsearch就是这样做的。如果你将某个字段类型设置为 text,Elasticsearch在索引文档时,会对这个字段的值进行分析处理,将这个字段的值拆分成一个一个词条(Term),每个词条都会反向指向到文档。这样就可以通过关键字搜索到对应的文档内容了( *`ω´)


    举个栗子🌰,有以下3个文档:

    文档1: "hello world"

    文档2:"hello, nice to meet you"

    文档3:"you are really nice"


    在索引时,按照Elasticsearch默认的分词器,文档1会被切分为 "hello",  "world" 两个词条,文档2则会被切分为 "hello", "nice", "to", "meet", "you" 五个词条,文档3则会被切分 "you", "are", "really", "nice" 四个词条。由此而构建出来的倒排索引如下表格所示:


    词条

    文档编号

    hello

    ["文档1","文档2"]

    world

    ["文档1"]

    nice

    ["文档2","文档3"]

    to

    ["文档2"]

    meet 

    ["文档2"]

    you

    ["文档2","文档3"]

    are

    ["文档3"]

    really

    ["文档3"]


    这样我们就可以很快地根据关键字搜索到对应的文档了。


    在关键词不是很多的情况下,Elasticsearch 会将倒排索引都存放于内存中,所以搜索的速度很快;但如果关键词很多的话,内存不能放下所有的内容,Elasticsearch 就会前置再生成一层索引(类似于词典一样,如:A开头的关键词在磁盘中的哪个位置),这样就可以减少磁盘的访问次数,提升性能。


    具体逻辑如图:



    🍊排序和打分

    当存在大量文档时,搜索某个关键字可能会匹配到非常多的文档。想象一下在图书馆的查询系统中只输入一个"学"字,必定会“被水淹没,不知所措”。


    如果能将用户希望看到的文档排在前面,那将会极大地提高搜索的体验,但如何才能实现这个效果呢?


    Elasticsearch 默认采用了一种相关性评分的算法,会针对匹配到关键词的文档逐一打分,并根据打分的结果进行排序。评分高位列在前,即代表这个文档和关键字的相关性最高。那么这个默认评分机制是怎样设计的呢?


    默认评分机制与以下几个因素相关:

    1. 词频:词在文档中出现的频率越高➡️权重越高 。简单来说就是如果一个文档中出现了多次搜索的关键词,说明这个文档最有可能是用户想搜索到的。

    2. 逆向文档频率:词在所有文档中出现的频率越高➡️权重越低。常用词如 “and” 或 “the” 对相关度贡献很少,因为它们在多数文档中都会出现。

    3. 字段长度归一值:字段的长度越短➡️字段的权重越高 。如果词出现在类似标题 title 这样的字段,要比它出现在内容 body这样的字段中的相关度更高。


    Elasticsearch 会结合这几个因素计算出每个文档相对这个搜索的关键词的评分,并且按照评分排序返回结果,当然你也可以指定排序的字段,如按照日期倒序返回。

    除此之外,Elasticsearch 也支持自定义评分函数,如果你觉得内置的评分机制不符合业务诉求,可以在搜索的时候指定一个评分的函数(一段脚本),Elasticsearch 会用这段脚本去替换内置的评分逻辑。


    🍊搜索原理

    上面有提到,Elasticsearch是一个分布式的搜索引擎,由多个节点组成,每个索引由多个分片组成(每个分片分布在不同的节点上)。


    所以我们每次搜索的时候,收到请求的节点会作为客户端,向每个分片发起请求进行搜索,然后再将数据汇总进行处理(分页/排序),最终返回给接口调用方:



    如果使用过数据库的分库分表就会知道,当业务数据被打散在多个表时,如果需要同时查询多张表,那么数据的分页和排序都会有问题(排序不准确或者数量不正确),要解决这个问题,就需要进行额外的操作,在性能上就会有一定的损失。


    Elasticsearch 的分片就类似分库分表,所以它也会遇到同样的问题。Elasticsearch 提供了4种搜索方式来供用户选择,是牺牲数据的准确性来提高性能,还是降低性能保证数据的准确呢,就是属于你自己的博弈了(笑):


    🟠query and fetch

    向索引的所有分片都发出查询请求, 各分片返回的时候把元素文档 ( document)和计算后的排名信息一起返回。

    这种搜索方式是最快的。因为相比下面的几种搜索方式,这种查询方法只需要去 shard查询一次。但是各个 shard 返回的结果的数量之和可能是用户要求的size的n倍。

    ✅优点:这种搜索方式是最快的。因为相比后面的几种es的搜索方式,这种查询方法只需要去shard查询一次。

    ❌缺点:返回的数据量不准确,可能返回(N*分片数量)的数据并且数据排名也不准确,通过上面打分部分的描述我们知道,打分需要依赖“逆向文档频率”这个因素,这个因素需要计算关键词在所有文档中出现的频率,但是这种搜索方式只会在当前分片上去计算,所以这个因素其实是不准确的,导致评分也不准确。


    🟠query then fetch(ES默认搜索类型)

    先向所有的 shard 发出请求,各分片只返回文档id(不包括文档内容)和排名相关的信息(也就是文档对应的分值), 然后按照各分片返回的文档的分数进行重新排序和排名,取前 size 个文档,然后再根据文档 id 去相关的 shard 取 document。这种方式返回的 document 数量与用户要求的大小是相等的。

    ✅优点:返回的数据量是准确的。

    ❌缺点:性能一般,并且数据排名不准确(不准确的原因同上)。


    🟠DFS query and fetch 

    逻辑大概和第一种搜索方式类似,只不过这种方式有个DFS的步骤,为了解决排名不准确的问题,也就是在进行查询之前, 先对所有分片发送请求, 把所有分片中的词频和文档频率等打分依据全部汇总到一块, 再执行后面的操作,由于有了全局的文档频率因素,所以排名是准确的

    ✅优点:排名准确。

    ❌缺点:返回的数据量不准确,性能一般。


    🟠DFS query then fetch 

    逻辑大概和第二种搜索方式类似,这种方式也解决了排名不准确的问题,而且返回的数据也是准确的,但是性能是最差的

    ✅优点:排名准确/数据准确。

    ❌缺点:性能很慢。

    我们可以根据自己业务需求来选择对应的搜索方式,一般来讲默认的搜索方式可以满足大部分的诉求。




    至此,基础的 Elasticsearch 背景、基础概念和原理就为大家介绍完啦,下一期会给大家带来常用的API介绍和一些进阶打开方式( *`ω´)



    03

    小考拉唠嗑时间


    最近有认真听取关注的朋友们的意见认真思考排版应该怎么好好优化下!

    (毕竟之前写得最多的可能是Markdown∠( ᐛ 」∠)_

    之前有朋友反馈过说字太多看着太累啦,但说实话到现在想破头我暂时也还没想好技术分享类的文章应该怎么写才能让大家觉得轻松易读,考拉会好好努力探索的!

    今天这个新排版你们还喜欢吗(紧张




    公司和我每天都去的咖啡厅中间有一家猫咖,每次路过我都会留下认真看一会儿猫猫。


    之前遇到的“睥睨众生的🍊猫”


    今天惊喜地发现他家居然还有一只小柴,奶毛都还没褪掉。老板看我喜欢它它似乎也很喜欢我的样子,就跑出来放它和我一起玩儿。

    轻柔绒绒的毛和热乎乎软软的小舌头,两个巴掌大小,标致得像个玩具小狗,光看着它在阳光下跌跌撞撞走过来冲我摇尾巴我的心都要化掉了(´;ω;`)



    我不知道它的名字,姑且就叫它卡门吧(

    春天来了又到了和小动物贴贴的季节,最近杭州的阳光也变得好起来。

    大家也要多出门晒太阳!




    最近可能是赶上财年末述职,回顾了很多自己这一年多来在工作这件事上的变化。还是蛮感慨的,也确认了一下自己还算得上是有长进。

    唯一让我很困惑的是之前在同样的一群人中间当众演讲基本都是实习或入职的转正答辩,按道理说都是比述职压力大多了的场景;考拉读书时代也是相对来说脱稿演讲经历比较丰富的那种,结果一开口心率直接飙上了一百三。


    但考拉果然也还是年轻的老幺,不愧是我的师兄们,师兄们讲得都很好。



    明年不知道还能不能继续做老幺(毕竟HC咱说了不算),但一定也会比今年厉害很多(确信)!


    前段日子一段青春期时总在我耳边晃悠的诗的片段再一次总是莫名其妙出现在我脑海:一月你还没出现,二月你睡在隔壁;三月下起了大雨,四月里遍地蔷薇……

    然后三月第一天的杭州真的下了还挺大的雨,希望四月也可以真的看到遍地蔷薇。

    春天来啦,宜睡长觉,宜出门玩儿,忌坏情绪,忌不甘心。



    喜欢这个船新的排版方式就点个赞和在看再走呗



    文章转载自考拉和她的朋友们,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

    评论