想成为一名顶尖Java开发工程师?这些优化手段一定要掌握!(五)

本文涉及的产品
传统型负载均衡 CLB,每月750个小时 15LCU
检索分析服务 Elasticsearch 版,2核4GB开发者规格 1个月
应用型负载均衡 ALB,每月750个小时 15LCU
简介: 想成为一名顶尖Java开发工程师?这些优化手段一定要掌握!

🍊 消息重复消费

消息重复消费的问题

第一种情况是发送时消息重复,当一条消息已被成功发送到服务端并完成持久化,此时出现了网络抖动或者客户端宕机,导致服务端对客户端应答失败。 如果此时生产者意识到消息发送失败并尝试再次发送消息,消费者后续会收到两条内容相同并且 Message ID 也相同的消息。

第二种情况是投递时消息重复,消息消费的场景下,消息已投递到消费者并完成业务处理,当客户端给服务端反馈应答的时候网络闪断。 为了保证消息至少被消费一次,tMQ 的服务端将在网络恢复后再次尝试投递之前已被处理过的消息,消费者后续会收到两条内容相同并且 Message ID 也相同的消息。

第三种情况是负载均衡时消息重复,比如网络抖动、Broker 重启以及订阅方应用重启,当MQ的Broker或客户端重启、扩容或缩容时,会触发Rebalance,此时消费者可能会收到重复消息。

那么怎么解决消息重复消费的问题呢?就是对消息进行幂等性处理。

在MQ中,是无法保证每个消息只被投递一次的,因为网络抖动或者客户端宕机等其他因素,基本都会配置重试机制,所以要在消费者端的业务上做消费幂等处理,MQ的每条消息都有一个唯一的MessageId,这个参数在多次投递的过程中是不会改变的,业务上可以用这个MessageId加上业务的唯一标识来作为判断幂等的关键依据,例如订单ID。而这个业务标识可以使用Message的Key来进行传递。消费者获取到消息后先根据id去查询redis/db是否存在该消息,如果不存在,则正常消费,消费完后写入redis/db。如果存在,则证明消息被消费过,直接丢弃。

🍊 消息顺序

消息顺序的问题,如果发送端配置了重试机制,mq不会等之前那条消息完全发送成功,才去发送下一条消息,这样可能会出现发送了1,2,3条消息,但是第1条超时了,后面两条发送成功,再重试发送第1条消息,这时消息在broker端的顺序就是2,3,1了。RocketMQ消息有序要保证最终消费到的消息是有序的,需要从Producer、Broker、Consumer三个步骤都保证消息有序才行。

在发送者端:在默认情况下,消息发送者会采取Round Robin轮询方式把消息发送到不同的分区队列,而消费者消费的时候也从多个MessageQueue上拉取消息,这种情况下消息是不能保证顺序的。而只有当一组有序的消息发送到同一个MessageQueue上时,才能利用MessageQueue先进先出的特性保证这一组消息有序。

而Broker中一个队列内的消息是可以保证有序的。在消费者端:消费者会从多个消息队列上去拿消息。这时虽然每个消息队列上的消息是有序的,但是多个队列之间的消息仍然是乱序的。消费者端要保证消息有序,就需要按队列一个一个来取消息,即取完一个队列的消息后,再去取下一个队列的消息。而给consumer注入的MessageListenerOrderly对象,在RocketMQ内部就会通过锁队列的方式保证消息是一个一个队列来取的。

MessageListenerConcurrently这个消息监听器则不会锁队列,每次都是从多个Message中取一批数据,默认不超过32条。因此也无法保证消息有序。

RocketMQ 在默认情况下不保证顺序,要保证全局顺序,需要把 Topic 的读写队列数设置为 1,然后生产者和消费者的并发设置也是 1,不能使用多线程。所以这样的话高并发,高吞吐量的功能完全用不上。全局有序就是无论发的是不是同一个分区,我都可以按照你生产的顺序来消费。分区有序就只针对发到同一个分区的消息可以顺序消费。

kafka保证全链路消息顺序消费,需要从发送端开始,将所有有序消息发送到同一个分区,然后用一个消费者去消费,但是这种性能比较低,可以在消费者端接收到消息后将需要保证顺序消费的几条消费发到内存队列(可以搞多个),一个内存队列开启一个线程顺序处理消息。

RabbitMq没有属性设置消息的顺序性,不过我们可以通过拆分为多个queue,每个queue由一个consumer消费。或者一个queue对应一个consumer,然后这个consumer内部用内存队列做排队,然后分发给底层不同的worker来处理,保证消息的顺序性。

🍊 消息积压

线上有时因为发送方发送消息速度过快,或者消费方处理消息过慢,可能会导致broker积压大量未消费消息。消息数据格式变动或消费者程序有bug,导致消费者一直消费不成功,也可能导致broker积压大量未消费消息。解决方案可以修改消费端程序,让其将收到的消息快速转发到其他主题,可以设置很多分区,然后再启动多个消费者同时消费新主题的不同分区。可以将这些消费不成功的消息转发到其它队列里去,类似死信队列,后面再慢慢分析死信队列里的消息处理问题。

在RocketMQ官网中,还分析了一个特殊情况,如果RocketMQ原本是采用的普通方式搭建主从架构,而现在想要中途改为使用Dledger高可用集群,这时候如果不想历史消息丢失,就需要先将消息进行对齐,也就是要消费者把所有的消息都消费完,再来切换主从架构。因为Dledger集群会接管RocketMQ原有的CommitLog日志,所以切换主从架构时,如果有消息没有消费完,这些消息是存在旧的CommitLog中的,就无法再进行消费了。这个场景下也是需要尽快的处理掉积压的消息。

🍊 延迟队列

消息被发送以后,并不想让消费者立刻获取,而是等待特定的时间后,消费者才能获取这个消息进行消费。例如10分钟,内完成订单支付,支付完成后才会通知下游服务进行进一步的营销补偿。往MQ发一个延迟1分钟的消息,消费到这个消息后去检查订单的支付状态,如果订单已经支付,就往下游发送下单的通知。而如果没有支付,就再发一个延迟1分钟的消息。最终在第10个消息时把订单回收,就不用对全部的订单表进行扫描,而只需要每次处理一个单独的订单消息。这个就是延迟对列的应用场景。

rabbittmq,rocketmq都可以通过设置ttl来设置延迟时间,kafka则是可以在发送延时消息的时候,先把消息按照不同的延迟时间段发送到指定的队列中,比如topic_1s,topic_5s,topic_10s,topic_2h,然后通过定时器进行轮训消费这些topic,查看消息是否到期,如果到期就把这个消息发送到具体业务处理的topic中,队列中消息越靠前的到期时间越早,具体来说就是定时器在一次消费过程中,对消息的发送时间做判断,看下是否延迟到对应时间了,如果到了就转发,如果还没到这一次定时任务就可以提前结束了。

mq设置过期时间,就会有消息失效的情况,如果消息在队列里积压超过指定的过期时间,就会被mq给清理掉,这个时候数据就没了。解决方案也有手动写程序,将丢失的那批数据,一点点地查出来,然后重新插入到 mq 里面去。

🍊 消息队列高可用

对于RocketMQ来说可以使用Dledger主从架构来保证消息队列的高可用,这个在上面也有提到过。

然后在说说rabbitmq,它提供了一种叫镜像集群模式,在镜像集群模式下,你创建的 queue,无论元数据还是 queue 里的消息都会存在于多个实例上,就是说,每个 RabbitMQ 节点都有这个 queue 的一个完整镜像,包含 queue 的全部数据的意思。然后每次你写消息到 queue 的时候,都会自动把消息同步到多个实例的 queue 上。

RabbitMQ 有很好的管理控制台,可以在后台新增一个策略,这个策略是镜像集群模式的策略,指定的时候是可以要求数据同步到所有节点的,也可以要求同步到指定数量的节点,再次创建 queue 的时候,应用这个策略,就会自动将数据同步到其他的节点上去了。只不过消息需要同步到所有机器上,导致网络带宽压力和消耗很重。

最后再说说kafka,它是天然的分布式消息队列,在Kafka 0.8 以后,提供了副本机制,一个 topic要求指定partition数量,每个 partition的数据都会同步到其它机器上,形成自己的多个 replica 副本,所有 replica 会选举一个 leader 出来,其他 replica 就是 follower。写的时候,leader 会负责把数据同步到所有 follower 上去。如果某个 broker 宕机了,没事儿,那个 broker上面的 partition 在其他机器上都有副本的,如果这上面有某个 partition 的 leader,那么此时会从 follower 中重新选举一个新的 leader 出来。

🌟 ElasticSearch调优

🍊 CPU优化

对于提升服务能力来说,升级硬件设备配置是最快速有效的方法之一。在配置Elasticsearch服务器时,考虑CPU型号对性能的影响非常重要。因此,建议选用具有高性能CPU的服务器,例如IntelXeon系列或AMDOpteron系列。此外,为了充分利用多核处理器的优势,可以将Elasticsearch节点放置在不同的物理CPU上,以增加性能。

大多数Elasticsearch部署对CPU的要求不高,常见的集群使用2到8个核的机器。如果需要选择更快的CPU或更多的核数,则选择更多的核数更加优越。因为多个内核可以提供更多的并发,这比略微更快的时钟频率更加重要。

注意:CPU的时钟频率是指CPU每秒钟能够执行的时钟周期次数。它通常以赫兹(Hz)为单位表示,如1GHz(1000兆赫)或2.4GHz(2.4千兆赫)。它影响CPU的处理能力和速度,因为更高的时钟频率意味着CPU能够执行更多的指令,并在更短的时间内完成任务。但是,时钟频率并不是唯一决定CPU性能的因素,其他因素如架构、缓存等也会对其性能产生影响。

🍊 内存优化

为了让Elasticsearch具有良好的性能,需要为其分配足够的内存。对于确定所需内存大小,需要考虑预期的数据量和查询负载进行估算。一般情况下,建议将内存分配给JVM堆,以确保Elasticsearch可以尽可能多地利用内存执行操作。但是,对于内存大小的设置,需要遵循以下规则:

当机器内存小于64G时,应将JVM堆大小设置为物理内存的50%左右,其中一半留给Lucene,另一半留给Elasticsearch。

当机器内存大于64G时,若主要使用场景是全文检索,建议给Elasticsearch Heap分配4~32G的内存,其余内存留给操作系统,以供Lucene使用。

若主要使用场景是聚合或排序,并且大多数是数值、日期、地理点和非分析类型的字符数据,建议给Elasticsearch Heap分配4~32G的内存,其余内存留给操作系统,以提供更快的查询性能。

若使用场景是聚合或排序,并且都是基于分析类型的字符数据,需要更多的Heap大小,建议机器上运行多个Elasticsearch实例,每个实例保持不超过50%的Elasticsearch Heap设置(但不超过32G),50%以上留给Lucene。

此外,禁止使用swap,否则会导致严重的性能问题。为了保证Elasticsearch的性能,可以在elasticsearch.yml中设置bootstrap.memory_lock:true,以保持JVM锁定内存。

值得注意的是,由于Elasticsearch构建基于Lucene,Lucene的索引文件segments是存储在单个文件中的,对于操作系统来说,将索引文件保持在缓存中以提高访问速度是非常友好的。

🍊 网络优化

网络带宽是Elasticsearch性能的瓶颈之一,因为基于网络通信的查询和索引操作需要充分利用带宽。若带宽不足,则可能导致操作变慢或超时。在需要传输大量数据时,带宽限制也可能成为性能瓶颈,影响集群响应时间和高并发请求的处理。

除了网络带宽,网络延迟也是Elasticsearch性能瓶颈的重要因素之一。网络延迟可能导致请求和响应之间的延迟或超时,从而影响集群的响应能力。由于Elasticsearch是分布式的,需要在不同节点之间传输数据,因此网络延迟高会降低其查询和索引性能。

此外,网络故障也可能导致Elasticsearch节点之间通信中断,影响集群的可用性和数据一致性。

网络拓扑结构也会影响集群的性能,例如,如果两个节点之间的网络距离很远,则同步数据的时间可能会增加,并且可能会增加网络故障的风险。

安全设置(例如加密和身份验证)可能会增加网络负载并影响Elasticsearch性能。因此,可以通过优化网络安全设置来减少性能损失。

为了提高Elasticsearch集群网络的性能和稳定性,需要对以下几个方面进行优化:

(1)带宽限制:当Elasticsearch集群的数据量较大,节点之间的数据交换量较大,可能会出现带宽限制的情况。解决这个问题的方法是增加带宽,可以升级网络硬件设备或购买更高带宽的网络服务。同时,可以使用分片和副本来减少节点之间的数据交换量,从而减少带宽负载。

(2)网络延迟:网络延迟是指在节点之间传输数据时所需要的时间,如果网络延迟过高,会影响Elasticsearch集群的性能。优化网络设置可以降低网络延迟,可以使用更快的网络硬件设备,采用更优化的网络协议,优化Elasticsearch的配置参数等方式来降低网络延迟。使用高速网络设备和协议:如Infiniband或RDMA,可以提高网络传输速度,降低网络延迟。

(3)网络故障:网络故障可能会导致Elasticsearch集群无法正常工作,因此需要采取相应的措施来解决网络故障问题。其中一种方法是采用冗余节点或备份节点来解决网络故障问题。当一个节点无法正常工作时,备份节点可以快速接管工作。同时,可以使用网络监控工具来及时发现并解决网络故障问题。如:Wireshark用于网络故障排除和网络安全分析、Nagios检查主机、服务和网络设备的状态来进行网络监控、Zabbix监控网络设备的状态、性能、流量和带宽使用情况等。

(4)网络拓扑结构:优化网络拓扑结构可以提高Elasticsearch集群的性能。可以采用更合理的网络拓扑结构,例如将Elasticsearch节点放置在相同的数据中心或物理机架上,这可以减少数据同步时间和网络故障的风险。在同一物理机架内或同一数据中心内部,可以使用多个节点来提高集群的性能和容错能力。

(5)网络安全:网络安全是Elasticsearch运行过程中必须关注的问题。可以针对网络安全问题进行优化,采用更快的加密算法,对于不同的数据流采用不同的加密等级。同时,可以使用更快的身份认证算法,例如使用公钥认证等方式来提高Elasticsearch性能。采用分层的网络架构可以提高集群的安全性和性能。例如,在内部网络中使用防火墙和安全网关来保护Elasticsearch集群,并将公共接口放置在外部网络中,以提供对外服务。

(6)部署负载均衡器:通过在Elasticsearch节点之间部署负载均衡器,可以平衡查询负载,避免单个节点负载过重导致性能下降。同时,负载均衡器还可以提高Elasticsearch集群的可用性,当有节点故障时,负载均衡器可以自动将查询请求发送到其他节点,保证服务的连续性。

🍊 磁盘优化

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%。

相关文章
|
9天前
|
缓存 算法 Java
本文聚焦于Java内存管理与调优,介绍Java内存模型、内存泄漏检测与预防、高效字符串拼接、数据结构优化及垃圾回收机制
在现代软件开发中,性能优化至关重要。本文聚焦于Java内存管理与调优,介绍Java内存模型、内存泄漏检测与预防、高效字符串拼接、数据结构优化及垃圾回收机制。通过调整垃圾回收器参数、优化堆大小与布局、使用对象池和缓存技术,开发者可显著提升应用性能和稳定性。
30 6
|
9天前
|
SQL 安全 Java
安全问题已经成为软件开发中不可忽视的重要议题。对于使用Java语言开发的应用程序来说,安全性更是至关重要
在当今网络环境下,Java应用的安全性至关重要。本文深入探讨了Java安全编程的最佳实践,包括代码审查、输入验证、输出编码、访问控制和加密技术等,帮助开发者构建安全可靠的应用。通过掌握相关技术和工具,开发者可以有效防范安全威胁,确保应用的安全性。
23 4
|
11天前
|
缓存 监控 Java
如何运用JAVA开发API接口?
本文详细介绍了如何使用Java开发API接口,涵盖创建、实现、测试和部署接口的关键步骤。同时,讨论了接口的安全性设计和设计原则,帮助开发者构建高效、安全、易于维护的API接口。
35 4
|
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
|
17天前
|
存储 Java 开发者
成功优化!Java 基础 Docker 镜像从 674MB 缩减到 58MB 的经验分享
本文分享了如何通过 jlink 和 jdeps 工具将 Java 基础 Docker 镜像从 674MB 优化至 58MB 的经验。首先介绍了选择合适的基础镜像的重要性,然后详细讲解了使用 jlink 构建自定义 JRE 镜像的方法,并通过 jdeps 自动化模块依赖分析,最终实现了镜像的大幅缩减。此外,文章还提供了实用的 .dockerignore 文件技巧和选择安全、兼容的基础镜像的建议,帮助开发者提升镜像优化的效果。
|
17天前
|
监控 Java 数据库连接
在Java开发中,数据库连接管理是关键问题之一
在Java开发中,数据库连接管理是关键问题之一。本文介绍了连接池技术如何通过预创建和管理数据库连接,提高数据库操作的性能和稳定性,减少资源消耗,并简化连接管理。通过示例代码展示了HikariCP连接池的实际应用。
17 1
|
10天前
|
安全 Java 测试技术
Java开发必读,谈谈对Spring IOC与AOP的理解
Spring的IOC和AOP机制通过依赖注入和横切关注点的分离,大大提高了代码的模块化和可维护性。IOC使得对象的创建和管理变得灵活可控,降低了对象之间的耦合度;AOP则通过动态代理机制实现了横切关注点的集中管理,减少了重复代码。理解和掌握这两个核心概念,是高效使用Spring框架的关键。希望本文对你深入理解Spring的IOC和AOP有所帮助。
23 0
|
11天前
|
Java API Android开发
kotlin和java开发优缺点
kotlin和java开发优缺点
25 0
WK
|
16天前
|
开发框架 移动开发 Java
C++和Java哪个更适合开发移动应用
本文对比了C++和Java在移动应用开发中的优劣,从市场需求、学习难度、开发效率、跨平台性和应用领域等方面进行了详细分析。Java在Android开发中占据优势,而C++则适合对性能要求较高的场景。选择应根据具体需求和个人偏好综合考虑。
WK
31 0