ES八股相关知识
为什么要使用ElasticSearch?和传统关系数据库(如 MySQL)有什么不同?
典型回答
数据模型
Elasticsearch 是基于文档的搜索引擎,它使用 JSON 文档来存储数据。在 Elasticsearch 中,相关的数据通常存储在同一个文档中,而不是分散在多个表中。
MySQL 是一个关系型数据库管理系统,它使用表、行和列的结构来组织数据。数据通过外键关系分散在多个表中。
查询语言
Elasticsearch使用 QueryDSL(Domain Specific Language),这是一种非常灵活的查询语言,基于JSON,支持全文搜索、复合查询、过滤以及聚合等。
MySQL 使用 SQl(Structured Query Language),这是一种强类型和非常成熟的语言,专门用于查询和管理关系数据库。
全文搜索
Elasticsearch 的核心功能是全文搜索。它对数据进行索引时会自动建立全文搜索索引,使其在搜索大量文本数据时表现优异。
MySQL虽然也提供了基本的全文搜索功能,但其主要设计目标是处理结构化数据的存储和查询,对全文搜索的支持不如 Elasticsearch 那样强大。
事务支持
Elasticsearch 不支持传统的 ACID(原子性、一致性、隔离性、持久性)事务。虽然它确保了单个文档操作的原子性,但不适用于跨多个文档的复杂事务。
MySQL 支持 ACID 事务,这使得它非常适合需要严格数据一致性的应用,如金融服务和其他商业数据处理,
主要场景和优势
Elasticsearch是一个开源的分布式搜索和分析引擎,主要适用于以下场景
1.搜索引擎:用于快速检索文档、商品、新闻等。
2.日志分析:通过分析日志数据,帮助企业了解其业务的性能情况。
3.数据分析:帮助数据科学家和数据分析师进行数据分析,以获取有价值的信息,
4.商业智能:帮助企业制定数据驱动的决策,以实现商业上的成功。
5.实时监控:帮助企业实时监测系统性能、监控数据变化,以保证系统正常运行。
6.安全性:帮助企业保证数据的安全性,保证数据不被非法窃取。
7.应用程序开发:帮助开发人员开发基于搜索的应用程序,以加用户体验。
Elasticsearch具有以下几个优势:
1.高性能:Elasticsearch具有高性能的搜索和分析能力,其中涵盖了多种查询语言和数据结构。
2.**可扩展性:**Elasticsearch是分布式的,可以通过增加节点数量扩展搜索和分析能力。
3.灵活性:Elasticsearch支持多种数据类型,支持多种语言,支持动态映射,允许快速地调整模型以适应不同
的需求。
4.实时分析:Elasticsearch支持实时分析,可以对数据进行实时查询,这对于快速检索数据非常有用。
5.**可靠性:**Elasticsearch具有可靠性和高可用性,支持数据备份和恢复。
ElasticSearch为什么快?
典型回答
Elasticsearch是一个高性能、分布式搜索引擎,它之所以快,主要有以下几个原因:
1.分布式存储:Elasticsearch使用分布式存储技术,将数据存储在多个节点上,从而减少单个节点的压力,提高整体性能。
2.索引分片:Elasticsearch把每个索引划分成多个分片,这样可以让查询操作并行化,从而提高查询速度。
3.全文索引:Elasticsearch使用了高效的全文索引技术,把文档转化成可搜索的结构化数据,使得搜索操作快速高效。
4.倒排索引:Elasticsearch支持倒排索引这种数据结构,倒排索引将文档中的每个词与该词出现在哪些文档中进行映射,并存储这些信息。当搜索请求发生时,ES可以快速查找包含所有搜索词的文档,从而返回结果。
5.索引优化:Elasticsearch通过索引优化技术,可以使查询速度更快。例如,它支持索引覆盖、索引下推等优化技术,使得查询速度更快。
6.预存储结果:Elasticsearch在插入数据时,对数据进行预处理,把结果预存储到索引中,从而在查询时不需要再重新计算,提高查询速度。
7.高效的查询引警:Elasticsearch使用了高效的查询引擎,支持各种类型的查询,并对复杂查询提供了优化策略,从而提高查询速度。
8.异步请求处理:ES使用了异步请求处理机制,能够在请求到达时立即返回,避免长时间的等待,提高用户体验
9.内存存储:ES使用了内存存储技术,能够在读写数据时大大减少磁盘访问次数,提高数据存储和查询效率。
总之,Elasticsearch快的原因在于它使用了各种高效的技术,使得数据存储、查询、处理都变得更加高效,从而实现了快速的搜索体验。
倒排索引是什么?
典型回答
在 ElasticSearch 中,倒排索引是一种常用的索引结构,用于快速搜索文档中的某个词汇。
倒排索引的结构与传统的索引结构相反,传统的索引结构是由文档构成的,每个文档包含了若干个词汇,然后根据这些词汇建立索引。而倒排索引是由词汇构成的,每个词汇对应了若干个文档,然后根据这些文档建立索引。
对于一个包含多个词汇的文档,倒排索引会将每个词汇作为一个关键字(Term),然后记录下该词汇所在的文档编号(DocumentID)及该词汇在文档中的位置(Term Position)。这样,当用户输入一个关键字时,就可以快速地查找到包含该关键字的文档编号,然后通过文档编号再查找到对应的文档内容。
倒排索引的优点在于它可以快速定位包含关键字的文档,而且可以支持复杂的搜索操作,如词组搜索、通配符搜索等。同时,由于倒排索引是由词汇构成的,因此在进行数据分析和统计时也非常有用。在ElasticSearch 中,倒排索引是一种非常重要的索引结构,它被广泛应用于搜索引擎、日志分析、推荐系统等领域。
扩展知识
倒排索引建立过程
ES中的倒排索引建立过程主要有2个步骤,分别是分词、建立倒排索引
比如我们现在有三份文档内容,分别是
分词
在倒排索引建立过程中,首先需要将文档中的原始文本分解成一个个词项(Term)。Elasticsearch 中默认使用标准分词器(Standard Analyzer)进行分词。
以上三个文本内容,我们经过分词之后,就会包含了“深入"、“理解"、“Java”、“核心”、"技术”、"编程”、“思想”、“Hollis”、“周志明”、"布鲁斯·埃克尔"等词
生成倒排索引
将分开的词,当做索引,与对应的文档ID进行关联,形成倒排表。
在生成了倒排表后,还会对倒排表进行压缩,减少空间占用。常用的压缩算法包括Variable Bvte Encoding和Simple9等。最后再将压缩后的倒排表存储在磁盘中,以便后续的搜索操作能够快速地访问倒排表。
如何保证ES和数据库的数据一致性?
典型回答
在业务中,我们通常需要把数据库中的数据变更同步到ES中,那么如何保证数据库和ES的一致性呢?通常有以下几种做法:
双写
在代码中,对数据库和ES进行双写,并且先操作本地数据库,后操作ES,而且还需要把两个操作放到一个事务中:
在以上逻辑中,如果写数据库成功,写ES失败,那么事务会回滚,
如果写数据库成功,写ES超时,实际上ES操作成功,这时候数据库会回滚,导致数据不一致。这时候需要重试来保证最终一致性。
这个方案的好处就是简单,容易实现。并且实时性比较高
缺点首先是需要改代码,有侵入性,还有就是存在不一致的情况。并且在本地事务中发生了外调(外部调用,调ES),大大拖长了事务,白白占用数据库链接,影响整体的吞吐量。
MQ异步消费
在应用中,如果我要更新数据库了,那么就抛一个消息出去,然后数据库和ES各自有一个监听者,监听消息之后各自去做数据变更,如果失败了就基于消息的重试在重新执行。
或者像之前那个方案一样,先操作数据库,然后异步通知ES去更新,这时候就可以借助本地消息表的方式来保证最终一致性了。
这个方案的好处是用了MQ,起到了解耦的作用,而且还做到了异步,提升了整体性能。
缺点就是MQ可能存在延迟,并且需要引入新的中间件,复杂度有所提升。
扫表定时同步
如果是ES中的数据变更的实时性要求不高,可以考虑定时任务扫表,然后批量更新ES。
这个方案优点是没有侵入性,数据库的写操作处不需要改代码。
缺点是实时性很差,并且轮询可能存在性能问题、效率问题以及给数据库带来压力。
监听binlog同步
还有一种方案,就是可以利用数据库变更时产生的binl0g来更新ES。通过监听binlog来更新ES中的数据,也有成熟的框架可以做这样的事情
好处就是对业务代码完全没有侵入性,业务也非常解,不需要关心这个ES的更新操作。
缺点就是需要基于binlog监听,需要引入第三方框架。存在一定的延迟,
总结一下,目前业内比较流行的方案是基于binlog监听的这种,首先一般业务量小的业务也不太需要用ES,所以用了ES的团队,一般并不太会关心引入新框架的复杂度问题,而且ES这种搜索,一般来说,秒级的延迟都是可以接受的,所以,综合来讲,基于canal做数据同步的方案,是比较合适的。
什么是ElasticSearch的深度分页问题?如何解决?
典型回答
在Elasticsearch中进行分页查询通常使用from和size参数。当我们对Elasticsearch发起一个带有分页参数的查询(如使用from和size参数)时,ES需要遍历所有匹配的文档直到达到指定的起始点(from),然后返回从这一点开始的size个文档。
在这个例子中:
·from 参数定义了要跳过的记录数。在这里,它跳过了前20条记录:
·size 参数定义了返回的记录数量。在这里,它返回了10条记录。.
from + size 的总数不能超过Elasticsearch索引的index.max result window设置,默认为10000。这意味着如果你设置from为9900,size为100,查询将会成功。但如果from为9900,size为101,则会失败。
ES的检索机制决定了,当进行分页查询时,Elasticsearch需要先找到并处理所有位于当前页之前的记录。例如如果你请求第1000页的数据,并且每页显示10条记录,系统需要先处理前9990条记录,然后才能获取到你请求的那10条记录。这意味着,随着页码的增加,数据库需要处理的数据量急剧增加,导致查询效率降低。
这就是ES的深度分页的问题,深度分页需要数据库在内存中维护大量的数据,并对这些数据进行排序和处理,这会消耗大量的CPU和内存资源。随着分页深度的增加,查询响应时间会显著增加。在某些情况下,这可能导致查询超时或者系统负载过重,
所以,需要想办法解决ES的深度分页的问题。
scrol
Scroll API在Elasticsearch中的主要目的是为了能够遍历大量的数据,它通常用于数据导出或者进行大规模的数据分析。可以用于处理大量数据的深度分页问题。
如上方式初始化一个带有scroll参数的搜索请求。这个请求返回一个scrol ID,用于后续的滚动。Scrol参数指定了scroll的有效期,例如1m表示一分钟。
接下来就可以使用返回的scrollD来获取下一批数据。每次请求也会更新scrollD的有效期。
我们需要重复以上操作直到到达想要的页数。比如第10页,则需要执行9次滚动操作,然后第10次请求将返回第10页的数据。
Scrol API可以解决深度分页问题,主要是因为他有以下几个特点:
1.避免重复排序:
·在传统的分页方式中,每次分页请求都需要对所有匹配的数据进行排序,以确定分页的起点。Scroll避免了这种重复排序,因为它保持了一个游标。
2.稳定视图:
Scroll提供了对数据的“稳定视图”。当你开始-个scrol时,Elasticsearch会保持搜索时刻的数据快照,这意味着即使数据随后被修改,返回的结果仍然是一致的。
3.减少资源消耗:
由于不需要重复排序,Scroli减少了对CPU和内存的消耗,特别是对于大数据集。
Scroll非常适合于处理需要访问大量数据但不需要快速响应的场景,如数据导出、备份或大规模数据分析。
但是,需要知道,使用Scrol API进行分页并不高效,因为你需要先获取所有前面页的数据。Scroll API主要用于遍历整个索引或大量数据,而不是用于快速访问特定页数的数据。
search after
search after 是 Elasticsearch 中用于实现深度分页的一种机制。与传统的分页方法(使用 from 和 size 参数)不同,search after 允许你基于上一次查询的结果来获取下一批数据,这在处理大量数据时特别有效。
在第一次查询时,你需要定义一个排序规则。不需要指定 search after 参数:
这个查询按 timestamp 字段排序,并在相同 timestamp 的情况下按 id 排序.
在后续的查询中,使用上一次查询结果中最后一条记录的排序值。
在这个例子中,search after 数组包含了 timestamp 和 id 的值,对应于上一次查询结果的最后一条记录。
search after 可以有效解决深度分页问题,原因如下
1.避免重复处理数据:与传统的分页方式不同,search after 不需要处理每个分页请求中所有先前页面上的数据。这大大减少了处理数据的工作量。
2.提高查询效率:由于不需要重复计算和跳过大量先前页面上的数据,search after 方法能显著提高查询效率,尤其是在访问数据集靠后部分的数据时。
但是这个方案有一些局限,一方面需要有一个全局唯一的字段用来排序,另外虽然一次分页查询时不需要处理先前页面中的数据,但实际需要依赖上一个页面中的查询结果。
对比
·对于小型数据集和需要随机页面访问的标准分页场景,传统的分页是最简单和最直接的选择。
·对于需要处理大量数据但不需要随机页面访问的场景,尤其是深度分页,search after提供了更好的性能和更高的效率。
·当需要处理非常大的数据集并且对数据一致性有要求时(如数据导出或备份),Scrol API是一个更好的选择。
如何优化 ElasticSearch 搜索性能?
典型回答
优化 Elasticsearch(ES)的查询性能涉及多个方面,从查询本身到集群配置和硬件资源。以下是一些关键的优化策略:
集群和硬件优化
·负载均衡: 确保查询负载在集群中均衡分配。
·硬件资源: 根据需要增加 CPU、内存或改善 I/O 性能(例如使用 SSD)
·配置 JVM: 优化 JM 设置,如堆大小,以提高性能。
合理分片和副本
虽然更多的分片可以提高写入吞吐量,因为可以并行写入多个分片。但是,查询大量分片可能会降低查询性能,因为每个分片都需要单独处理查询。而且分片数量过多可能会增加集群的管理开销和降低查询效率,尤其是在内存和文件句柄方面。所以,需要考虑数据量和硬件资源,合理设置分片数量。
但是这个说起来比较玄学,毕竟没有一种“一刀切”的方法来确定最优的分片和副本数量,因为这取决于多种因素,包括数据的大小、查询的复杂性、硬件资源和预期的负载等。
在ES每个节点上可以存储的分片数量与可用的堆内存大小成正比关系,但是 Elasticsearch 并未强制规定固定限值。这里有一个很好的经验法则:确保对于节点上已配置的每个 GB,将分片数量保持在 20 以下。如果某个节点拥有 30GB 的堆内存,那其最多可有 600 个分片,但是在此限值范围内,您设置的分片数量越少,效果就越好。一般而言,这可以帮助集群保持良好的运行状态。(来源参考:https://www.elastic.co/cn/blog/how-many-shards-should-i-have-in-my-elasticsearch-cluster )
精确的映射和索引设置
映射(Mapping)是定义如何存储和索引文档中字段的规则。我们可以在以下几个方面做一些优化
确切定义字段类型:为每个字段指定正确的数据类型(如 text,keyword,date,integer 等),这是因为不同的数据类型有不同的存储和索引方式。需要注意的是: text 类型用于全文搜索,它会被分析(analyzed),即分解为单个词项。 keyword 类型用于精确值匹配,过滤,排序和聚合。它不会被分析。
根据需要选择合适的分析器(Analyzer),对于 text 类型的字段,可以指定分析器来定义文本如何被分割和索引。对于不需要全文搜索的字段,使用keyword类型以避免分析开销。
查询优化
很多人用ES很慢,是因为自己的查询本身就用的不对,我们可以尝试着优化一下你的查询。如:
**·避免高开销查询:**如 wildcard、regexp 等类型的查询往往开销较大,尽量避免使用或优化其使用方式。
·使用过滤器:对于不需要评分的查询条件,使用filter 而不是query,因为 filter可以被缓存以加快后续相同查询的速度。
·查询尽可能少的字段:只返回查询中需要的字段,减少数据传输和处理时间。避免深度分页:
·避免深度分页,对于需要处理大量数据的情况,考虑使用search after
·避免使用脚本:尽量避免使用脚本(Script)查询,因为它们通常比简单査询要慢。(脚本执行通常比静态查询更消耗资源。每次执行脚本时,都需要进行编译(除非缓存)和运行,这会增加CPU和内存的使用。脚本执行不能利用索引,因此可能需要全面扫描文档。)
·使用 match 而非 term 查询文本字段:match 查询会分析査询字符串,而 term 查询不会,适用于精确值匹
西己。
·避免使用通配符、正则表达式:这类查询往往非常消耗资源,特别是以通配符开头的(如 *text)。
·合理使用聚合:聚合可以用于高效地进行数据分析,但复杂的聚合也可能非常消耗资源。优化聚合查询,如通过限制桶的数量,避免过度复杂的嵌套聚合。
使用缓存
·请求缓存: 对于不经常变化的数据,利用 ES 的请求缓存机制。
·清理缓存: 定期清理不再需要的缓存,释放资源。
监控和分析
·监控: 使用 Kibana、Elasticsearch-head、Elastic HQ 等工具监控集群状态和性能。
·慢查询日志: 启用慢查询日志来识别和优化慢查询。
ES支持哪些数据类型,和MySQL之间的映射关系是怎么样的?
典型回答
Elasticsearch支持以下数据类型
1**.Text**: 用于存储全文文本数据,如文章或书籍内容。支持全文搜索和分析。
2.Keyword: 用于存储文本值,通常用于索引结构化内容,如邮件地址、标签或任何需要精确匹配的内容。
3.Date: 存储日期或日期和时间。
4.Long,lnteger, Short,Byte,Double,Float: 这些是数值类型,用于存储各种形式的数字。
5.Boolean: 存储 true 或 false 值。
6.Binary: 用于存储二进制数据。
7.Object: 用于嵌套文档,即文档内部可以包含文档。
8.Nested: 类似于 Obiect 类型,但用于存储数组列表,其中列表中的每个元素都是完全独立且可搜索的。
我们经常会把MySQL 中的数据同步到ES 中,他们之间的类型的映射关系如下:
扩展知识
text 和 keyword 有啥区别?
text 类型被设计用于全文搜索。这意味着当文本被存储为 text 类型时,Elasticsearch 会对其进行分词,把文本分解成单独的词或短语,便于搜索引擎进行全文搜索。因为 text 字段经过分词,它不适合用于排序或聚合查询。
text适用于存储需要进行全文搜索的内容,比如新闻文章、产品描述等。
keyword 类型用于精确值匹配,不进行分词处理。这意味着存储在 keyword 字段的文本会被当作一个完整不可分割的单元进行处理。因为 keyword 类型字段是作为整体存储,它们非常适合用于聚合(如计数、求和、过滤唯一值等)和排序操作。由于不进行分词,keyword 类型字段不支持全文搜索,但可以进行精确匹配查询。
keywordl适用于需要进行精确搜索的场景,比如标签、ID 编号、邮箱地址等。
ES 不支持 decimal,如何避免丢失精度?
典型回答
通过上文我们知道,ES 不支持 decimal 类型的,只有 double、foat 等类型,那么,MySQL 中的 decimal 类型,同步到 ES 之后,如何避免丢失精度呢?
如以上 price 字段,在 es 中如何表示呢?有以下几种方式:
使用字符串类型(推荐)
将 decimal 数据作为字符串类型存储在 Elasticsearch 中。这种方式可以保证数字的精度不会丢失,因为字符串会保留数字的原始表示形式。
·优点:完全保留数字的精度。简单易于实现,数据迁移时不需特别处理。
·缺点:作为字符串存储的数字不能直接用于数值比较或数学运算,需要在应用层处理转换。
扩大浮点类型的精度(推荐)
虽然 double 类型在理论上可能会有精度损失,但实际上 double 类型提供的精度对于许多业务需求已经足够使用。如果决定使用这种方法,可以在数据迁移或同步时适当扩大数值范围以尽量减小精度损失
·优点:可以直接进行数值比较和数学运算。
·缺点:在非常高精度的需求下可能存在精度损失。
使用scaled float(推荐)
Elasticsearch 的 scaled float 类型是一种数值数据类型,专门用于存储浮点数。其特点是通过一个缩放因子(scaling factor)将浮点数转换为整数来存储,从而在一定范围内提高存储和计算的效率。
他使用一个缩放因子将浮点数转换为整数存储。例如,如果缩放因子是 100,那么值 123.45 会存储为 12345.
这样可以避免浮点数存储和计算中的精度问题。
使用多个字段
在某些情况下,可以将 decimal 数值拆分为两个字段存储:一个为整数部分,另一个为小数部分。这样做可以在不丢失精度的情况下,将数值分开处理。
·优点:保持数值精确,同时可进行部分数学运算,
·缺点:增加了数据处理的复杂性,需要在应用层重建数值。
使用自定义脚本
在查询时,可以使用 Elasticsearch 的脚本功能(如 Painless 脚本)来处理数值计算,确保在处理过程中控制精度。
·优点:灵活控制数据处理逻辑。
·缺点:可能影响查询性能,增加系统复杂性。
lasticsearch支持事务吗?为什么?
典型回答
**ES 虽然也可以认为是一个数据库,但是他并不支持传统意义上的 ACID 事务,**因为 ES它被设计出来是主要用作搜索引擎的,主要是提升查询效率的。如果支持复杂的事务操作意味着就要牺牲性能优势
虽然 Elasticsearch 不支持传统的事务,但是他是可以确保单个文档的更改(如创建、更新、删除)是原子性的
这意味着任何文档级的操作都完整地成功或完整地失败,但不保证跨多个文档或操作的一致性。
ES 支持乐观锁吗?如何实现的?
典型回答
支持,Elasticsearch 支持通过使用文档版本控制来实现乐观锁。(https://www.elastic.co/quide/en/elasticsearch/reference/current/optimistic-concurrency-control.html )
在 ES 中,每个文档存储时都有一个version 字段,这个版本号在每次文档更新时自动增加。当我们执行更新、删除或者使用脚本处理文档时,可以指定这个版本号来确保正在操作的文档是预期中的版本。如果操作中的版本号与存储在索引中的文档版本号不一致,说明文档已被其他操作更改,当前操作将会失败。(CAS)
version 关键字进行乐观锁已经被废弃了,替代方法是使用但是,从Elasticsearch 6.7 版本开始,使用if seg no和 if primary term 来指定版本。
假设有一个文档:
基于他,我们可以在更新时进行乐观锁控制,避免发生并发修改
这里的 if seq no 和if primary term 是 Elasticsearch 中的字段,用于管理乐观锁。如果文档自上次你读取以来没有被更改,if seq no 和 if primary term 会匹配,你的更改就会被应用。如果不匹配,更新操作会失败。
·seq no 是一个递增的序列号,表示文档的每次修改,
·primary term 表示主分片的当前任期,每当主分片发生变化时,这个值会增加。
扩展知识
为什么version 被废弃
原来的 version 机制是基于单一递增整数,它主要适用于简单的冲突检测,但在复杂的分布式系统中,仅依靠版本号可能无法准确地反映数据的历史和复制状态。尤其是在发生网络分区或节点故障时,仅凭version 可能导致数据丢失或过时的数据被错误地写入。
并且在高并发和高可用的环境下,单一的版本号不足以处理因节点故障或网络问题导致的多个副本之间的数据不-致问题。
而if seq no 和 if primary term 提供了一种方式来跟踪每个文档变更的序列号和主分片的期限。这允许系统更精确地控制和理解文档的变化历史,特别是在分布式和高并发的环境中。
使用这两个参数可以更准确地确定操作是否应当被执行。seg no 是一个递增的序列号,表示文档的每次修改;primary term 表示主分片的当前任期,每当主分片发生变化时,这个值会增加。这两个值共同工作,可以帮助系统在主分片发生变化时,侦测并防止冲突。
这种机制帮助确保即使在集群状态发生变化(如分片重新分配到新的节点)的情况下,也不会应用基于过时副本的更新。"它有效地防止了脑裂问题。
ES 支持悲观锁吗?
不支持!