Elasticsearch高频面试题总结
什么是Es
Elasticsearch 是建立在全文搜索引擎库 Lucene 基础上的搜索引擎,它隐藏了 Lucene 的复杂性,取而代之的提供一套简单一致的 RESTful API 不过掩盖不了它底层也是 Lucene 的事实,Elasticsearch 的倒排索引,其实就是 Lucene 的倒排索引。
为啥要使用es?
随着数据量增加,而业务中往往采用模糊查询进行数据的搜索,而模糊查询会导致查询引擎放弃索引, 导致系统查询数据时都是全表扫描,在百万级别的数据库中, 查询效率是非常低下的,而我们使用 ES 做一个全文索引, 将经常查询的系统功能的某些字段比如说 电商系统的商品表中商品名,描述、价格还有 id 这些字段我们放入 ES 索引库里,可以提高查询速度。
Elasticsearch中分片的概念
分片是Elasticsearch为了实现水平扩展和容错而引入的一个重要概念。
在创建索引时可以指定索引的分片数,默认是5个分片。
每个分片可以看成是一个独立的、功能完整的“裸” Lucene索引。
Elasticsearch会自动将索引数据平摊到多个分片上,每个分片可以部署到集群中的不同节点上。
查询和写入操作会自动并行的发生在所有分片上,从而提高性能。
分片存储数据是逻辑上的,不同分片间数据是完全隔离的。
可以随时增加新的节点来承载更多的分片,实现无缝扩展。
如果某个分片的节点不可用,会使用备份机制保证服务正常。
分片数一旦确定,很难修改,因此需要事先考虑数据量和需要的查询性能。
通过分片机制,可以实现Elasticsearch集群水平扩展的能力,提高整体吞吐量和系统的容错能力。是Elasticsearch支持分布式和海量数据的重要基石。
Elasticsearch倒排索引
倒排索引也是索引的一种。索引,本质上就是为了快速检索我们存储的数据。
倒排索引的概念和工作原理:
倒排索引事实上是每个词条对应的文档集合的倒排列表。
当文档被索引时,会将文本内容解析成一系列词条。
倒排索引是将文本作为词条项进行建立的关联型数据结构,每个词条项都带有指向包含该词条的文档列表
建立索引时,Elasticsearch会对每篇文档进行分析:
1.分词,将文本内容切分为各个词条项
3.为每个词条项建立倒排记录,即这个词条项出现在哪些文档中
3.记录词频,记录一个词条在每篇文档中出现的频率
比如插入一份文档,内容是“生存还是死亡”,这个时候通过使用分词器,会将它分解为“生存”、“还是”、“死亡”三个词语,然后可能还会把“还是”这个无意义的词语干掉。
接着,就会将这两个词语以及对应的文档 id 存下来:
word documentId
生存 1
死亡 1
然后我们再插入一个文档,这个内容是“生存”,于是索引就变成了:
word documentId
生存 1,2
死亡 1
下回在搜索“生存”的时候,就会返回1,2两份文档。
对于大量文档,这些倒排记录列表就形成了整个索引库。
查询时,只需使用查询词条在倒排索引中查找相关文档就可以,不用一个个扫描每个文档。
但是只是这样是远远不够的,世界上的语言种类特别多,每搜索一个单词,就都要全局遍历,效率特别低。这时候就需要用到了排序,以便采用二分查找等方式提高遍历效率,在这里 lucene 采用了跳表的数据结构,这就是Term Dictionary,另一方面,光使用排序还会导致磁盘IO速度过慢(因为数据都放在磁盘中),如果将数据放入内存,又会导致内存爆满。 所以,Lucene 的倒排索引,在上面的表格的基础上,在左边增加了一层字典树 term index,它不存储所有的单词,只存储单词前缀,通过字典书找到单词所在的块,也就是单词的大概位置,再在块里二分查找,找到对应的单词,再找到单词对应的文档列表。
term index 使用字典树(trie树)来加速term查找,节省内存,只记录term的前缀。
遍历字典树可以快速找到term所在的block位置。
再在对应的block内进行二分查找定位具体term。
FST(Finite State Transducers)优化:
Lucene会将term index以FST结构存储在内存中,这样比字典树更节省内存。
FST通过有限状态机的实现方法,对term进行更高效的压缩存储。
磁盘上的term dictionary(词汇字典)也采用块存储和公共前缀压缩的方式节省磁盘空间。
es中 term代表什么
term代表的是索引库中的一个独立词条(term) 具体来说:
当文档被索引时,会先使用分词器对文档内容进行切分,生成多个词条。
比如一个句子"Elasticsearch is a search engine"可能会被切分为以下词条:
"elasticsearch"
"is"
"a"
"search"
"engine"
对象每个切分出来的词条,Lucene都会给予一个独立的术语项称为term。
例如上例中就会有5个term:"elasticsearch"、"is"等。
term是构建倒排索引时的基本单位,Lucene会为每个term构建一个包含它出现文档列表的倒排记录项。
用户查询时,查询词也会对应一个term进行匹配。
es中 Term Dictionary
具体来说:
在建立倒排索引时,文档经过分词后会产生很多term(词条)。
Lucene会对所有的term进行去重,形成一个唯一的term集合,这个集合就叫做term dictionary。
Term dictionary记录着所有在索引库中出现过的term及其内部id。
当需要为新的文档建立倒排记录时,会先在term dictionary中查询/添加term,取得其内部id。
倒排记录中的term字段就使用这个内部id来替代具体的term字符串。
term dictionary在磁盘上以块状压缩的方式存储,在内存中使用FST结构优化查询效率。
所以简单总结:
Term dictionary是录指数库中的词汇表,记所有唯一term。
通过它可以高效查询/添加term的内部id。
Term id替代term串被用于构建倒排记录,节省空间成本。
它使倒排索引结构得以高效建立与查询评分。是一个核心概念
ES中FST(Finite State Transducer)结构
结构中文含义是有限状态转换器。它是一种用于对词汇表(term dictionary)进行高效压缩表示的结构。
FST的主要特点和作用是:
FST采用有限状态机表示法,对词汇表中的term进行编码压缩表示。
每个term在FST里用一条匹配路径来表示,通过字母或代码匹配这条路径即可定位term。
相邻term会共享尽可能长的公共前缀,从而大幅减少重复部分占用的内存。
例如terms["cat","cats","catch"]在FST里只需存储"c","a","t","ch","s"等部分。
FST路径 matching的效率比普通字典树等数据结构高很多。
Lucene在内存中使用FST来替代原始的term字典实现。
通过FST可以把庞大的词表高效压缩存储在内存中而不过度占用内存。
所以简单总结,FST就是一个将词汇表通过有限状态和路径匹配的理念进一步优化压缩的内存结构。 它让Lucene能高效地利用有限内存存储和访问巨大的词汇信息。
Elasticsearch 为啥比Mysql快
首先es是分布式,RESTful风格的搜索和数据分析引擎 Mysql 只有 term dictionary 这一层,是以 b+tree 排序的方式存储在磁盘上的。检索一个 term 需要若干次随机 IO 的磁盘操作。而 Lucene 在 term dictionary 的基础上添加了term index来加速检索,term index 以树的形式缓存在内存中。从 term index 查到对应的 term dictionary 的 block 位置之后,再去磁盘上找 term,大大减少了磁盘的 random access (随机IO)次数
简单的restful api,天生兼容多语言开发
在存储上 es是document格式的存储,mysql是表格模型的, 每个表有固定的结构,行和列对应记录。所有es不需要显式定义字段,而mysql需要。
在架构上面es是天然分布式的,可以很容易横向扩展,它是以cluster(集群)的形式运行,默认几乎所有的功能都有 cluster aware(集群感知)的能力,每个节点加入集群后,数据和任务会自动负载均衡到各个节点上。只需增加节点,即可实现无缝扩展。
先比MySQL不行,默认采用的是单机部署模式,而实现MySQL的集群部署,需要额外设置如MySQL集群(MySQL Cluster)或者MySQL主从复制等。 MySQL cluster,它的扩展也较为困难,比如增加新节点后需要手动重新平衡数据分片。
Elasticsearch每个节点都完整持有集群部分数据和书库,可以并行处理多个请求。而MySQL单点处理能力有限。
Elasticsearch写操作可以并行地写到多个分片中,提高吞吐量;MySQL单机写操作速度受限于磁盘I/O效率。
总之,Elasticsearch支持出于集群的设计,数据和负载可以动态分布到新增节点,无需人工干预,这就赋予了它强大的水平扩展能力
Lucene 的 term dictionar 是存储在内存吗
Lucene的term dictionary默认不是完全加载到内存中的,它采取了磁盘与内存混合存储的策略:
term dictionary以块(block)的形式存储在磁盘上,每个块包含一定数量的term。
系统启动时,Lucene会将部分活跃块加载到内存中,这些块组成了内存中的term dictionary缓存。
当需要查询term时,首先从内存缓存中查找。如果没有,则加载对应的disk块到内存缓存。
缓存满了,会根据使用频率驱逐最少使用的块出内存。
读写term时,都会更新对应的内存和磁盘块,保持数据一致。
所以:
term dictionary本身存放在磁盘,以节省内存
但热门块会保持在内存中,加速重复查询
通过块机制控制内存占用,保证性能和内存平衡
只有在内存很大或查询压力很低的情况下,Lucene才会完全加载term dictionary到内存。
因此说term dictionary默认是磁盘+内存混合存储更为准确。两级存储策略利用内存提速,又避免了全量加载的内存开销。
Es的text和keyword区别
ES5.0及以后的版本取消了string类型,将原先的string类型拆分为text和keyword两种类型。 区别在于text会对字段进行分词处理而keyword则不会进行分词。如果字段是text类型,存入的数据会先进行分词,然后将分完词的词组存入索引,而keyword则不会进行分词,直接存储。
text
类型的数据被用来索引长文本,例如电子邮件主体部分或者一款产品的介绍,这些文本会被分析在建立索引文档之前会被分词器进行分词,转化为词组。经过分词机制之后es允许检索到该文本切分而成的词语,但是text类型的数据不能用来过滤、排序和聚合等操作。 (强行使用报错示例java :co.elastic.clients.elasticsearch._types.ElasticsearchException: [es/search] failed: [search_phase_execution_exception] all shards failed
)keyword
类型的数据可以满足电子邮箱地址、主机名、状态码、邮政编码和标签等数据的要求,不进行分词,常常被用来过滤、排序和聚合。