Elasticsearch科普与应用

面试七股多一股 2024-02-21 22:11:38
前言

本次主要目的是科普下Elasticsearch(以下简称:ES),同时分享下目前自己使用到的ES的情况。

章一:什么是ES简介:

Elasticsearch 是一个分布式、高扩展、高实时的搜索与数据分析引擎。它能很方便的使大量数据具有搜索、分析和探索的能力。充分利用Elasticsearch的水平伸缩性,能使数据在生产环境变得更有价值。Elasticsearch 的实现原理主要分为以下几个步骤,首先用户将数据提交到Elasticsearch 数据库中,再通过分词控制器去将对应的语句分词,将其权重和分词结果一并存入数据,当用户搜索数据时候,再根据权重将结果排名,打分,再将返回结果呈现给用户。

Elasticsearch是与名为Logstash的数据收集和日志解析引擎以及名为Kibana的分析和可视化平台一起开发的。这三个产品被设计成一个集成解决方案,称为“Elastic Stack”(以前称为“ELK stack”)。

Elasticsearch可以用于搜索各种文档。它提供可扩展的搜索,具有接近实时的搜索,并支持多租户。Elasticsearch是分布式的,这意味着索引可以被分成分片,每个分片可以有0个或多个副本。每个节点托管一个或多个分片,并充当协调器将操作委托给正确的分片。再平衡和路由是自动完成的。相关数据通常存储在同一个索引中,该索引由一个或多个主分片和零个或多个复制分片组成。一旦创建了索引,就不能更改主分片的数量。

Elasticsearch使用Lucene,并试图通过JSON和Java API提供其所有特性。它支持facetting和percolating,如果新文档与注册查询匹配,这对于通知非常有用。另一个特性称为“网关”,处理索引的长期持久性;例如,在服务器崩溃的情况下,可以从网关恢复索引。Elasticsearch支持实时GET请求,适合作为NoSQL数据存储,但缺少分布式事务。

ES整体架构:

结构:为方便理解,可以对照下ES和MySQL的关系:

ES数据架构的主要概念与关系数据库Mysql对比表

Type的概念,是适用于早期的ES版本,后续的ES版本,未使用到此概念。

章二:ES中的核心概念

集群

一个集群,集群中有多个节点,其中有一个为主节点,这个主节点是可以通过选举产生的,主从节点是对于集群内部来说的。es的一个概念就是去中心化,字面上理解就是无中心节点,这是对于集群外部来说的,因为从外部来看es集群,在逻辑上是个整体,你与任何一个节点的通信和与整个es集群通信是等价的。

节点

Elasticsearch集群中服务分多个角色

Master节点:

负责集群内调度决策,集群状态、节点信息、索引映射、分片信息、路由信息,Master真正主节点是通过选举诞生的,一般一个集群内至少要有三个Master可竞选成员,防止主节点损坏。

Data存储节点:

用于存储数据及计算,分片的主从副本,热数据节点,冷数据节点;

Client协调节点:

协调多个副本数据查询服务,聚合各个副本的返回结果,返回给客户端;

Elasticsearch的两次查询:

多节点及多分片能够提高系统的写性能,但是这会让数据分散在多个Data节点当中,Elasticsearch并不知道我们要找的文档,到底保存在哪个分片的哪个segment文件中。所以,为了均衡各个数据节点的性能压力,Elasticsearch每次查询都是请求 所有 索引所在的Data节点,查询请求时协调节点会在相同数据分片多个副本中,随机选出一个节点发送查询请求,从而实现负载均衡。而收到请求的副本会根据关键词权重对结果先进行一次排序,当协调节点拿到所有副本返回的文档ID列表后,会再次对结果汇总排序,最后才会用 DocId去各个副本Fetch具体的文档数据将结果返回。可以说,Elasticsearch通过这个方式实现了所有分片的大数据集的全文检索,但这种方式也同时加大了Elasticsearch对数据查询请求的耗时。Kibana计算节点:

作用是实时统计分析、聚合分析统计数据、图形聚合展示。

倒排索引

倒排索引是区别于正排索引的概念:

正排索引:是以文档对象的唯一 ID 作为索引,以文档内容作为记录。

倒排索引:Inverted index,指的是将文档内容中的单词作为索引,将包含该词的文档 ID 作为记录。

举例:

倒排索引-组成单词词典(Term Dictionary)

单词词典的实现一般用B+树,B+树构造的可视化过程网址:B+ Tree Visualization

倒排列表(Posting List)

倒排列表记录了单词对应的文档集合,有倒排索引项(Posting)组成

倒排索引项主要包含如下信息:文档id用于获取原始信息单词频率(TF,Term Frequency),记录该单词在该文档中出现的次数,用于后续相关性算分位置(Posting),记录单词在文档中的分词位置(多个),用于做词语搜索(Phrase Query)偏移(Offset),记录单词在文档的开始和结束位置,用于高亮显示

倒排索引不可变的好处不需要锁,提升并发能力,避免锁的问题数据不变,一直保存在os cache中,只要cache内存足够filter cache一直驻留在内存,因为数据不变可以压缩,节省cpu和io开销

分片

代表索引分片,es可以把一个完整的索引分成多个分片,这样的好处是可以把一个大的索引拆分成多个,分布到不同的节点上。构成分布式搜索。分片的数量只能在索引创建前指定,并且索引创建后不能更改。

副本

代表索引副本,es可以设置多个索引的副本,副本的作用一是提高系统的容错性,当某个节点某个分片损坏或丢失时可以从副本中恢复。二是提高es的查询效率,es会自动对搜索请求进行负载均衡。

分词

分词是将文本转换成一系列单词(Term or Token)的过程,也可以叫文本分析,在ES里面称为Analysis

分词器

分词器是ES中专门处理分词的组件,英文为Analyzer,它的组成如下:

Character Filters:针对原始文本进行处理,比如去除html标签Tokenizer:将原始文本按照一定规则切分为单词Token Filters:针对Tokenizer处理的单词进行再加工,比如转小写、删除或增新等处理分词器调用顺序

预定义的分词器Standard Analyzer

默认分词器

按词切分,支持多语言

小写处理

Simple Analyzer

按照非字母切分

小写处理

Whitespace Analyzer

空白字符作为分隔符

Stop Analyzer

相比Simple Analyzer多了去除请用词处理

停用词指语气助词等修饰性词语,如the, an, 的, 这等

Keyword Analyzer

不分词,直接将输入作为一个单词输出

Pattern Analyzer

通过正则表达式自定义分隔符

默认是\W+,即非字词的符号作为分隔符

Language Analyzer 提供了30+种常见语言的分词器自定义分词Character Filters

在Tokenizer之前对原始文本进行处理,比如增加、删除或替换字符等

自带的如下:

1.HTML Strip Character Filter:去除HTML标签和转换HTML实体

2.Mapping Character Filter:进行字符替换操作

3.Pattern Replace Character Filter:进行正则匹配替换

会影响后续tokenizer解析的position和offset信息

Tokenizers

将原始文本按照一定规则切分为单词(term or token)

自带的如下:

1.standard 按照单词进行分割

2.letter 按照非字符类进行分割

3.whitespace 按照空格进行分割

4.UAX URL Email 按照standard进行分割,但不会分割邮箱和URL

5.Ngram 和 Edge NGram 连词分割

6.Path Hierarchy 按照文件路径进行分割

Token Filters对于tokenizer输出的单词(term)进行增加、删除、修改等操作自带的如下:1.lowercase 将所有term转为小写2.stop 删除停用词3.Ngram 和 Edge NGram 连词分割4.Synonym 添加近义词的term

自定义分词

自定义分词需要在索引配置中设定 char_filter、tokenizer、filter、analyzer等

分词使用说明

分词会在如下两个时机使用:

创建或更新文档时(Index Time),会对相应的文档进行分词处理查询时(Search Time),会对查询语句进行分词查询时通过analyzer指定分词器通过index mapping设置search_analyzer实现一般不需要特别指定查询时分词器,直接使用索引分词器即可,否则会出现无法匹配的情况分词使用建议明确字段是否需要分词,不需要分词的字段就将type设置为keyword,可以节省空间和提高写性能善用_analyze API,查看文档的分词结果

recovery

代表数据恢复或叫数据重新分布,es在有节点加入或退出时会根据机器的负载对索引分片进行重新分配,挂掉的节点重新启动时也会进行数据恢复。

章三:ES的常规操作(具体的查询问题以及优化下一期展开)

官方文档:

www.elastic.co/guide/cn/el…(基于 Elasticsearch 2.x 版本,有些内容可能已经过时。)

索引写入流程图:

客户端选择一个node发送请求过去,这个node就是coordinating node (协调节点)coordinating node,对document进行路由,将请求转发给对应的node实际上的node上的primary shard处理请求,然后将数据同步到replica nodecoordinating node,如果发现primary node和所有的replica node都搞定之后,就会返回请求到客户端ES 使用了一个内存缓冲区 Buffer,先把要写入的数据放进 buffer;同时将数据写入 translog 日志文件(其实是些os cache)。refresh:buffer数据满/1s定时器到期会将buffer写入操作系统segment file中,进入cache立马就能搜索到,所以说es是近实时(NRT,near real-time)的flush:tanslog超过指定大小/30min定时器到期会触发commit操作将对应的cache刷到磁盘file,commit point写入磁盘,commit point里面包含对应的所有的segment filetranslog 默认5s把cache fsync到磁盘,所以es宕机会有最大5s窗口的丢失数据索引查询流程图:

读取数据客户端发送任何一个请求到任意一个node,成为coordinate nodecoordinate node 对document进行路由,将请求rr轮训转发到对应的node,在primary shard 以及所有的replica中随机选择一个,让读请求负载均衡,接受请求的node,返回document给coordinate notecoordinate node返回给客户端搜索过程客户端发送一个请求给coordinate node协调节点将搜索的请求转发给所有的shard对应的primary shard 或replica shardquery phase:每一个shard 将自己搜索的结果(其实也就是一些唯一标识),返回给协调节点,有协调节点进行数据的合并,排序,分页等操作,产出最后的结果fetch phase ,接着由协调节点,根据唯一标识去各个节点进行拉去数据,最总返回给客户端创建索引PUT indexName1{ "mappings" : { "dynamic" : "strict", "properties" : { "advertiser_name" : { "type" : "object", "enabled" : false }, "body" : { "type" : "text" }, "cdn_url" : { "type" : "object", "enabled" : false }, "created_at" : { "type" : "long" }, "days" : { "type" : "short" }, "domain" : { "type" : "text", "fields" : { "keyword" : { "type" : "keyword" } }, }, "heat_degree" : { "type" : "short" }, "html_url" : { "type" : "object", "enabled" : false }, "interaction" : { "type" : "long" }, "message" : { "type" : "text", }, "page_logo" : { "type" : "object", "enabled" : false }, "sub_domain" : { "type" : "text", "fields" : { "keyword" : { "type" : "keyword" } }, } } }, "settings" : { "index" : { "refresh_interval" : "300s", "indexing" : { "slowlog" : { "level" : "info", "threshold" : { "index" : { "warn" : "200ms", "trace" : "20ms", "debug" : "50ms", "info" : "100ms" } }, "source" : "1000" } }, "unassigned" : { "node_left" : { "delayed_timeout" : "5m" } }, "analysis" : { "analyzer" : { "zf_analyzer" : { "filter" : [ "lowercase", "stop", "snowball" ], "char_filter" : [ "my_mapping" ], "type" : "custom", "tokenizer" : "standard" } }, "char_filter" : { "my_mapping" : { "type" : "mapping", "mappings" : [ ".=> @", ":=> @" ] } } }, "priority" : "100", "number_of_replicas" : "1", "routing" : { "allocation" : { "require" : { "box_type" : "hot" } } }, "search" : { "slowlog" : { "level" : "info", "threshold" : { "fetch" : { "warn" : "200ms", "trace" : "50ms", "debug" : "80ms", "info" : "100ms" }, "query" : { "warn" : "500ms", "trace" : "50ms", "debug" : "100ms", "info" : "200ms" } } } }, "number_of_shards" : "12" } }}

打开/关闭索引POST indexName1/_closePOST indexName1,indexName2/_closePOST indexName1/_openPOST indexName1,indexName2/_open设置索引刷新时间PUT indexName1/_settings{ "refresh_interval": "90s"}查询个数GET indexName1/_count{ "query": { "bool": { "filter": { "range": { "sales": { "gte": 1000 } } } } }}查询所有//查询所有GET /indexName1/_doc/_search{ "query":{ "match_all":{} }}查询不区分大小写//新建索引,字段不区分大小写PUT indexName1{ "mappings": { "dynamic": "strict", "properties": { "store_name": { "type": "keyword", "normalizer": "my_normalizer" }, "store_id": { "type": "keyword" } } }, "settings": { "index": { "search": { "slowlog": { "level": "info", "threshold": { "fetch": { "warn": "200ms", "trace": "50ms", "debug": "80ms", "info": "100ms" }, "query": { "warn": "500ms", "trace": "50ms", "debug": "100ms", "info": "200ms" } } } }, "refresh_interval": "10s", "indexing": { "slowlog": { "level": "info", "threshold": { "index": { "warn": "200ms", "trace": "20ms", "debug": "50ms", "info": "100ms" } }, "source": "1000" } }, "analysis": { "normalizer": { "my_normalizer": { "filter": "lowercase", "type": "custom" } } }, "number_of_shards": "1", "unassigned": { "node_left": { "delayed_timeout": "5m" } }, "number_of_replicas": "0" } }}//插入数据PUT /indexName1/_doc/1{ "store_name":"HighSteel", "store_id":"com.google.highsteel" }PUT /indexName1/_doc/2{ "store_name":"KKKKKKOOOOO", "store_id":"com.google.highsteel" }//查询所有GET /indexName1/_doc/_search{ "query":{ "match_all":{} }}//模糊查询,不区分大小写GET indexName1/_search{ "query":{ "wildcard" : { "store_name": "*KKkkkKOooOO*" } }}章四:ES的优化项设置分片数#创建索引时指定,指定后,不能进行修改"number_of_shards" : "12"副本数#设置副本数PUT /indexName1/_settings{ "number_of_replicas": 1}内存

内存不用超过节点内存的一半,另外一半给Lucene使用。

冷热节点PUT indexName1/_settings{ "index.routing.allocation.require.box_type": "warm"}索引模版,索引生命周期索引生命周期管理策略:help.aliyun.com/document_de…可以配置按大小或者按时间(天)来生成新的索引,索引名称,自动+1。旧索引自动切换为冷索引。新生成的索引为热索引。eg:index-01,index-02查询索引信息

返回的统计信息和 节点统计 的输出很相似:search 、 fetch 、 get 、 index 、 bulk 、 segment counts 等等。

GET indexName1/_statssegment合并#查看集群上索引的删除条目的情况GET _cat/indices/?v&s=docs.deleted:desc#对某个索引,进行段合并POST indexName1/_forcemerge?max_num_segments=1&only_expunge_deletes=true#查看正在进行的forcemerge任务GET _tasks?detailed=true&actions=*forcemerge&human#取消任务POST _tasks/J2FOnHkDSIGJ43AaTDniJQ:5929987436/_cancel慢写入,慢查询慢查询#查询条件search_time_ms:[5000 TO *]慢写入#查询条件index_time_ms:[200 TO *]章五:ES的注意事项索引字段不能修改,只能新增segment段合并,forcemerge,磁盘空间问题考虑。fielddata占用内存空间问题indices.fielddata.memory_size_in_bytes_cat/segments/indexname?v&h=shard,segment,size,size.memory#fielddata具体字段占用GET _cat/fielddata?v&s=size:desc#fielddata具体索引占用GET _cat/indices?v&h=index,fielddata.memory_size&s=fielddata.memory_size:descflush(sync_interval)tanslog超过指定大小/30min定时器到期会触发commit操作将对应的cache刷到磁盘file,commit point写入磁盘,commit point里面包含对应的所有的segment filerefresh(refresh_interval)buffer数据满/1s定时器到期会将buffer写入操作系统segment file中,进入cache立马就能搜索到,所以说es是近实时(NRT,near real-time)的使用建议:对于实时性要求不高且想优化写入的业务场景,建议根据业务实际调大刷新频率。修改索引非readonlyPUT /indexName1/_settings{ "index.blocks.read_only_allow_delete": null}热索引、冷索引(自己定义,未用索引模版+索引生命周期管理)冷热切换索引PUT indexName1*/_settings{ "index.routing.allocation.require.box_type": "warm"}
0 阅读:0