Java高级开发高频面试题(十一)

本文涉及的产品
检索分析服务 Elasticsearch 版,2核4GB开发者规格 1个月
日志服务 SLS,月写入数据量 50GB 1个月
简介: Java高级开发高频面试题

🍊 磁盘优化

Elasticsearch的性能会受到磁盘延迟的影响,因此为了优化磁盘性能,建议使用高速存储设备如SSD或NVMe,并选择合适的RAID级别。尽可能选择固态硬盘(SSD),因为它比任何旋转介质机械硬盘或磁带写入数据时都会有较大的IO提升,特别是在随机写和顺序写方面。同时,应确保系统I/O调度程序的正确配置,以优化写入数据发送到硬盘的时间,这可以提高写入速度。默认*nix发行版下的调度程序cfq是为机械硬盘优化的,而deadline和noop是为SSD优化的,使用它们可以提高写入性能。

如果使用机械硬盘,可以尝试获取15kRPM驱动器或高性能服务器硬盘来提高磁盘速度。此外,使用RAID0也是提高硬盘速度的有效途径。不需要使用镜像或其他RAID变体,因为Elasticsearch本身提供了副本功能进行备份。如果在硬盘上使用备份功能,对写入速度有较大的影响。最后,避免使用网络附加存储(NAS),因为NAS通常很慢、延时大且是单点故障,而且不如本地驱动器更可靠。因此,建议将数据和索引分开存储,以充分利用不同类型的存储设备。

🍊 计算机系统优化

为了优化Elasticsearch的性能,在以下方面可以采取措施:

(1)禁用不必要的服务和进程,避免占用系统资源,确保Elasticsearch能够充分利用系统资源。

(2)将Elasticsearch节点配置为静态IP,避免动态IP更改导致网络连接问题,从而保证Elasticsearch节点的稳定性。

(3)关闭防火墙或者调整其设置,以避免防火墙对网络流量造成延迟和影响搜索性能。

操作系统对于Elasticsearch的性能有着重要影响,因此为了优化操作系统设置,可以采取以下措施:

首先,可以通过调整内核参数来提高网络性能和文件系统性能,这可以通过修改/sys文件系统下的控制参数来实现。例如,可以通过调整tcp_tw_reuse和tcp_tw_recycle参数来提高TCP连接的处理效率。同时,也可以通过调整文件系统的读写缓存参数来提高文件系统的性能。

其次,可以将文件描述符限制设置为较高的值,以允许Elasticsearch使用更多的文件句柄。可以通过修改/etc/security/limits.conf文件来设置文件描述符限制。这样可以避免因文件描述符数量不足而导致Elasticsearch性能下降的情况。

另外,还可以启用TransparentHugepages和NUMA支持,以提高内存访问效率。TransparentHugepages是一种Linux内核特性,它可以将大页内存自动映射到进程的虚拟内存空间中,从而减少内存管理时的开销。而NUMA是一种多处理器系统结构,它可以将内存和处理器的访问紧密地绑定在一起,从而提高内存访问效率。

综上所述,通过对操作系统进行优化设置,可以有效提升Elasticsearch的性能和稳定性。

🍊 Elasticsearch本身配置参数

为了优化Elasticsearch性能,可以调整以下配置参数:

(1)分配更大的JVM堆内存,以允许Elasticsearch使用更多的内存来缓存索引和搜索结果。

(2)调整索引分片数量,以平衡查询负载和数据分布。

(3)调整索引缓存设置,以缓存查询结果并减少IO操作。

(4)调整搜索线程池大小,以避免搜索请求积压造成性能下降。

🍊 GC调优

根据Elasticsearch官方发布的文档,JDK8附带的HotSpotJVM的早期版本存在可能导致索引损坏的问题,特别是在启用G1GC收集器时。这个问题影响JDK8u40附带的HotSpot版本之前的版本。如果使用较高版本的JDK8或JDK9+,建议使用G1GC,因为它对Heap大对象的优化效果比较明显,目前的项目也是使用G1GC并且运行效果良好。为了启用G1GC,需要修改jvm.options文件中的配置。将原本的如下配置,代码如下:

# 开启使用CMS垃圾回收器
-XX:+UseConcMarkSweepGC
# 初始化CMS垃圾回收器的初始占用阈值为75%
-XX:CMSInitiatingOccupancyFraction=75
# 只使用初始化占用阈值作为CMS垃圾回收器的出发条件
-XX:+UseCMSInitiatingOccupancyOnly
改为:
# 开启 G1 垃圾回收器
-XX:+UseG1GC
# 设置最大垃圾回收时间为 50 毫秒
-XX:MaxGCPauseMillis=50

其中,-XX:MaxGCPauseMillis用于控制预期的最高GC时长,默认值为200ms。如果线上业务对GC停顿敏感,可以适当设置低一些,但是如果设置过小,可能会带来比较高的CPU消耗。需要注意的是,如果集群因为GC导致卡死,仅仅换成G1GC可能无法根本上解决问题,通常需要优化数据模型或者Query。总之,使用G1GC需要慎重,需要根据具体情况进行评估和调整。

🍊 索引优化设置

索引优化是Elasticsearch中的一个重要方面,它主要是通过优化插入过程来提升Elasticsearch的性能。虽然Elasticsearch的索引速度本身已经相当快了,但是具体数据仍可以参考官方的benchmark测试结果来了解。根据测试结果总结,不同的硬件和软件组合会影响Elasticsearch在不同场景下的性能表现。具体如下:

(1)索引速度:单节点下,Elasticsearch可以达到每秒2,000个文档的索引速度;而在分布式集群中,其索引速度可以达到每秒3,000到8,000个文档。使用具有高性能硬件配置的服务器可获得更快的索引速度。而索引速度取决于机器的CPU、内存、网络带宽和磁盘性能等因素。

(2)搜索速度:在单节点上,Elasticsearch的搜索速度可以达到每秒50万到70万次查询;在分布式集群上,随着节点和硬件的增加,其搜索速度可以线性扩展,最高可以达到数百万次查询。使用SSD硬盘的服务器和更高版本的Elasticsearch(如5.x和6.x)也可以提高搜索速度。

(3)响应时间:在所有硬件和软件组合下,Elasticsearch的响应时间都可以控制在1秒以内。使用高性能硬件配置的服务器可以获得更快的响应时间。

(4)内存使用:Elasticsearch使用内存来缓存数据,提高读取和写入速度。虽然内存使用与集群规模和硬件配置有关,但在所有情况下,Elasticsearch的内存使用都可以整体控制在较低的水平。使用高性能硬件配置的服务器可以获得更低的内存使用率。

总之,硬件配置越高,性能指标就越好。但是需要注意的是,硬件配置越高,成本也会越高。因此,在实际使用中,需要根据自己的应用场景和需求,选择合适的硬件配置,以达到最优性能和成本的平衡。

🍊 批量提交

建议在提交大量数据时,采用批量提交(Bulk操作)的方式来提高效率。使用bulk请求时,每个请求的大小不要超过几十兆字节,因为太大会导致内存使用过大。在ELK过程中,比如Logstashindexer向Elasticsearch中提交数据,可以通过调整batchsize来优化性能。需要根据文档大小和服务器性能来设置合适的size大小。如果Logstash中提交文档大小超过20MB,Logstash会将一个批量请求切分为多个批量请求。如果在提交过程中,遇到EsRejectedExecutionException异常,则说明集群的索引性能已经达到极限。此时,可以考虑提高服务器集群的资源,或者减少数据收集速度。例如,只收集Warn、Error级别以上的日志,以降低数据量。需要根据业务规则来决定如何进行优化。

🍊 增加Refresh时间间隔

为了提高Elasticsearch的索引性能,在写入数据的过程中,采用了延迟写入的策略。换言之,数据先写入内存中,当超过默认的1秒(即index.refresh_interval)后,会进行一次写入操作,将内存中的segment数据刷新到磁盘中。只有当数据刷新到磁盘中后,才能对数据进行搜索操作,因此Elasticsearch提供的是近实时搜索功能,而不是实时搜索功能。

如果系统对数据的延迟要求不高,可以通过延长refresh时间间隔来减少segment合并压力,从而提高索引速度。例如在进行全链路跟踪时,可以将index.refresh_interval设置为30秒,从而减少refresh次数。在进行全量索引时,可以将refresh次数临时关闭,将index.refresh_interval设置为-1,数据导入成功后再打开到正常模式,例如设置为30秒。在加载大量数据时,也可以暂时不使用refresh和replicas功能,将index.refresh_interval设置为-1,将index.number_of_replicas设置为0。

综上所述,延迟写入的策略可以提高Elasticsearch的索引性能,适当地调整refresh时间间隔和关闭refresh和replicas功能可以更进一步地提高性能。

🍊 修改index_buffer_size的设置

索引缓冲是一个重要的性能优化工具,通过调整索引缓冲的设置,可以控制内存的分配情况,从而优化节点的索引进程的性能。在Elasticsearch中,索引缓冲的设置是一个全局配置,它会应用于一个节点上所有不同的分片上。可以通过在Elasticsearch的配置文件中设置indices.memory.index_buffer_size参数来控制索引缓冲的大小。这个参数可以接受一个百分比或者一个表示字节大小的值。默认是10%,意味着分配给节点的总内存的10%用来做索引缓冲的大小。如果设置的是百分比,那么这个百分比会被分到不同的分片上。同时,也可以通过设置indices.memory.min_index_buffer_size参数来指定最小的索引缓冲大小,这个参数的默认值是48mb。另外,还可以通过设置indices.memory.max_index_buffer_size参数来控制索引缓冲的最大值。需要注意的是,如果为索引缓冲设置了一个过大的值,可能会导致节点的性能下降。因此,在进行索引缓冲的设置时,需要根据具体的应用场景和硬件配置来进行权衡和调整。

🍊 修改translog相关的设置

为了减少硬盘的IO请求,可以采取一系列操作来优化系统的性能表现。其中一个方法是通过控制数据从内存到硬盘的操作频率来实现。可以通过增加sync_interval时间的设置,来延迟数据写入到硬盘的操作。这样可以降低硬盘的负载,减少硬盘的IO请求。sync_interval的默认时间为5秒,可以通过命令index.translog.sync_interval:5s来设置。

另一个方法是控制translog数据块的大小,以减少flush到lucene索引文件的次数。这样可以进一步减少对硬盘的IO请求,提高系统性能表现。translog数据块的默认大小为512m,可以通过命令index.translog.flush_threshold_size:512mb来设置。需要根据具体的实际情况进行调整。

综上所述,以上两种方法都可以减少硬盘的IO请求,提高系统的性能表现。需要根据系统的实际情况进行优化调整,以达到最优化的性能表现。

🍊 _id字段、_all字段、_source字段、index属性

在使用Elasticsearch中,应该注意一些最佳实践的建议。首先,_id字段不应该自定义,因为这可能会导致版本管理方面的问题。建议使用Elasticsearch提供的默认ID生成策略或使用数字类型ID做为主键。

其次,需要注意使用_all字段和_source字段的场景和实际需求。_all字段包含了所有的索引字段,可以方便做全文检索,但如果不需要进行全文检索,就可以禁用该字段。_source字段存储了原始的document内容,如果没有获取原始文档数据的需求,可以通过设置includes和excludes属性来定义需要存储在_source中的字段,避免不必要的存储开销。因此,在使用_all字段和_source字段时,需要根据实际需求来进行权衡和选择。

此外,合理的配置使用index属性,analyzed和not_analyzed,根据业务需求来控制字段是否分词或不分词。只有groupby需求的字段,应该配置成not_analyzed,以提高查询或聚类的效率。综上所述,对于Elasticsearch的使用,需要根据实际情况来进行配置和选择,以达到最佳实践的效果。

🍊 减少副本数量

Elasticsearch默认拷贝数量为3个,这样的配置虽然能够提高Cluster的可用性,增加查找次数,但是对写入索引的效率也会造成影响。在索引过程中,需要将更新的文档发送到副本节点上,等待副本节点生效后才能返回结果。在实际应用中,建议根据实际需求来调整副本数目。对于业务搜索等关键应用场景,建议仍然保留副本数为3个,以确保数据的可靠性和高可用性;而对于内部ELK日志系统、分布式跟踪系统等应用场景,则完全可以将副本数设置为1个来提高写入效率。因此,在进行Elasticsearch集群的配置时,需要综合考虑集群的可用性、性能和数据可靠性等因素,以实现最优的性价比。

🍊 查询方面优化

Elasticsearch作为业务搜索的近实时查询时,查询效率的优化显得尤为重要。

🎉 路由优化

Elasticsearch作为业务搜索的查询效率的优化显得尤为重要。在查询文档时,Elasticsearch使用公式shard=hash(routing)%number_of_primary_shards来计算文档应该存放到哪个分片中。routing默认为文档的ID,也可以使用用户ID等自定义值。通过对路由进行优化,可以提高查询效率和搜索速度。

🎉 routing查询

查询数据时,如果不带routing参数,则查询过程需要经过分发和聚合两个步骤。首先,请求会被发送到协调节点,协调节点将查询请求分发到每个分片上;随后,协调节点搜集每个分片上的查询结果,进行排序,最后将结果返回给用户。这种方式存在的问题是由于不知道需要查询的数据具体在哪个分片上,因此需要搜索所有分片,增加了查询的时间和资源消耗。

相比较而言,带routing查询可以直接根据routing信息定位到某个分片进行查询,而不需要查询所有的分片,经过协调节点进行排序。以用户查询为例,如果将routing设置为userid,则可以直接查询出数据,大大提高了查询效率。

总之,带routing查询可以加速查询过程,减少了不必要的查询操作,提高了查询效率。

🎉 Filter VS Query

Filter与Query的使用方法是Elasticsearch中最常用的两种查询上下文,但是它们的使用方式是有所不同的。在实际使用中,应该尽可能使用过滤器上下文(Filter)进行查询,而不是使用查询上下文(Query)。

Query上下文主要是用来评估文档与查询语句之间的匹配程度,并为匹配的文档打分。相比之下,过滤器上下文主要是用来检查文档是否与查询语句匹配,它所做的仅仅是返回结果为是或否的答案,无需进行打分等计算过程,从而提高查询的效率和性能。

此外,过滤器上下文的结果还可以进行缓存,即使在多次查询中使用同样的查询条件,也可以直接返回缓存中已经计算得到的结果,避免了重复计算,提高了查询效率。因此,在实际使用中,尽可能使用过滤器上下文进行查询是非常有必要和推荐的。

🎉 深度翻页

在使用Elasticsearch过程中,应注意尽量避免大翻页的出现,因为正常翻页查询都是从from开始size条数据,需要在每个分片中查询打分排名在前面的from+size条数据。这样,协同节点将会收集每个分配的前from+size条数据,一共会受到N*(from+size)条数据,然后进行排序,最终返回其中from到from+size条数据。如果from或size很大,参加排序的数量也会同步扩大很多,导致CPU资源消耗增大。

为了解决这一问题,可以使用Elasticsearch中的scroll和scroll-scan高效滚动的方式,以减小CPU资源消耗。

除此之外,还可以结合实际业务特点,根据文档id大小和文档创建时间的一致有序性,以文档id作为分页的偏移量,并将其作为分页查询的一个条件。这样可以优化查询性能,减少排序参与的数据量,提高查询效率。同时,这也需要根据具体业务情况进行实践验证。

🎉 脚本合理使用

在开发中,脚本(script)的合理使用至关重要。目前脚本使用主要有三种形式:内联动态编译方式、_script索引库中存储和文件脚本存储的形式。其中,内联动态编译方式适合较小的脚本,文件脚本存储形式适合大型脚本,而_script索引库中存储形式则是最常见的使用方式之一。

一般来说,在实际应用中,应尽量采用第二种方式,先将脚本存储在_script索引库中,从而能够提前编译脚本。通过引用脚本id并结合params参数,可以实现模型(逻辑)和数据的分离,同时也更便于脚本模块的扩展和维护。

在使用脚本时,一定要注意场景的选择。对于较小的脚本,可以使用内联动态编译方式;对于大型脚本,则应采用文件脚本存储形式。而对于一些常见的使用场景,最好使用_script索引库中存储形式,以达到更好的编译效果和更高的代码可读性。

总之,脚本的合理使用能够提高开发效率和代码质量,是开发过程中非常重要的一环。

🎉 Cache的设置及使用

在Elasticsearch中,查询性能的优化是非常重要的。其中,QueryCache是Elasticsearch查询的关键性能优化之一,因为它可以减少查询的响应时间。当使用filter查询时,ES会自动使用QueryCache。如果业务场景中的过滤查询比较多,建议将querycache设置大一些,以提高查询速度。可以通过indices.queries.cache.size参数来设置QueryCache的大小,它的默认值为10%。用户可以将其设置为百分比或具体值,如256mb。此外,用户还可以通过设置index.queries.cache.enabled参数来禁用QueryCache。

除了QueryCache之外,Elasticsearch还提供了另一个缓存机制,即FieldDataCache。在聚类或排序场景下,Elasticsearch会频繁使用FieldDataCache。因此,为了提高查询性能,建议用户在这些场景下设置FieldDataCache的大小。indices.fielddata.cache.size参数可用于设置FieldDataCache的大小,用户可以将其设置为30%或具体值10GB。但是,如果场景或数据变更比较频繁,设置cache并不是好的做法,因为缓存加载的开销也是特别大的。

在查询请求发起后,每个分片会将结果返回给协调节点。为了提升查询性能,Elasticsearch提供了一个ShardRequestCache。用户可以通过index.requests.cache.enable参数来开启ShardRequestCache。但是,需要注意的是,shardrequestcache只缓存hits.total、aggregations和suggestions类型的数据,并不会缓存hits的内容。用户也可以通过设置indices.requests.cache.size参数来控制缓存空间大小。默认情况下,其值为1%。

🎉 更多查询优化经验

(1)针对query_string或multi_match的查询,可以采用将多个字段的值索引到一个新字段的方法。在mapping阶段设置copy_to属性,将多个字段的值索引到新字段,这样在进行multi_match查询时可以直接使用新字段进行查询,从而提高查询速度。

(2)对于日期字段的查询,特别是使用now进行查询时,由于不存在缓存,建议从业务需求出发,考虑是否必须使用now进行查询。事实上,利用querycache可以大大提高查询效率,因此也需要对查询缓存进行充分利用。

(3)在设置查询结果集大小时,应根据实际情况进行设置,不能设置过大的值,如将query.setSize设置为Integer.MAX_VALUE。因为Elasticsearch内部需要建立一个数据结构来存放指定大小的结果集数据,设置过大的值会耗费大量的内存资源。

(4)对于聚合查询,需要避免层级过深的aggregation,因为这会导致内存和CPU资源消耗较大。建议在服务层通过程序来组装业务,或者采用pipeline的方式来优化查询。

(5)对于预先聚合数据的方式,可以采用复用预索引数据的技巧来提高聚合性能。例如,如果要根据年龄进行分组,可以预先在索引阶段设置一个age_group字段,将数据进行分类,而不是通过rangeaggregations来按年龄分组。这样可以通过age_group字段来进行groupby操作,从而避免层级过深的aggregation查询,提高聚合性能。

(6)在编写代码时,有时需要在数据集中进行匹配查询。对于大型数据集,经常需要考虑性能问题。为了优化查询性能,一种常见的优化方式是使用filter代替match查询。filter的优点在于它可以缓存,因此可以提高查询的速度。此外,由于match查询通常涉及模糊匹配和转换的过程(fuzzy_transpositions),使用filter可以避免这种无意义的查询操作,进一步提高查询效率。然而,需要注意的是,使用filter来代替match查询的优化是有限的。对于一些复杂的查询,仍需要使用match查询来实现。因此,在优化查询性能时,需要综合考虑使用不同的查询方式,以达到最优化的效果。

(7)对于数据类型的选择需要理解Elasticsearch底层数据结构,在Elasticsearch2.x时代,所有数字都是按照keyword类型进行处理,这意味着每个数字都会建立一个倒排索引。虽然这种处理方式可以提高查询速度,但是在执行范围查询时,例如type>1 and type<5,需要将查询转换为type in (1,2,3,4,5),这显著增加了范围查询的难度和耗时。随后,Elasticsearch进行了优化,在处理integer类型数据时采用了一种类似于B-tree的数据结构,即Block k-d tree,以加速范围查询。Block k-d tree被设计用于多维数值字段,并可用于高效过滤地理位置等数据。此外,它还可用于单维度的数值类型。对于单维度的数据,Block k-d tree的实现与传统的B-tree有所不同。它对所有值进行排序,并反复从中心进行切分,生成具有类似B-tree结构的索引。该结构的叶子节点存储的不是单个值,而是一个值的集合,也就是所谓的一个Block。每个Block最多包含512至1024个值,以确保值在Block之间均匀分布。这种数据结构大大提高了范围查询的性能,因为在传统的索引结构中,满足查询条件的文档集合并不是按照文档ID顺序存储的,而是需要构造一个巨大的bitset来表示。而使用Block k-d tree索引结构,则可以直接定位满足查询条件的叶子节点块在磁盘上的位置,然后顺序读取,显著提高了范围查询的效率。

(8)在使用Block k-d tree的数据结构进行范围查询时,磁盘读取是顺序读取,因此对范围查询有很大的优势。然而在某些场景下使用PointRangeQuery会非常慢,因为它需要将满足查询条件的docid集合拿出来单独处理。这个处理过程在org.apache.lucene.search.PointRangeQuery#createWeight方法中可以读取到,主要逻辑是在创建scorer对象的时候,顺带先将满足查询条件的docid都选出来,然后构造成一个代表docid集合的bitset。在执行advance操作时,就会在这个bitset上完成。由于这个构建bitset的过程类似于构造Query cache的过程,所有的耗时都在build_scorer上。因此,使用PointRangeQuery在该场景下会非常慢。另外,对于term查询,如果数值型字段被转换为PointRangeQuery,也会遇到同样的问题。在这种情况下,无法像Postlings list那样按照docid顺序存放满足查询条件的docid集合,因此无法实现postings list上借助跳表做蛙跳的操作。

(9)对于像isDeleted这样只有两个可能取值(是/否)的字段,Elasticsearch会自动根据倒排索引的文档数和Term的文档频率来判断是否使用倒排索引进行查询。如果该Term的文档频率太高,超过了一定的阈值,Elasticsearch会认为使用倒排索引查询的效率不如使用全表扫描,因此会放弃使用倒排索引,转而使用全表扫描。这个阈值可以通过设置index.max_terms_count来调整。如果该字段在查询时频繁被使用,可以考虑将其映射为一个不分词、不使用倒排索引的字段,这样可以避免在查询时产生额外的开销。

(10)当进行多个term查询并列的时候,在Elasticsearch中执行顺序不是由查询时写入的顺序决定的。实际上,Elasticsearch会根据每个filter条件的区分度来评估执行顺序,将高区分度的filter条件先执行,以此可以加速后续的filter循环速度,从而提高查询效率。举例来说,如果一个查询条件的结果集很小,那么Elasticsearch就会优先执行这个条件。为了实现这一点,当使用term进行查询时,每个term都会记录一个词频,即这个term在整个文档中出现的次数。这样Elasticsearch就能判断每个term的区分度高低,从而决定执行顺序。综上所述,当使用多个term查询时,Elasticsearch会根据每个filter条件的区分度来决定执行顺序,以此提高查询效率。

(11)为了快速查找索引 Term 的位置,可以采用哈希表作为索引表来提高查找效率。同时,为了减少倒排链的查询和读取时间,可以采用 RoaringBitmap 数据结构来存储倒排链,并且结合 RLE Container 实现倒排链的压缩存储。这样,可以将倒排链的合并问题转化为排序问题,从而实现批量合并,大大降低了合并的性能消耗。另外,根据数据的分布,自动选择 bitmap/array/RLE 容器,可以进一步提高 RLE 倒排索引的性能。

(12)在增量索引场景下,如果增量索引的变更量非常大,会导致频繁更新内存 RLE 倒排索引,进而带来内存和性能的消耗。为了解决这个问题,可以将 RLE 倒排索引的结构固化到文件中,在写索引时就可以完成对倒排链的编码,避免了频繁更新内存索引的问题。这种做法可以提升索引的写入性能,同时保证了查询的高效性和稳定性。

通过开启慢查询配置定位慢查询

一般而言,当 Elasticsearch 查询所花费的时间超过一定阈值时,系统会记录该查询的相关信息并将其记录在慢查询日志中以供查看。在实际的应用场景中,可以通过对慢查询日志的分析来确定哪些查询较为耗时,从而帮助进行性能优化。通过开启慢查询配置可以快速定位Elasticsearch查询速度缓慢的问题,并进行相应的性能调优,提高系统的查询效率和用户体验。对于 ElasticSearch这类搜索引擎而言,通过设置开启慢查询日志可以快速定位查询速度较慢的原因。

🍊 数据结构优化

针对 Elasticsearch 的使用场景,需要根据实际情况进行文档数据结构的设计,以便更好地发挥 Elasticsearch 的搜索和分析能力。在设计文档结构时,需要将使用场景作为主要考虑因素,去掉不必要的数据。这有助于减少索引的大小、提高搜索和分析的效率。

在实际使用 Elasticsearch 进行数据存储和检索时,应根据具体场景灵活使用索引和文档类型,合理划分数据和定义字段。这有助于提高搜索和分析的精度和效率,并能够满足不同场景的需求。

因此,在使用 Elasticsearch 时,必须深入了解应用场景,进行合理的文档数据结构设计,去掉不必要的数据,提高搜索和分析的效率和精度,最终实现更好的业务效果和用户体验。

🎉 减少不需要的字段

如果Elasticsearch作为业务搜索服务的一部分,应该避免将一些不需要用于搜索的字段存储到Elasticsearch中。这种做法能够节省空间,同时在相同的数据量下,也能提高搜索性能。此外,应该避免使用动态值作为字段,因为动态递增的mapping可能会导致集群崩溃。同样,需要控制字段的数量,业务中不使用的字段应该不要索引。控制索引的字段数量、mapping深度、索引字段的类型,这是优化Elasticsearch性能的关键之一。

Elasticsearch在默认情况下设置了一些关于字段数、mapping深度的限制,即index.mapping.nested_objects.limit、index.mapping.total_fields.limit和index.mapping.depth.limit。其中,index.mapping.nested_objects.limit限制了Elasticsearch中嵌套对象的数量,它的默认值为10000。index.mapping.total_fields.limit限制了Elasticsearch中字段的数量,它的默认值为1000。index.mapping.depth.limit限制了Elasticsearch中mapping的嵌套深度,它的默认值为20。在实际使用中,根据业务需求可以适当调整这些限制值以获得更好的性能。

🎉 Nested Object vs Parent/Child

建议在mapping设计阶段尽量避免使用nested或parent/child的字段,因为这些查询性能较差,能不用就不用。如果必须使用nestedfields,要保证nestedfields字段不能过多,因为针对1个document,每一个nestedfield,都会生成一个独立的document,这将使doc数量剧增,影响查询效率,尤其是JOIN的效率。

默认Elasticsearch的限制是每个索引最多有50个nestedfields,如果需要增加或减少nestedfields的数量,可以修改配置文件中的index.mapping.nested_fields.limit参数。对于常规的文档存储和查询,使用NestedObject可以保证文档存储在一起,因此读取性能高;相反,对于需要独立更新父文档或子文档的情况下,可以使用Parent/Child结构,这样可以保证父子文档可以独立更新,互不影响;但是为了维护join关系,需要占用部分内存,读取性能较差。因此,在选择使用NestedObject还是Parent/Child结构时,需要根据具体的场景进行选择,子文档偶尔更新且查询频繁时可以选择NestedObject,而子文档更新频繁时可以选择Parent/Child结构。

🎉 静态映射

为确保集群的稳定性,在使用Elasticsearch的过程中,推荐选择静态映射方式。静态映射不仅能够保证数据类型的一致性,还能够提高查询效率。相反,如果使用动态映射,可能会导致集群崩溃,并带来不可控制的数据类型,从而影响业务的正常运行。此外,在Elasticsearch中,数据的存储类型分为匹配字段和特征字段两种。匹配字段用于建立倒排索引以进行query匹配,而特征字段如ctr、点击数、评论数等则用于粗排。因此,在设计索引时需要根据不同的功能选择不同类型的字段进行建立倒排索引,以满足业务的需求和提高查询效率。综上所述,静态映射是建立Elasticsearch稳定、高效的必要条件,而动态映射的使用应在必需时加以限制。

🎉 document 模型设计

MySQL经常需要进行一些复杂的关联查询,但是在Elasticsearch中并不推荐使用复杂的关联查询,因为一旦使用会影响性能。因此,最好的做法是在Java系统中先完成关联,将关联好的数据直接写入Elasticsearch中。这样,在搜索时,就不需要使用Elasticsearch的搜索语法来完成关联搜索。

在设计document模型时,需要非常重视,因为在搜索时执行复杂的操作会影响性能。Elasticsearch支持的操作有限,因此需要避免考虑使用Elasticsearch进行一些难以操作的事情。如果确实需要使用某些操作,最好是在document模型设计时就完成。此外,需要尽量避免使用复杂操作,例如join/nested/parent-child搜索,因为它们的性能都很差。

相关实践学习
使用阿里云Elasticsearch体验信息检索加速
通过创建登录阿里云Elasticsearch集群,使用DataWorks将MySQL数据同步至Elasticsearch,体验多条件检索效果,简单展示数据同步和信息检索加速的过程和操作。
ElasticSearch 入门精讲
ElasticSearch是一个开源的、基于Lucene的、分布式、高扩展、高实时的搜索与数据分析引擎。根据DB-Engines的排名显示,Elasticsearch是最受欢迎的企业搜索引擎,其次是Apache Solr(也是基于Lucene)。 ElasticSearch的实现原理主要分为以下几个步骤: 用户将数据提交到Elastic Search 数据库中 通过分词控制器去将对应的语句分词,将其权重和分词结果一并存入数据 当用户搜索数据时候,再根据权重将结果排名、打分 将返回结果呈现给用户 Elasticsearch可以用于搜索各种文档。它提供可扩展的搜索,具有接近实时的搜索,并支持多租户。
相关文章
|
4天前
|
监控 Java 应用服务中间件
高级java面试---spring.factories文件的解析源码API机制
【11月更文挑战第20天】Spring Boot是一个用于快速构建基于Spring框架的应用程序的开源框架。它通过自动配置、起步依赖和内嵌服务器等特性,极大地简化了Spring应用的开发和部署过程。本文将深入探讨Spring Boot的背景历史、业务场景、功能点以及底层原理,并通过Java代码手写模拟Spring Boot的启动过程,特别是spring.factories文件的解析源码API机制。
16 2
|
9天前
|
存储 算法 Java
大厂面试高频:什么是自旋锁?Java 实现自旋锁的原理?
本文详解自旋锁的概念、优缺点、使用场景及Java实现。关注【mikechen的互联网架构】,10年+BAT架构经验倾囊相授。
大厂面试高频:什么是自旋锁?Java 实现自旋锁的原理?
|
14天前
|
存储 缓存 Oracle
Java I/O流面试之道
NIO的出现在于提高IO的速度,它相比传统的输入/输出流速度更快。NIO通过管道Channel和缓冲器Buffer来处理数据,可以把管道当成一个矿藏,缓冲器就是矿藏里的卡车。程序通过管道里的缓冲器进行数据交互,而不直接处理数据。程序要么从缓冲器获取数据,要么输入数据到缓冲器。
Java I/O流面试之道
|
9天前
|
SQL 安全 Java
安全问题已经成为软件开发中不可忽视的重要议题。对于使用Java语言开发的应用程序来说,安全性更是至关重要
在当今网络环境下,Java应用的安全性至关重要。本文深入探讨了Java安全编程的最佳实践,包括代码审查、输入验证、输出编码、访问控制和加密技术等,帮助开发者构建安全可靠的应用。通过掌握相关技术和工具,开发者可以有效防范安全威胁,确保应用的安全性。
23 4
|
10天前
|
存储 缓存 Java
大厂面试必看!Java基本数据类型和包装类的那些坑
本文介绍了Java中的基本数据类型和包装类,包括整数类型、浮点数类型、字符类型和布尔类型。详细讲解了每种类型的特性和应用场景,并探讨了包装类的引入原因、装箱与拆箱机制以及缓存机制。最后总结了面试中常见的相关考点,帮助读者更好地理解和应对面试中的问题。
34 4
|
11天前
|
缓存 监控 Java
如何运用JAVA开发API接口?
本文详细介绍了如何使用Java开发API接口,涵盖创建、实现、测试和部署接口的关键步骤。同时,讨论了接口的安全性设计和设计原则,帮助开发者构建高效、安全、易于维护的API接口。
35 4
|
11天前
|
存储 Java 程序员
Java基础的灵魂——Object类方法详解(社招面试不踩坑)
本文介绍了Java中`Object`类的几个重要方法,包括`toString`、`equals`、`hashCode`、`finalize`、`clone`、`getClass`、`notify`和`wait`。这些方法是面试中的常考点,掌握它们有助于理解Java对象的行为和实现多线程编程。作者通过具体示例和应用场景,详细解析了每个方法的作用和重写技巧,帮助读者更好地应对面试和技术开发。
50 4
|
21天前
|
开发框架 JavaScript 前端开发
HarmonyOS UI开发:掌握ArkUI(包括Java UI和JS UI)进行界面开发
【10月更文挑战第22天】随着科技发展,操作系统呈现多元化趋势。华为推出的HarmonyOS以其全场景、多设备特性备受关注。本文介绍HarmonyOS的UI开发框架ArkUI,探讨Java UI和JS UI两种开发方式。Java UI适合复杂界面开发,性能较高;JS UI适合快速开发简单界面,跨平台性好。掌握ArkUI可高效打造符合用户需求的界面。
74 8
|
16天前
|
SQL Java 程序员
倍增 Java 程序员的开发效率
应用计算困境:Java 作为主流开发语言,在数据处理方面存在复杂度高的问题,而 SQL 虽然简洁但受限于数据库架构。SPL(Structured Process Language)是一种纯 Java 开发的数据处理语言,结合了 Java 的架构灵活性和 SQL 的简洁性。SPL 提供简洁的语法、完善的计算能力、高效的 IDE、大数据支持、与 Java 应用无缝集成以及开放性和热切换特性,能够大幅提升开发效率和性能。
|
17天前
|
存储 Java 关系型数据库
在Java开发中,数据库连接是应用与数据交互的关键环节。本文通过案例分析,深入探讨Java连接池的原理与最佳实践
在Java开发中,数据库连接是应用与数据交互的关键环节。本文通过案例分析,深入探讨Java连接池的原理与最佳实践,包括连接创建、分配、复用和释放等操作,并通过电商应用实例展示了如何选择合适的连接池库(如HikariCP)和配置参数,实现高效、稳定的数据库连接管理。
34 2