一. 集群配置
1.1 节点
- 相同角色的节点,避免使用差异较大的服务器配置,
- 避免使用“超大杯”服务器(SS:Super Server),比如128核CPU,1T的内存,2T的固态硬盘。这样可能会产生较大的资源浪费。
- 等量的配置,使用较少的物理机好于使用较多的虚拟机。比如一个一个五台4核16G的物理机,好于10甚至11台2核8G的虚拟机,这里不仅仅是虚拟机本身可能也会消耗一部分性能的问题,也涉及数据安全的问题。
- 避免在同一台服务器上部署多个节点,会增加集群管理的难度。
1.2内存
据业务量不同,内存的需求也不同,一般生产建议不要少于16G。ES是比较依赖内存的,并且对内存的消耗也很大,内存对ES的重要性甚至是高于CPU的,所以即使是数据量不大的业务,为了保证服务的稳定性,在满足业务需求的前提下,我们仍需考虑留有不少于20%的冗余性能。一般来说,按照百万级、千万级、亿级数据的索引,我们为每个节点分配的内存为16G/32G/64G就足够了,太大的内存,性价比就不是那么高了。
1.3 磁盘
对于ES来说,磁盘可能是最重要的了,因为数据都是存储在磁盘上的,当然这里说的磁盘指的是磁盘的性能。磁盘性能往往是硬件性能的瓶颈,木桶效应中的最短板。ES应用可能要面临不间断的大量的数据读取和写入。生产环境可以考虑把节点冷热分离,“热节点”使用SSD做存储,可以大幅提高系统性能;冷数据存储在机械硬盘中,降低成本。另外,关于磁盘阵列,可以使用raid 0。
1.4 CPU
CPU对计算机而言可谓是最重要的硬件,但对于ES来说,可能不是他最依赖的配置,因为提升CPU配置可能不会像提升磁盘或者内存配置带来的性能收益更直接、显著。当然也不是说CPU的性能就不重要,只不过是说,在硬件成本预算一定的前提下,应该把更多的预算花在磁盘以及内存上面。通常来说单节点cpu 4核起步,不同角色的节点对CPU的要求也不同。服务器的CPU不需要太高的单核性能,更多的核心数和线程数意味着更高的并发处理能力。现在PC的配置8核都已经普及了,更不用说服务器了。
1.5网络
ES是天生自带分布式属性的,并且ES的分布式系统是基于对等网络的,节点与节点之间的通信十分的频繁,延迟对于ES的用户体验是致命的,所以对于ES来说,低延迟的网络是非常有必要的。因此,使用扩地域的多个数据中心的方案是非常不可取的,ES可以容忍集群夸多个机房,可以有多个内网环境,支持跨AZ部署,但是不能接受多个机房跨地域构建集群,一旦发生了网络故障,集群可能直接GG,即使能够保证服务正常运行,维护这样(跨地域单个集群)的集群带来的额外成本可能远小于它带来的额外收益。
1.6 集群规划
没有最好的配置,只有最合适的配置。
- 在集群搭建之前,首先你要搞清楚,你ES cluster的使用目的是什么?主要应用于哪些场景,比如是用来存储事务日志,或者是站内搜索,或者是用于数据的聚合分析。针对不同的应用场景,应该指定不同的优化方案。
- 集群需要多少种配置(内存型/IO型/运算型),每种配置需要多少数量,通常需要和产品运营和运维测试商定,是业务量和服务器的承载能力而定,并留有一定的余量。
- 一个合理的ES集群配置应不少于5台服务器,避免脑裂时无法选举出新的Master节点的情况,另外可能还需要一些其他的单独的节点,比如ELK系统中的Kibana、Logstash等。
二. 集群搭建
参见:ELK
三. 其他配置
es的默认配置是一个非常合理的默认配置,绝大多数情况下是不需要修改的,如果不理解某项配置的含义,没有经过验证就贸然修改默认配置,可能造成严重的后果。比如max_result_window这个设置,默认值是1W,这个设置是分页数据每页最大返回的数据量,冒然修改为较大值会导致OOM。ES没有银弹,不可能通过修改某个配置从而大幅提升ES的性能,通常出厂配置里大部分设置已经是最优配置,只有少数和具体的业务相关的设置,事先无法给出最好的默认配置,这些可能是需要我们手动去设置的。关于配置文件,如果你做不到彻底明白配置的含义,不要随意修改。
四. JVM
jvm heap分配
7.6版本默认1GB,这个值太小,很容易导致OOM。Jvm heap大小不要超过物理内存的50%,最大也不要超过32GB(compressed oop),它可用于其内部缓存的内存就越多,但可供操作系统用于文件系统缓存的内存就越少,heap过大会导致GC时间过长。修改jvm heap有两种方式:
修改jvm堆内存
- 在ES进程启动的时候加上参数:ES_JAVA_OPTS=”-Xms512m -Xmx512m”,或在jvm.options中修改-Xms和-Xmx,注意最大值和最小值要相同,避免jvmheap在运行中resize,这是一个非常耗性能的过程。
- 修改环境变量:ES_HEAP_SIZE,环境变量的优先级高于jvm.options中的设置的数值。
通常来说JVM其他配置是不需要修改的
五. Threadpool
查看线程数
$$ curl -XGET 'http://127.0.0.1:9200/_nodes/stats?pretty' > es_stats.json
5.1 ES中不同线程池解释
**Generic**
:用于常规操作(例如,后台节点发现)。线程池类型为scaling。**Search**
:用于计数/搜索/搜索推荐(建议词)操作。线程池类型fixed_auto_queue_size的大小为:可获得的线程数:默认情况下是CPU的线程数 * 3 / 2 + 1
。默认情况下是CPU的线程数指的是比如4核8线程的CPU默认就是8,初始queue_size为 1000。当超过queue最大值,ES将拒绝请求。**search_throttled**
:用于对进行计数/搜索/建议/获取操作search_throttled indices。线程池类型fixed_auto_queue_size的大小为1,初始queue_size为100。**get**
:线程池类型fixed 的大小为可获得的线程数,默认情况下是cpu的线程数,queue_size为1000。**analyze**
:用于分析请求。线程池类型fixed的大小为1,队列大小为16。**write**
:对于单文档索引/删除/更新和批量请求。线程池类型fixed的大小为#可用线程数,queue_size为1W。该池的最大大小为1 + 可用线程数。**snapshot**
:用于快照/还原操作。线程池类型scaling的keepalive为5m,最大值为min(5, (CPU线程数)/2)。**warmer**
:用于段预热操作。线程池类型scaling的keepalive为5m,最大值为min(5, (CPU线程数)/2)。**refresh**
:用于刷新操作。线程池类型scaling的keepalive为5m,最大值为min(10, (CPU线程数)/2)。**listener**
:主要用于Java客户端,当侦听器线程设置为时执行动作 true。线程池类型scaling的默认最大值为min(10, (CPU线程数)/2)。**fetch_shard_started**
:用于列出分片状态。线程池类型为scalingkeep-alive5m,默认最大大小为2 *CPU线程数。**fetch_shard_store**
:用于列出碎片存储。线程池类型为scalingkeep-alive5m,默认最大大小为2 * # of available。**flush**
:对于flush,sync flush和translogfsync操作。线程池类型scaling的keepalive为5m,默认最大大小为min(5, (CPU线程数)/2)。**force_merge**
:用于强制合并操作。线程池类型fixed的大小为1,队列大小为无限制。**management**
:用于集群管理。默认最大大小为5。
5.2 线程池类型
在Java标准的线程池里面,主要有3个重要的参数会影响线程数的变化:corePoolSize(核心线程数)
、maximumPoolSize(最大线程数)
和keepAliveTime(线程存活时间)
。
**fixed**
:
fixed是线程数固定,任务队列大小可调(海底捞的水杯)
开始提交任务时,会不断创建新的线程,直至线程数达到配置的线程数,在这之后,再创建任务,如果任务队列没满,任务会进入任务队列;如果队列满了,再提交任务会被阻塞。线程数一旦达到阈值就不会减少,即使任务都执行完毕,线程是空闲的,也不会销毁
fixed配置:
thread_pool:
write:
size: 30
queue_size: 1000
**fixed_auto_queue_size**
fixed_auto_queue_size是线程数固定,但是不允许用无界任务队列
fixed_auto_queue_size配置:
thread_pool:
search:
size: 30
queue_size: 500
min_queue_size: 10
max_queue_size: 1000
**scaling**
:(类似于发动机机油)当线程池个数低于corePoolSize时,每次提交新任务都会触发新的线程创建。当线程数量高于corePoolSize时,ES会首先检查创建的线程数是否达到了maximumPoolSize,如果没有,继续创建,否则才会进入任务队列。如果线程空闲时间超过keepAliveTime且线程数大于corePoolSize,线程会被销毁。
scaling设置:
thread_pool:
warmer:
core: 1
max: 8
keep_alive: 2m
5.4 线程数设置
- 一般来说,ES默认会自动检测处理器的核心线程数量,并且基于处理器的线程数自动设置ES的线程池,但是在以下几种情况需要我们手动设置ES的线程池:
- 如果需要在同一主机上运行多个ES实例,则应修改为node.processors做对应的修改,比如,一台 8核16线程的服务器应该把ES的两个实例分别设置node.processors= 8。不过,这是骨灰级玩家操作,因为你修改了线程数,影响范围不仅仅是线程池这么简单,还涉及很多其他因素,比如:Jvm processors。所以之前也曾经提到,尽量不要在同一台服务器上部署多个节点,避免给自己挖坑。
- 不排除ES会获取到错误的处理器的核心线程数量,所以我们可以手动的设置node.processors来避免这些问题。
- 设置方法
node.processors: 8
六. Bootstrap Checks(引导检查)
开发模式和生产模式:
如果Elasticsearch节点无法通过非环回地址与另一台机器形成集群,则认为该节点处于开发模式;如果它可以通过非环回地址加入集群,则该节点处于生产模式。
单节点发现:
设置
discovery.type = single-node
,当前节点会选举自己为master节点并且不参与任何集群,用于开发模式,此时ES启动会绕过引导检查。
引导检查:
在ES启动时,自身有非常严格的检查机制,起检查的项包含非常多的内容,其目的是为了确保ES所有设置的正确性和合理性,从而保证ES服务运行时的稳定和最佳性能。如果我们做出了不合理的ES配置,ES宁愿拒绝服务,也不会为我们启动一个有问题的服务节点,这也是为了我们好,避免了服务启动后给我们带来不可预料的重大问题,从而导致严重后果。在开发模式下,一些不重要的问题可能只是以警告信息输出,但是在生产模式下,引导检查时会强制把这些警告升级为错误,除非我们解决了这些错误,不然ES无法启动。
引导包含的项:
- 堆大小检查:最大值最小值要保证相等,并且最好是在4G-32G之间。
- 文件描述符检查:
- 内存锁检查:bootstrap.memory_lock: true
- 最大线程数检查:
Elasticsearch需要具有创建大量线程的能力。检查线程的最大数量可确保Elasticsearch进程有权在正常使用下创建足够的线程。仅在Linux上强制执行此检查。如果您使用的是Linux,要通过最大线程数检查,必须将系统配置为允许Elasticsearch进程创建至少4096个线程。这可以通过/etc/security/limits.conf 使用nproc设置来完成(请注意,您可能还必须增加root用户的限制)。
- 最大文件大小检查:
作为单个分片的组成部分的段文件以及作为跨记录的组成部分的跨记录世代可能会变得很大(超过数GB)。在Elasticsearch流程可以创建的文件的最大大小受到限制的系统上,这可能导致写入失败。因此,这里最安全的选择是最大文件大小不受限制,这就是最大文件大小引导检查所强制执行的内容。要通过最大文件检查,您必须配置系统以使Elasticsearch进程能够写入无限大小的文件。这可以通过 /etc/security/limits.conf使用fsize设置来完成unlimited(请注意,您可能还必须增加root用户的限制)。
- 虚拟内存最大大小检查:
Elasticsearch和Lucene mmap很有用,可以将索引的某些部分映射到Elasticsearch地址空间中。这将某些索引数据保留在JVM堆之外,但保留在内存中,以实现快速访问。为了使此方法有效,Elasticsearch应该具有无限的地址空间。虚拟内存的最大大小检查要求Elasticsearch进程具有无限的地址空间,并且仅在Linux上强制执行。要通过最大大小的虚拟内存检查,您必须将系统配置为允许Elasticsearch进程具有无限的地址空间。可以通过添加
- 最大映射技术检查:
最大映射计数检查可检查内核是否允许进程至少具有262,144个内存映射区域,并且仅在Linux上强制执行。要通过最大地图计数检查,您必须将vm.max_map_countvia 配置sysctl为至少为262144。
- 客户端JVM检查:
OpenJDK派生的JVM提供了两种不同的JVM:客户端JVM和服务器JVM。这些JVM使用不同的编译器从Java字节码生成可执行的机器代码。调整客户端JVM的启动时间和内存占用量,同时调整服务器JVM的性能以最大化性能。两个VM之间的性能差异可能很大。客户端JVM检查可确保Elasticsearch不在客户端JVM内运行。要通过客户端JVM检查,您必须使用服务器VM启动Elasticsearch。在现代系统和操作系统上,服务器VM是默认设置。
- 使用串行收集器检查:
针对不同工作负载的OpenJDK衍生的JVM有各种垃圾收集器。特别是,串行收集器最适合于单逻辑CPU机器或非常小的堆,这两种都不适合运行Elasticsearch。将串行收集器与Elasticsearch一起使用可能会破坏性能。串行收集器检查可确保未将Elasticsearch配置为与串行收集器一起运行。要通过串行收集器检查,您不得使用串行收集器启动Elasticsearch(无论是从您使用的JVM的默认值开始,还是用明确指定了它-XX:+UseSerialGC)。请注意,Elasticsearch随附的默认JVM配置将Elasticsearch配置为使用CMS收集器。
- 系统调用过滤器检查:
Elasticsearch根据操作系统(例如Linux上的seccomp)安装各种样式的系统调用过滤器。安装这些系统调用过滤器是为了防止执行与分叉相关的系统调用的能力,以作为对Elasticsearch上任意代码执行攻击的防御机制。系统调用筛选器检查可确保如果启用了系统调用筛选器,则说明它们已成功安装。要通过系统调用过滤器检查,您必须修复系统上阻止安装系统调用过滤器的任何配置错误(检查日志),或者将设置bootstrap.system_call_filter为false来禁用系统调用过滤器。
- OnError和OnOutOfMemoryError检查:
如果JVM遇到致命错误(OnError)或OutOfMemoryError (OnOutOfMemoryError), JVM选项OnError和OnOutOfMemoryError允许执行任意命令。但是,在默认情况下,Elasticsearch系统调用过滤器(seccomp)是启用的,这些过滤器防止分叉。因此,使用OnError或OnOutOfMemoryError和系统调用过滤器是不兼容的。如果使用了这两个JVM选项,并且启用了系统调用过滤器,OnError和OnOutOfMemoryError检查将阻止Elasticsearch启动。这张支票总是强制执行的。要通过此检查,不要启用OnError或OnOutOfMemoryError;相反,升级到Java 8u92并使用JVM标志ExitOnOutOfMemoryError。虽然它没有OnError和OnOutOfMemoryError的全部功能,但是在启用seccomp的情况下,将不支持任意分支。
- Early-access check:
- G1GC检查:
ES默认的GC收集器是CMS,若使用G1GC,可能会导致索引损坏,原因是JDK 8附带的HotSpot早期版本存在一些问题,受影响的版本为JDK 8u40之前的版本。
- 所有权限检查:
所有权限检查可确保引导过程中使用的安全策略不会将权限授予java.security.AllPermissionElasticsearch。以授予的所有权限运行等同于禁用安全管理器。
- 集群发现检查:
默认情况下,Elasticsearch首次启动时,它将尝试发现在同一主机上运行的其他节点。如果在几秒钟内找不到任何当选的主节点,则Elasticsearch将形成一个包含所有其他已发现节点的集群。发现配置:
- `discovery.seed_hosts`
- `discovery.seed_providers`
- `cluster.initial_master_nodes`
七. 数据备份与恢复
数据备份恢复的方式有很多种,这里只列举4种:
- logstash方式:把日志或者数据存储成json格式,使用logstash读取。
- snapshot+hdfs方式
- CCR方式
- alias + scroll + _bulk 重建索引的方式
7.1 snapshot+hdfs方式做灾备+恢复
7.1.1 hadoop下载安装配置
假设hadoop的下载目录为:/root/soft/hadoop/hadoop-3.3.0.tar.gz
解压目录为:/root/soft/hadoop/hadoop-3.3.0
下载
$ mkdir /root/soft/hadoop
$ cd /root/soft/hadoop/
$ wget https://mirrors.bfsu.edu.cn/apache/hadoop/common/hadoop-3.3.0/hadoop-3.3.0.tar.gz
解压
$ tar zxvf hadoop-3.3.0.tar.gz
配置
core-site.xml
配置
$ cd /hadoop-3.3.0/etc/hadoop/
$ vim core-site.xml
<property>
<name>fs.defaultFS</name>
<value>hdfs://172.16.10.184:9000</value>
</property>
hdfs-site.xml
配置
<property>
<name>dfs.namenode.name.dir</name>
<value>/usr/local/data/namenode</value>
</property>
<property>
<name>dfs.datanode.data.dir</name>
<value>/usr/local/data/datanode</value>
</property>
yarn-site.xml
配置
<property>
<name>yarn.resourcemanager.hostname</name>
<value>172.16.10.184</value>
</property>
mapred-site.xml
配置
<property>
<name>mapreduce.framework.name</name>
<value>yarn</value>
</property>
设置环境变量
$ echo -e '\nexport HADOOP_HOME=/root/soft/hadoop/hadoop-3.3.0' >> /etc/profile
$ echo -e '\nexport PATH=$PATH:$HADOOP_HOME/bin:$HADOOP_HOME/sbin' >> /etc/profile
$ source /etc/profile
$ echo $PATH
启动/停止HDFS
#启动
$ $HADOOP_HOME/sbin/start-dfs.sh
#停止
$ $HADOOP_HOME/sbin/stop-dfs.sh
访问验证:
7.1.2 elasticsearch集群安装hdfs插件
- 停止集群自动重新分片
PUT _cluster/settings
{
"transient" : {
"cluster.routing.allocation.enable" : "none"
}
}
集群内所有节点安装
repository-hdfs
插件$ cd {elasticsearch安装路径}
$ ./bin/elasticsearch-plugin install repository-hdfs
重新所有节点
打开集群自动重新分片
PUT _cluster/settings
{
"transient" : {
"cluster.routing.allocation.enable" : "all"
}
}
7.1.3 创建备份
创建备份仓库
PUT _snapshot/{备份仓库名称}?pretty
{
"type": "hdfs",
"settings": {
"uri": "hdfs://172.16.10.182:9000/", //hdfs的集群地址
"path": "elasticsearch/respositories/hdfs_repository",
"conf.dfs.client.read.shortcircuit": "true",
"max_snapshot_bytes_per_sec" : "20mb", //
"max_restore_bytes_per_sec" : "20mb" //网络宽带上限
}
}
**type**
的可选枚举值为:
**fs**
: 共享文件系统,将快照文件存放于文件系统中- url: 指定文件系统的URL路径,支持协议:http,https,ftp,file,jar
- s3: AWS S3对象存储,快照存放于S3中,以插件形式支持
- hdfs: 快照存放于hdfs中,以插件形式支持
- cos: 快照存放于腾讯云COS对象存储中,以插件形式支持
检查备份仓库是否创建出成功
POST /_snapshot/{备份仓库名称}/_verify
#结果
全量索引备份
PUT _snapshot/{备份仓库名称}/{备份的名字}
{
"wait_for_completion":true //设置为同步执行,一直等待执行结果。
}
指定索引备份
PUT _snapshot/{备份仓库名称}/{备份的名字}
{
"indices": "{索引名字}", //索引名字
"ignore_unavailable": true, //忽略不存在的索引
"include_global_state": false, //忽略cluster的全局state
"partial": true //允许部分备份,避免备份失败。比如主分片不可用,若未设置此项,则可能会导致备份失败
}
查看备份的进度
GET /_snapshot/{备份仓库名称}/{备份的名字}?pretty
7.1.4 恢复备份数据
POST _snapshot/{备份仓库名称}/{备份的名字}/_restore?pretty
{
"indices": "{索引名字}", //备份时指定的索引名字
"ignore_unavailable": true, //忽略不存在的索引
"include_global_state": true, //忽略cluster的全局state
"rename_pattern": "*", //重命名索引时的正则
"rename_replacement": "$1"//重命名索引时,符合正则部分要替换的字符
}
查看索引恢复的进度
GET {索引名字}/_recovery?pretty
取消恢复
DELETE {索引名字}/_recovery?pretty
7.2 CCR方式做灾备
CCR跨集群复制 (Cross Cluster Replication) 功能支持将特定索引从一个 ElasticSearch 集群复制到一个或多个 ElasticSearch 集群。
除了跨数据中心复制之外,CCR 还有许多其他用例,包括数据本地化(将数据复制到距离用户/应用程序服务器更近的位置,例如,将产品目录复制到全球 20 个不同的数据中心),或者将数据从 Elasticsearch 集群复制到中央报告集群(例如,全球 1000 家银行分行都向其本地 Elasticsearch 集群写入数据,并复制回总部的集群用于报告)。
跨集群复制的条件:
- 需要远程集群
- 新集群版本必须高于旧集群,并且需要满足兼容性条件(附件)
跨集群复制的特点:
- 复制是基于索引的
- 复制的索引是只读的(flower),没有选举leader一说。
操作
- 在主动复制的集群的kibana中打开CCR
- 打开了CCR之后会在左侧列表内多处两个菜单
- 我们选择连接远程集群
- 配置集群复制
7.3 alias + scroll + _bulk 重建索引
为什么要重建索引?
- mapping需要修改的时候
- 某些对数据实时性要求不高场景,可以定时更新,这要可以提高检索性能
- 数据迁移的时候
迁移方案:alias + scroll + _bulk
迁移完成后,alias指向索引B
给person
索引起个别名叫”person_template
“
POST _aliases
{
"actions": [
{
"add": {
"index": "person",
"alias": "person_template"
}
}
]
}
然后开始把person
的数据迁移至person1
迁移完成后将person
指向的”person_template
“删掉,给新增索引person1
指向”person_template
“
POST _aliases
{
"actions": [
{
"remove": {
"index": "person",
"alias": "person_template"
}
},
{
"add": {
"index": "person1",
"alias": "person_template"
}
}
]
}
八. 集群调优
8.1 慢日志
分片级的,针对于data_node。
现象:
- 连接大量被拒绝
- CPU居高不下
- 查询超时
原因:
- 软件配置不合理
- 硬件资源不够
8.2 配置
慢查询配置:
配置整个节点
index.search.slowlog.threshold.query.warn: 10
sindex.search.slowlog.threshold.query.info: 5s
index.search.slowlog.threshold.query.debug: 2s
index.search.slowlog.threshold.query.trace: 500ms
index.search.slowlog.threshold.fetch.warn: 1s
index.search.slowlog.threshold.fetch.info: 800ms
index.search.slowlog.threshold.fetch.debug: 500ms
index.search.slowlog.threshold.fetch.trace: 200ms
index.search.slowlog.level: info
配置某个索引
PUT /index/_settings
{
"index.search.slowlog.threshold.query.warn":"10s",
"index.search.slowlog.threshold.query.info":"5s",
"index.search.slowlog.threshold.query.debug":"2s",
"index.search.slowlog.threshold.query.trace":"500ms",
"index.search.slowlog.threshold.fetch.warn":"1s",
"index.search.slowlog.threshold.fetch.info":"800ms",
"index.search.slowlog.threshold.fetch.debug":"500ms",
"index.search.slowlog.threshold.fetch.trace":"200ms",
"index.search.slowlog.level":"info"
}
慢写入配置:
index.indexing.slowlog.threshold.index.warn: 10s
index.indexing.slowlog.threshold.index.info: 5s
index.indexing.slowlog.threshold.index.debug: 2s
index.indexing.slowlog.threshold.index.trace: 500ms
index.indexing.slowlog.level: info
index.indexing.slowlog.source: 1000
8.3 优化建议
搜索优化:
- 减小
page size
: 搜索结果不要返回过大的结果集,每页搜索数量控制在1000以内。 - 避免deeping: 如果需要深度分页,建议使用scroll search,如果一定要使用深查询,
考虑使用search after,下面是讲解:
https://www.elastic.co/guide/en/elasticsearch/reference/7.7/search-request-body.html#request-body-search-search-after
- 聚合查询避免过多分桶:对于桶数过多的聚合,应将数据切片:比如按时间、按地理位置等分词请求。
- 能用
filter
的不要使用query
. - 避免过多
agg
嵌套。 - 尽量避免使用
fuzzy
、前缀和正则,可以使用suggest做模糊搜索。
写入优化:
- 用bulk批量写入
- 使用多线程将数据写入ES
- 大量数据一次性写入:
如果我们需要一次性写入大量数据,可以先禁止refresh和replia复制,将index.refresh_interval 设置为-1T将index.number_of_replicas 设置为0。但是ES不需要创建segment file文件了,replica数据也不需要互相同步了。此时写入的速度会非常快,我们可以等数据写入完成之后,再把他们设置回来
- 增加buffer缓冲区大小:
indices.memory.index_buffer_size
,一般来说indices.memory.index_buffer_size
/total_shards
< jvm heap/10 ,增大 buffer 的大小可以大大提高写入速度。
- 把更多内存留给OS Cache:
可能有人认为应该把Jvm heap调大会增加性能,这都是想当然的操作,我们不仅不应该增大Jvm heap,反而应该把更多内存留给OS Cache,因为Lucene底层的读写都是基于OS Cache的,需要大量的内存。
数据结构:
- 尽量使用手工mapping,尽量手动指定string字段类型。
- 避免稀疏数据结构: 会产生磁盘浪费,同时降低读写性能。
- 避免将没有任何关联性的数据写入同一个索引.
- 检索和聚合解耦: 避免把大量用于检索的数据和用于聚合的数据放在同一个index.
- 仅用于检索的字段关闭doc value,仅用于聚合的字段关闭index。
- 避免使用filelddata.
- 禁用norms: 对于不需要参与评分的字段,可设置false,常见的比如filter和agg字段。
- 禁用index_options
- 禁用_all field
- source和store分离(高级玩法,可节省带宽)。
- 使用最小的数据类型: 能用short的不要用long,这一点在ES里更为重要。
- scripting查询参数化: script脚本会触发编译行为,并且会产生缓存,使用不当会造成查询巨慢,还会造成OOM.
- ld: 对于某些id字段,不需要我们做全文检索,使用keyword比数字类型性能更好
- 尽量避免使用nested和parent/child:能在mapping 阶段搞定的,尽量在mapping阶段搞定。
- 避免自定义id。
- 业务框架解耦:避免业务对ES过多的功能依赖,ES本身是一个搜索、存储、分析引擎,尽量把功能依赖放在这三个点上,有些功能虽然ES提供了,但是我们能用其他方案取代就用其他方案,这样留出更多资源让ES专注于搜索和聚合。即便ES提供的解决方案可能更好,但是代价却是占用了主要功能的资源。比如智能推荐、模糊查询等。
集群:
- 冷热分离: 高频数据使用性能更好的硬件,
HDD(机械硬盘)
<SSD(固态硬盘)
<RAM(内存)
- 单一职责: 关闭master节点的data-node设置(如果是开启状态的话),数据节点和Master节点一定要分开,集群规模越大,这样做的意义也就越大。
- 增加协调节点的数量:稳定的Master节点对于群集健康非常重要!理论上讲,应该尽可能的减轻 Master节点的压力,分片数量越多,Master节点维护管理shard的任务越重,并且节点可能就要承担更多的数据转发任务,可增加“仅协调”节点来缓解Master节点和Data节点的压力,但是在集群中添加过多的仅协调节点会增加整个集群的负担,因为选择的主节点必须等待每个节点的集群状态更新确认。
- 高可用性(HA)群集至少需要三个主节点,其中至少两个不是仅投票节点。即使其中一个节点发生故障,这样的群集也将能够选举一个主节点。生产环境最好设置3台仅Master候选节点(node.master = true node.data = true)
- 以上的投票节点可用,群集仍可以正常工作。这意味着,如果存在三个或四个主节点以上的投票节点可用,群集仍可以正常工作。这意味着,如果存在三个或四个主节点则群集可以容忍其中一个节点不可用。如果有两个或更少的主机资格节点,则它们必须都保持可用
- 禁用swapping: swapping会大大降低性能。
索引:
- 控制分片数量:每台节点的Shard 数量越少,每个shard分配的CPU、内存和IO资公源越多,单个Shard 的性能越好,当一台机器一个Shard时,单个Shard性能最好。分片越少,分页查询越快。如果相同资源分配相同的前提下,shard数量越少,单个shard的体积越大,查询性能越低,速度越慢,这个取舍应根据实际集群状况和结合应用场景等因素综合考虑, 单个分片最大不超过20G-40G(不是绝对的)。
- 搜索日志: 统计高频词汇,对于命中率极高的关键词添加应用层缓存。
- 冷热热分离: 把冷热数据放在不同索引。
硬件:
- 全能型: 数据节点处理与数据相关的操作,例如CRUD,搜索和聚合。这些操作是I/O内存和CPU密集型的,所以他们需要更高配置的服务器以及更高的带宽,并且集群的性能冗余非常重要。
- 带宽: 由于仅投票节不参与Master 竞选,所以和真正的 Master 节点相比,它需要的内存和CPU较少。但是,所有候选节点以及仅投票节点都可能是数据节点,所以他们都需要快速稳定低延迟的网络。