ElasticSearch 性能调优
- 作者: 博学谷狂野架构师
- GitHub地址:GitHub地址 (有咱们精心筹备的130本电子书PDF)
概述
性能优化是个涉及面十分广的问题,不同的环境,不同的业务场景可能会存在不同的优化计划,本文只对一些相干的知识点做简略的总结,具体计划能够依据场景自行尝试。
配置文件调优
通过elasticsearch.yml
配置文件调优
内存锁定
容许 JVM 锁住内存,禁止操作系统替换进来
因为JVM产生swap替换会导致极大升高ES的性能,为了避免ES产生内存替换,咱们能够通过锁定内存来实现,这将极大进步查问性能,但同时可能造成OOM,须要对应做好资源监控,必要的时候进行干涉。
批改ES配置
批改ES的配置文件elasticsearch.yml,设置bootstrap.memory_lock为true
COPY#集群名称cluster.name: elastic#以后该节点的名称node.name: node-3#是不是有资格竞选主节点node.master: true#是否存储数据node.data: true#最大集群节点数node.max_local_storage_nodes: 3#给以后节点自定义属性(能够省略)#node.attr.rack: r1#数据存档地位path.data: /usr/share/elasticsearch/data#日志寄存地位path.logs: /usr/share/elasticsearch/log#是否开启时锁定内存(默认为是)#bootstrap.memory_lock: true#设置网关地址,我是被这个坑死了,这个地址我原先填写了本人的理论物理IP地址,#而后启动始终报有效的IP地址,无奈注入9300端口,这里只须要填写0.0.0.0network.host: 0.0.0.0#设置映射端口http.port: 9200#外部节点之间沟通端口transport.tcp.port: 9300#集群发现默认值为127.0.0.1:9300,如果要在其余主机上造成蕴含节点的群集,如果搭建集群则须要填写#es7.x 之后新增的配置,写入候选主节点的设施地址,在开启服务后能够被选为主节点,也就是说把所有的节点都写上discovery.seed_hosts: ["node-1","node-2","node-3"]#当你在搭建集群的时候,选出合格的节点集群,有些人说的太官网了,#其实就是,让你抉择比拟好的几个节点,在你节点启动时,在这些节点当选一个做领导者,#如果你不设置呢,elasticsearch就会本人选举,这里咱们把三个节点都写上cluster.initial_master_nodes: ["node-1","node-2","node-3"]#在群集齐全重新启动后阻止初始复原,直到启动N个节点#简略点说在集群启动后,至多复活多少个节点以上,那么这个服务才能够被应用,否则不能够被应用,gateway.recover_after_nodes: 2#删除索引是是否须要显示其名称,默认为显示#action.destructive_requires_name: true# 容许内存锁定,进步ES性能bootstrap.memory_lock: true
批改JVM配置
批改jvm.options,通常设置-Xms和-Xmx的的值为“物理内存大小的一半和32G的较小值”
这是因为,es内核应用lucene,lucene自身是独自占用内存的,并且占用的还不少,官网倡议设置es内存,大小为物理内存的一半,剩下的一半留给lucene
COPY-Xms2g-Xmx2g
敞开操作系统的swap
长期敞开
COPYsudo swapoff -a
永恒敞开
正文掉或删除所有swap相干的内容
COPYvi /etc/fstab
批改文件描述符
批改/etc/security/limits.conf,设置memlock为unlimited
COPYelk hard memlock unlimitedelk soft memlock unlimited
批改系统配置
设置虚拟内存
批改/etc/systemd/system.conf
,设置vm.max_map_count
为一个较大的值
COPYvm.max_map_count=10240000
批改文件下限
批改/etc/systemd/system.conf
,设置DefaultLimitNOFILE,DefaultLimitNPROC,DefaultLimitMEMLOCK为一个较大值,或者不限定
COPYDefaultLimitNOFILE=100000DefaultLimitNPROC=100000DefaultLimitMEMLOCK=infinity
重启ES
服务发现优化
Elasticsearch 默认被配置为应用单播发现,以避免节点无心中退出集群
组播发现应该永远不被应用在生产环境了,否则你失去的后果就是一个节点意外的退出到了你的生产环境,仅仅是因为他们收到了一个谬误的组播信号,ES是一个P2P类型的分布式系统,应用gossip协定,集群的任意申请都能够发送到集群的任一节点,而后es外部会找到须要转发的节点,并且与之进行通信,在es1.x的版本,es默认是开启组播,启动es之后,能够疾速将局域网内集群名称,默认端口的雷同实例退出到一个大的集群,后续再es2.x之后,都调整成了单播,防止平安问题和网络风暴;
单播discovery.zen.ping.unicast.hosts
,倡议写入集群内所有的节点及端口,如果新实例退出集群,新实例只须要写入以后集群的实例,即可主动退出到以后集群,之后再解决原实例的配置即可,新实例退出集群,不须要重启原有实例;
节点zen相干配置:discovery.zen.ping_timeout
:判断master选举过程中,发现其余node存活的超时设置,次要影响选举的耗时,参数仅在退出或者选举 master 主节点的时候才起作用discovery.zen.join_timeout
:节点确定退出到集群中,向主节点发送退出申请的超时工夫,默认为3sdiscovery.zen.minimum_master_nodes
:参加master选举的最小节点数,当集群可能被选为master的节点数量小于最小数量时,集群将无奈失常选举。
故障检测( fault detection )
故障检测状况
以下两种状况下回进行故障检测
COPY* 第一种是由master向集群的所有其余节点发动ping,验证节点是否处于活动状态* 第二种是:集群每个节点向master发动ping,判断master是否存活,是否须要发动选举
配置形式
故障检测须要配置以下设置应用
discovery.zen.fd.ping_interval
:节点被ping的频率,默认为1s。discovery.zen.fd.ping_timeout
期待ping响应的工夫,默认为 30s,运行的集群中,master 检测所有节点,以及节点检测 master 是否失常。discovery.zen.fd.ping_retries
ping失败/超时多少导致节点被视为失败,默认为3。
队列数量优化
不倡议自觉加大es的队列数量,要依据理论状况来进行调整
如果是偶发的因为数据突增,导致队列阻塞,加大队列size能够应用内存来缓存数据,如果是持续性的数据阻塞在队列,加大队列size除了加大内存占用,并不能无效进步数据写入速率,反而可能加大es宕机时候,在内存中可能失落的上数据量。
查看线程池状况
通过以下能够查看线程池的状况,哪些状况下,加大队列size呢?
COPYGET /_cat/thread_pool
察看api中返回的queue和rejected,如果的确存在队列回绝或者是继续的queue,能够酌情调整队列size。
内存应用
配置熔断限额
设置indices的内存熔断相干参数,依据理论状况进行调整,避免写入或查问压力过高导致OOM
indices.breaker.total.limit
: 50%,集群级别的断路器,默认为jvm堆的70%indices.breaker.request.limit
: 10%,单个request的断路器限度,默认为jvm堆的60%indices.breaker.fielddata.limit
: 10%,fielddata breaker限度,默认为jvm堆的60%。
配置缓存
依据理论状况调整查问占用cache,防止查问cache占用过多的jvm内存,参数为动态的,须要在每个数据节点配置
indices.queries.cache.size
: 5%,管制过滤器缓存的内存大小,默认为10%,承受百分比值,5%或者准确值,例如512mb。
创立分片优化
如果集群规模较大,能够阻止新建shard时扫描集群内全副shard的元数据,晋升shard调配速度
cluster.routing.allocation.disk.include_relocations: false
,默认为true
零碎层面调优
jdk版本
选用以后版本ES举荐应用的ES,或者应用ES自带的JDK
jdk内存配置
首先,-Xms和-Xmx设置为雷同的值,防止在运行过程中再进行内存调配,同时,如果零碎内存小于64G,倡议设置略小于机器内存的一半,残余留给零碎应用,同时,jvm heap倡议不要超过32G(不同jdk版本具体的值会略有不同),否则jvm会因为内存指针压缩导致内存节约
敞开替换分区
敞开替换分区,避免内存产生替换导致性能降落(局部状况下,宁死勿慢) swapoff -a
文件句柄
Lucene 应用了 大量的 文件,同时,Elasticsearch 在节点和 HTTP 客户端之间进行通信也应用了大量的套接字,所有这所有都须要足够的文件描述符,默认状况下,linux默认运行单个过程关上1024个文件句柄,这显然是不够的,故须要加大文件句柄数 ulimit -n 65536
mmap
Elasticsearch 对各种文件混合应用了 NioFs( 注:非阻塞文件系统)和 MMapFs ( 注:内存映射文件系统)。
请确保你配置的最大映射数量,以便有足够的虚拟内存可用于 mmapped 文件。这能够临时设置:sysctl -w vm.max_map_count=262144
或者你能够在 /etc/sysctl.conf
通过批改 vm.max_map_count
永恒设置它。
磁盘
如果你正在应用 SSDs,确保你的零碎 I/O 调度程序是配置正确的
当你向硬盘写数据,I/O 调度程序决定何时把数据理论发送到硬盘,大多数默认linux 发行版下的调度程序都叫做 cfq(齐全偏心队列),但它是为旋转介质优化的:机械硬盘的固有个性意味着它写入数据到基于物理布局的硬盘会更高效。
这对 SSD 来说是低效的,只管这里没有波及到机械硬盘,然而,deadline 或者 noop 应该被应用,deadline 调度程序基于写入等待时间进行优化, noop 只是一个简略的 FIFO 队列。
COPYecho noop > /sys/block/sd/queue/scheduler
磁盘挂载
COPYmount -o noatime,data=writeback,barrier=0,nobh /dev/sd* /esdata*
其中,noatime,禁止记录拜访工夫戳;data=writeback,不记录journal;barrier=0,因为敞开了journal,所以同步敞开barrier;nobh,敞开buffer_head,避免内核影响数据IO
磁盘其余注意事项
应用 RAID 0,条带化 RAID 会进步磁盘I/O,代价显然就是当一块硬盘故障时整个就故障了,不要应用镜像或者奇偶校验 RAID 因为正本曾经提供了这个性能。
另外,应用多块硬盘,并容许 Elasticsearch 通过多个 path.data 目录配置把数据条带化调配到它们下面,不要应用近程挂载的存储,比方 NFS 或者 SMB/CIFS。这个引入的提早对性能来说齐全是南辕北辙的。
应用形式调优
当elasticsearch自身的配置没有显著的问题之后,发现es应用还是十分慢,这个时候,就须要咱们去定位es自身的问题了,首先祭出定位问题的第一个命令:
Index(写)调优
正本数置0
如果是集群首次灌入数据,能够将正本数设置为0,写入结束再调整回去,这样正本分片只须要拷贝,节俭了索引过程
COPYPUT /my_temp_index/_settings{"number_of_replicas": 0}
主动生成doc ID
通过Elasticsearch写入流程能够看出,如果写入doc时如果内部指定了id,则Elasticsearch会先尝试读取原来doc的版本号,以判断是否须要更新,这会波及一次读取磁盘的操作,通过主动生成doc ID能够防止这个环节
正当设置mappings
将不须要建设索引的字段index属性设置为not_analyzed或no。
- 对字段不分词,或者不索引,能够缩小很多运算操作,升高CPU占用,尤其是binary类型,默认状况下占用CPU十分高,而这种类型进行分词通常没有什么意义。
- 缩小字段内容长度,如果原始数据的大段内容毋庸全副建设 索引,则能够尽量减少不必要的内容。
- 应用不同的分析器(analyzer),不同的分析器在索引过程中 运算复杂度也有较大的差别。
调整_source字段
_source` 字段用于存储 doc 原始数据,对于局部不须要存储的字段,能够通过 includes excludes过滤,或者将`_source`禁用,个别用于索引和数据拆散,这样能够升高 I/O 的压力,不过理论场景中大多不会禁用`_source
对analyzed的字段禁用norms
Norms用于在搜寻时计算doc的评分,如果不须要评分,则能够将其禁用
COPYtitle": {"type": "string","norms": {"enabled": false}
调整索引的刷新距离
该参数缺省是1s,强制ES每秒创立一个新segment,从而保障新写入的数据近实时的可见、可被搜寻到,比方该参数被调整为30s,升高了刷新的次数,把刷新操作耗费的系统资源释放出来给index操作应用
COPYPUT /my_index/_settings{ "index" : { "refresh_interval": "30s" }}
这种计划以就义可见性的形式,进步了index操作的性能。
批处理
批处理把多个index操作申请合并到一个batch中去解决,和mysql的jdbc的bacth有类似之处
比方每批1000个documents是一个性能比拟好的size,每批中多少document条数适合,受很多因素影响而不同,如单个document的大小等,ES官网倡议通过在单个node、单个shard做性能基准测试来确定这个参数的最优值
Document的路由解决
当对一批中的documents进行index操作时,该批index操作所需的线程的个数由要写入的目标shard的个数决定
有2批documents写入ES, 每批都须要写入4个shard,所以总共须要8个线程,如果能缩小shard的个数,那么消耗的线程个数也会缩小,例如下图,两批中每批的shard个数都只有2个,总共线程耗费个数4个,缩小一半。
默认的routing就是id,也能够在发送申请的时候,手动指定一个routing value,比如说put/index/doc/id?routing=user_id
值得注意的是线程数尽管升高了,然而单批的解决耗时可能减少了。和进步刷新距离办法相似,这有可能会缩短数据不见的工夫
Search(读)调优
在存储的Document条数超过10亿条后,咱们如何进行搜寻调优
数据分组
很多人拿ES用来存储日志,日志的索引治理形式个别基于日期的,基于天、周、月、年建索引,如下图,基于天建索引
当搜寻单天的数据,只须要查问一个索引的shards就能够,当须要查问多天的数据时,须要查问多个索引的shards,这种计划其实和数据库的分表、分库、分区查问计划相比,思路相似,小数据范畴查问而不是海底捞针。
开始的计划是建一个index,当数据量增大的时候,就扩容减少index的shard的个数,当shards增大时,要搜寻的shards个数也随之显著回升,基于数据分组的思路,能够基于client进行数据分组,每一个client只需依赖本人的index的数据shards进行搜寻,而不是所有的数据shards,大大提高了搜寻的性能,如下图:
应用Filter代替Query
在搜寻时候应用Query,须要为Document的相关度打分,应用Filter,没有打分环节解决,做的事件更少,而且filter实践上更快一些。
如果搜寻不须要打分,能够间接应用filter查问,如果局部搜寻须要打分,倡议应用’bool’查问,这种形式能够把打分的查问和不打分的查问组合在一起应用,如
COPYGET /_search{"query": {"bool": {"must": {"term": {"user": "kimchy"}},"filter": {"term": {"tag": "tech"}}}}}
ID字段定义为keyword
个别状况,如果ID字段不会被用作Range 类型搜寻字段,都能够定义成keyword类型,这是因为keyword会被优化,以便进行terms查问,Integers等数字类的mapping类型,会被优化来进行range类型搜寻,将integers改成keyword类型之后,搜寻性能大概能晋升30%
hot_threads
能够应用以下命令,抓取30s区间内的节点上占用资源的热线程,并通过排查占用资源最多的TOP线程来判断对应的资源耗费是否失常
COPYGET /_nodes/hot_threads&interval=30s
个别状况下,bulk,search类的线程占用资源都可能是业务造成的,然而如果是merge线程占用了大量的资源,就应该思考是不是创立index或者刷磁盘距离太小,批量写入size太小造成的。
pending_tasks
有一些工作只能由主节点去解决,比方创立一个新的索引或者在集群中挪动分片,因为一个集群中只能有一个主节点,所以只有这一master节点能够解决集群级别的元数据变动
在99.9999%的工夫里,这不会有什么问题,元数据变动的队列基本上放弃为零,在一些常见的集群里,元数据变动的次数比主节点能解决的还快,这会导致期待中的操作会累积成队列,这个时候能够通过pending_tasks api剖析以后什么操作阻塞了es的队列,比方,集群异样时,会有大量的shard在recovery,如果集群在大量创立新字段,会呈现大量的put_mappings的操作,所以失常状况下,须要禁用动静mapping。
COPYGET /_cluster/pending_tasks
字段存储
以后es次要有doc_values,fielddata,storefield三种类型,大部分状况下,并不需要三种类型都存储,可依据理论场景进行调整:
以后用得最多的就是doc_values,列存储,对于不须要进行分词的字段,都能够开启doc_values来进行存储(且只保留keyword字段),节约内存,当然,开启doc_values会对查问性能有肯定的影响,然而,这个性能损耗是比拟小的,而且是值得的;
fielddata构建和治理 100% 在内存中,常驻于 JVM 内存堆,所以可用于疾速查问,然而这也意味着它实质上是不可扩大的,有很多边缘状况下要提防,如果对于字段没有剖析需要,能够敞开fielddata;
storefield次要用于_source
字段,默认状况下,数据在写入es的时候,es会将doc数据存储为_source
字段,查问时能够通过_source
字段疾速获取doc的原始构造,如果没有update,reindex等需要,能够将_source字段disable;
_all
,ES在6.x以前的版本,默认将写入的字段拼接成一个大的字符串,并对该字段进行分词,用于反对整个doc的全文检索,在晓得doc字段名称的状况下,倡议敞开掉该字段,节约存储空间,也防止不带字段key的全文检索;
norms:搜寻时进行评分,日志场景个别不须要评分,倡议敞开;
事务日志
Elasticsearch 2.0之后为了保障不丢数据,每次 index、bulk、delete、update 实现的时候,肯定会触发同步刷新 translog 到磁盘上,才给申请返回 200 OK
异步刷新
采纳异步刷新,这个扭转在进步数据安全性的同时当然也升高了一点性能,如果你不在意这点可能性,还是心愿性能优先,能够在 index template 里设置如下参数
COPY{ "index.translog.durability": "async"}
其余参数
index.translog.sync_interval
对于一些大容量的偶然失落几秒数据问题也并不重大的集群,应用异步的 fsync 还是比拟无益的,比方,写入的数据被缓存到内存中,再每5秒执行一次 fsync ,默认为5s,小于的值100ms是不容许的。
index.translog.flush_threshold_size
translog存储尚未平安保留在Lucene中的所有操作,尽管这些操作可用于读取,但如果要敞开并且必须复原,则须要从新编制索引,此设置管制这些操作的最大总大小,以避免复原工夫过长,达到设置的最大size后,将产生刷新,生成新的Lucene提交点,默认为512mb。
refresh_interval
执行刷新操作的频率,这会使索引的最近更改对搜寻可见,默认为1s,能够设置-1为禁用刷新,对于写入速率要求较高的场景,能够适当的加大对应的时长,减小磁盘io和segment的生成;
禁止动静mapping
动静mapping的毛病
- 造成集群元数据始终变更,导致 不稳固;
- 可能造成数据类型与理论类型不统一;
- 对于一些异样字段或者是扫描类的字段,也会频繁的批改mapping,导致业务不可控。
映射配置
动静mapping配置的可选值及含意如下
- true:反对动静扩大,新增数据有新的字段属性时,主动增加对于的mapping,数据写入胜利
- false:不反对动静扩大,新增数据有新的字段属性时,间接疏忽,数据写入胜利
- strict:不反对动静扩大,新增数据有新的字段时,报错,数据写入失败
批量写入
批量申请显然会大大晋升写入速率,且这个速率是能够量化的,官网倡议每次批量的数据物理字节数5-15MB是一个比拟不错的终点,留神这里说的是物理字节数大小。
文档计数对批量大小来说不是一个好指标,比如说,如果你每次批量索引 1000 个文档,记住上面的事实:1000 个 1 KB 大小的文档加起来是 1 MB 大,1000 个 100 KB 大小的文档加起来是 100 MB 大。
这可是完完全全不一样的批量大小了,批量申请须要在协调节点上加载进内存,所以批量申请的物理大小比文档计数重要得多,从 5–15 MB 开始测试批量申请大小,迟缓减少这个数字,直到你看不到性能晋升为止。
而后开始减少你的批量写入的并发度(多线程等等方法),用iostat 、 top 和 ps 等工具监控你的节点,察看资源什么时候达到瓶颈。如果你开始收到 EsRejectedExecutionException ,你的集群没方法再持续了:至多有一种资源到瓶颈了,或者缩小并发数,或者提供更多的受限资源(比方从机械磁盘换成 SSD),或者增加更多节点。
索引和shard
es的索引,shard都会有对应的元数据,
因为es的元数据都是保留在master节点,且元数据的更新是要hold住集群向所有节点同步的,当es的新建字段或者新建索引的时候,都会要获取集群元数据,并对元数据进行变更及同步,此时会影响集群的响应,所以须要关注集群的index和shard数量,
应用倡议
倡议如下
- 应用shrink和rollover api,绝对生成适合的数据shard数;
- 依据数据量级及对应的性能需求,抉择创立index的名称,形如:按月生成索引:test-YYYYMM,按天生成索引:test-YYYYMMDD;
- 管制单个shard的size,失常状况下,日志场景,倡议单个shard不大于50GB,线上业务场景,倡议单个shard不超过20GB;
段合并
段合并的计算量宏大, 而且还要吃掉大量磁盘 I/O
合并在后盾定期操作,因为他们可能要很长时间能力实现,尤其是比拟大的段,这个通常来说都没问题,因为大规模段合并的概率是很小的。
如果发现merge占用了大量的资源,能够设置:index.merge.scheduler.max_thread_count: 1
特地是机械磁盘在并发 I/O 反对方面比拟差,所以咱们须要升高每个索引并发拜访磁盘的线程数,这个设置容许 max_thread_count + 2 个线程同时进行磁盘操作,也就是设置为 1 容许三个线程,对于 SSD,你能够疏忽这个设置,默认是 Math.min(3, Runtime.getRuntime().availableProcessors() / 2) ,对 SSD 来说运行的很好。
业务低峰期通过force_merge强制合并segment,升高segment的数量,减小内存耗费;敞开冷索引,业务须要的时候再进行开启,如果始终不应用的索引,能够定期删除,或者备份到hadoop集群;
主动生成_id
当写入端应用特定的id将数据写入es时,es会去查看对应的index下是否存在雷同的id,这个操作会随着文档数量的减少而耗费越来越大,所以如果业务上没有强需要,倡议应用es主动生成的id,放慢写入速率。
routing
对于数据量较大的业务查问场景,es侧个别会创立多个shard,并将shard调配到集群中的多个实例来摊派压力,失常状况下,一个查问会遍历查问所有的shard,而后将查问到的后果进行merge之后,再返回给查问端。
此时,写入的时候设置routing,能够防止每次查问都遍历全量shard,而是查问的时候也指定对应的routingkey,这种状况下,es会只去查问对应的shard,能够大幅度降低合并数据和调度全量shard的开销。
应用alias
生产提供服务的索引,切记应用别名提供服务,而不是间接裸露索引名称,防止后续因为业务变更或者索引数据须要reindex等状况造成业务中断。
防止宽表
在索引中定义太多字段是一种可能导致映射爆炸的状况,这可能导致内存不足谬误和难以复原的状况,这个问题可能比预期更常见,index.mapping.total_fields.limit
,默认值是1000
防止稠密索引
因为索引稠密之后,对应的相邻文档id的delta值会很大,lucene基于文档id做delta编码压缩导致压缩率升高,从而导致索引文件增大,同时,es的keyword,数组类型采纳doc_values构造,每个文档都会占用肯定的空间,即便字段是空值,所以稠密索引会造成磁盘size增大,导致查问和写入效率升高。
最初说一句(求关注,别白嫖我)
如果这篇文章对您有所帮忙,或者有所启发的话,求一键三连:点赞、转发、在看。您的反对是我保持写作最大的能源。
- 作者: 博学谷狂野架构师
- GitHub地址:GitHub地址 (有咱们精心筹备的130本电子书PDF)
本文由
传智教育博学谷狂野架构师
教研团队公布。如果本文对您有帮忙,欢送
关注
和点赞
;如果您有任何倡议也可留言评论
或私信
,您的反对是我保持创作的能源。转载请注明出处!