ElasticSearch使用进阶
1、优化 Elasticsearch
1.1、服务器资源选择
# ----------------------------------- Paths ------------------------------------ # # Path to directory where to store the data (separate multiple locations by comma): # #path.data: /path/to/data # # Path to log files: # #path.logs: /path/to/logs
-
使用 SSD(固态硬盘)
-
使用 RAID0。条带化 RAID 会提高磁盘 I/O,代价显然就是当一块硬盘故障时整个就故障了。不要使用镜像或者奇偶校验 RAID 因为副本已经提供了这个功能。
-
(RAID全称是独立磁盘冗余阵列(Redundant Array of Independent Disks),基本思想是把多个磁盘组合起来,组合一个磁盘阵列组,使得性能大幅提高。RAID可以充分发挥出多块硬盘的优势,可以提升硬盘速度,增大容量,提供容错功能够确保数据安全性。最常用的四种RAID为 RAID 0、RAID 1、RAID 5、RAID 10,RAID0称为条带化存储,将数据分段存储在各个磁盘中,读写均可以并行处理,因此读写速率为单个磁盘的N倍,没有冗余功能,任何一个磁盘的损坏就会导致的数据不可用。参考:https://www.cnblogs.com/Chary/p/17942784)
-
- 不要使用远程挂载的存储,比如 NFS 或者 SMB/CIFS,这个引入的延迟对性能来说完全是背道而驰的。
- 相同角色的节点应分配相同的CPU、内存和磁盘空间
- 虚拟机一个操作系统只能部署一个ES节点,物理机一个操作系统可部署多个ES节点
1.1.2、内存设置
- 每个ES节点最大堆内存设置不超过 31GB,JVM 堆内存设置最多为操作系统内存的 50%(且不包含每个ES节点给操作系统预留的2G内存)。
- 例如,虚拟机上部署了一个ES节点,JVM堆内存配置为 24G,则操作系统内存至少配置 50G 内存,计算公式:24/50%+2;假设物理机上部署了两个ES节点,每个节点JVM堆内存配置为 31G,则操作系统内存至少应有128G,计算公式:2*(31/50%+2)
- 确保 Xmx 和 Xms 的大小是相同的,其目的是为了能够在 Java 垃圾回收机制清理完堆区后不需要重新分隔计算堆区的大小而浪费资源,可以减轻伸缩堆大小带来的压力。
-Xms1g
-Xmx1g
- -Xms 31g
- -Xmx 31g
1.1.3、服务器CPU和ES内存配比
服务器CPU和ES实例 JVM 堆内存配比规则建议如下:
节点角色类型 | 应用场景 |
虚拟机 (CPU核 : JVM堆内存) |
物理机 (CPU核 : JVM堆内存) |
主节点 | 1:2 | 1:4 | |
协调节点 | 1:4 | 1:8 | |
热数据节点 | 高并发 | 1:2 | 1:4 |
其他 | 1:4 | 1:8 | |
其他类型数据节点 | 1:4 | 1:8 |
例1:高并发业务场景下,在虚拟机部署一个 ES 热数据节点,ES的JVM堆内存大小设置为31GB,则虚拟机至少配置 16 核CPU,计算公式:[31/21]([] 表示结果向上取整,下同)。
例2:高并发业务场景下,在物理机部署2个 ES 热数据节点,ES的JM堆内存大小设置为31GB,则物理机至少配置 16 核CPU,计算公式:2x[31/41]。
1.2、索引设计
1.2.1、合理设置索引分片数
分片和副本的设计为 ES 提供了支持分布式和故障转移的特性,但并不意味着分片和副本是可以无限分配的。而且注意因为索引的路由机制,在索引创建之后就无法再改变其分片的数量。
分片数不能设置太大,分片对资源是有消耗的,如下:
- 一个分片的底层即为一个 Lucene 索引,会消耗一定文件句柄、内存、以及 CPU 运转。
- 每一个搜索请求都需要命中索引中的每一个分片,如果每一个分片都处于不同的节点还好,但如果多个分片都需要在同一个节点上竞争使用相同的资源就有些糟糕了。
- 用于计算相关度的词项统计信息是基于分片的。如果有许多分片,每一个都只有很少的数据会导致很低的相关度。
一个业务索引具体需要分配多少分片可能需要架构师和技术人员对业务的增长有个预先的判断,横向扩展应当分阶段进行,为下一阶段准备好足够的资源。
一般来说,分片数设置遵循以下原则:
- 1GB 的 ES JVM堆内存最大可存放 20 个分片(指的全部,包括主副本)(例如 15GB的堆内存则最大可设置300个分片),单个节点的分片数不应超过 6000,集群总分片数不应超过 50000
-
单个主分片合理的大小为:10G~50G,随着教据量的增长,单个分片超过 50G时,需对索引进行拆分或扩容索引主分片个教。
-
单个索引所有主分片存储空间合计不超过3T,超过后需对索引进行拆分。
-
单个主分片包含的文档数过多,会降低查询性能,单个主分片文档数最大为4亿条,超过后需对索引进行拆分或扩容索引主分片个数。
- 为了平衡可靠性、查询性能、写入性能和成本需求,需要合理设置主分片的副本数。副本数越大,可靠性越高、查询性能越好,资源成本越高;副本数越小,可靠性越低、写人性能越高、资源成本越低。为避免集群出现单点故障,主分片的副本数至少设置为1
为提升性能,索引创建时要根据存量数据以及未来一年数据的预估增长量,规划合理的主分片个数,单个索引的主分片数量应不超过 128 个。计算方法如下:
说明:
- N:表示建议单个索引的主分片个数,N<=128,如果计算出来的N大于128,应对索引进行拆分
- S:表示单个索引为了一年的总数据量,S=存量数据+未来一年数据的预估增长量
- N=S/20,20 为建议一年后的索引单个分片的大小
同一索引的主分片和副本分片禁止部署在同一台服务器上。
1.2.2、映射和字段设置规范
- 应当根据业务需要合理规划索引的映射,不采用动态生成映射,采用模板创建索引,固定住索引字段,字段个数不超过 1000 个
- 字符串自动类型:text 和 keyword,精准查询和聚合技术的字段指定为 keyword 类型,分词检索字段指定为 text 类型
- 数值类型尽量选择短精度的,以节省存储空间
- 数值类型若无范围检索和计算需求,尽量使用 keyword 类型,可大大提高查询效率
1.3、推迟分片分配
对于节点瞬时中断的问题,默认情况,集群会等待一分钟来查看节点是否会重新加入,如果这个节点在此期间重新加入,重新加入的节点会保持其现有的分片数据,不会触发新的分片分配。这样就可以减少 ES 在自动再平衡可用分片时所带来的极大开销。
通过修改参数 delayed_timeout ,可以延长再均衡的时间,可以全局设置,也可以在索引级别进行修改。如下配置某个索引的推迟分配时间:
PUT /_all/_settings { "settings": { "index.unassigned.node_left.delayed_timeout": "5m" } }
1.4、带 routing(文档 _id)查询
Elasticsearch 是通过以下公式知道一个文档存放在哪个分片中的:(具体解释参考:https://www.cnblogs.com/wenxuehai/p/18141760#_label4_0)
shard = hash(routing) % number_of_primary_shards
当不带 routing 查询时,因为不知道要查询的数据具体在哪个分片上,所以会先计算出该数据存储在哪个节点上,然后再将请求分发到具体节点。但当你带 routing 查询时,即指定了文档存储的分片,通过这种方式,ES 只会在相关的分片上执行查询,从而能提高查询效率。
1.5、写入速度优化
ES 的默认配置,是综合了数据可靠性、写入速度、搜索实时性等因素,实际使用时,我们需要根据公司要求,进行偏向性的优化。针对于搜索性能要求不高,但是对写入要求较高的场景,我们需要尽可能的选择恰当写优化策略。综合来说,可以考虑以下几个方面来提升写索引的性能:
- 加大 Translog Flush ,目的是降低 Iops、Writeblock。
- 增加 Index Refresh 间隔,目的是减少 Segment Merge 的次数。
- 调整 Bulk 线程池和队列。
- 优化节点间的任务分布。
- 优化 Lucene 层的索引建立,目的是降低 CPU 及 IO。
1.5.1、批量数据提交
ES 提供了 Bulk API 支持批量操作,当我们有大量的写任务时,可以使用 Bulk 来进行批量写入。通用的策略如下:Bulk 默认设置批量提交的数据量不能超过 100M,数据条数一般是根据文档的大小和服务器性能而定的,我们可以将单次批处理的数据大小应从 5MB~15MB 逐渐增加,当性能没有提升时,把这个数据量作为最大值。
1.5.2、优化存储设备
1.5.3、合理使用合并
Lucene 以段的形式存储数据。当有新的数据写入索引时,Lucene 就会自动创建一个新的段。随着数据量的变化,段的数量会越来越多,消耗的多文件句柄数及 CPU 就越多,查询效率就会下降。由于 Lucene 段合并的计算量庞大,会消耗大量的 I/O,所以 ES 默认采用较保守的策略,在后台定期进行段合并。
1.5.4、减少 Refresh 的次数
Lucene 在新增数据时,采用了延迟写入的策略,默认情况下索引的 refresh_interval 为1 秒。Lucene 将待写入的数据先写到内存中,超过 1 秒(默认)时就会触发一次 Refresh,然后 Refresh 会把内存中的的数据刷新到操作系统的文件缓存系统中。
如果我们对搜索的实效性要求不高,可以将 Refresh 周期延长,例如 30 秒。这样还可以有效地减少段刷新次数,但这同时意味着需要消耗更多的 Heap 内存。
1.5.5、加大 Flush 设置
Flush 的主要目的是把文件缓存系统中的段持久化到硬盘,当 Translog 的数据量达到512MB 或者 30 分钟时,会触发一次 Flush。index.translog.flush_threshold_size 参数的默认值是 512MB,我们可以进行修改。增加参数值意味着文件缓存系统中可能需要存储更多的数据,所以我们需要为操作系统的文件缓存系统留下足够的空间。
1.5.6、减少副本的数量
ES 为了保证集群的可用性,提供了 Replicas(副本)支持,然而每个副本也会执行分析、索引及可能的合并过程,所以 Replicas 的数量会严重影响写索引的效率。
当写索引时,需要把写入的数据都同步到副本节点,副本节点越多,写索引的效率就越慢。
如果我们需要大批量进行写入操 作 ,可以先禁止 Replica 复制 , 设置index.number_of_replicas: 0 关闭副本。在写入完成后,Replica 修改回正常的状态。
2、ES配置说明
参数名 |
参数值示例 |
说明 |
cluster.name |
elasticsearch |
配置 ES 的集群名称,默认值是 ES,建议改成与所存数据相关的名称,ES 会自动发现在同一网段下的集群名称相同的节点 |
node.name |
node-1 |
集群中的节点名,在同一个集群中不能重复。节点的名称一旦设置,就不能再改变了。当然,也可以设 置 成 服 务 器 的 主 机 名 称 , 例 如node.name:${HOSTNAME}。 |
node.master |
true |
指定该节点是否有资格被选举成为 Master 节点,默认是 True。如果被设置为 True,则只是有资格成为Master 节点,具体能否成为 Master 节点,需要通过选举产生。 |
node.data |
true |
指定该节点是否存储索引数据,默认为 True。数据的增、删、改、查都是在 Data 节点完成的。 |
index.number_of_shards |
1 |
设置索引分片个数,默认是 1 片。也可以在创建索引时设置该值,具体设置为多大都值要根据数据量的大小来定,如果数据量不大,则设置成 1 时效率最高 |
index.number_of_replicas |
1 |
设置默认的索引副本个数,默认为 1 个。副本数越多,集群的可用性越好,但是写索引时需要同步的数据越多。 |
transport.tcp.compress |
true |
设置在节点间传输数据时是否压缩,默认为 False,不压缩 |
discovery.zen.minimum_master_nodes |
1 |
设置在选举 Master 节点时需要参与的最少的候选主节点数,默认为 1。如果使用默认值,则当网络不稳定时有可能会出现脑裂。 合理的数值为 (master_eligible_nodes/2)+1 ,其中master_eligible_nodes 表示集群中的候选主节点数 |
discovery.zen.ping.timeout |
3s |
设置在集群中自动发现其他节点时 Ping 连接的超时时间,默认为 3 秒。在较差的网络环境下需要设置得大一点,防止因误判该节点的存活状态而导致分片的转移 |
cluster.initial_master_nodes |
["node-1"]或["node - 1", "node - 2", "node - 3"] |
用于在集群初始化时指定哪些节点有资格成为初始的主节点。 作用: 配置方式: |
discovery.seed_hosts |
["192.168.118.130:9300","192.168.118.131:9300"] |
该配置用于指定种子节点,种子节点(seed nodes)是指在集群启动时,被用来帮助其他节点发现和加入集群的特定节点。 一般我们可以将那些运行稳定、资源充足且不太可能频繁下线的节点配置为种子节点,比如可以将初始主节点和有资格成为主节点的节点配置为种子节点。 |
3、索引阶段性能提升
- 使用批量请求并调整其大小:每次批量数据 5–15 MB 大是个不错的起始点。
- 段和合并:Elasticsearch 默认值是 20 MB/s,对机械磁盘应该是个不错的设置。如果你用的是 SSD,可以考虑提高到 100–200 MB/s。如果你在做批量导入,完全不在意搜索,你可以彻底关掉合并限流。另外还可以增加 index.translog.flush_threshold_size 设置,从默认的 512 MB 到更大一些的值,比如 1GB,这可以在一次清空触发的时候在事务日志里积累出更大的段。
- 如果你的搜索结果不需要近实时的准确度,考虑把每个索引的 index.refresh_interval 改到 30s。
- 如果你在做大批量导入,考虑通过设置 index.number_of_replicas: 0 关闭副本。