ElasticSearch读写原理及性能调优
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
ElasticSearch读写原理及性能调优
ES写⼊/查询原理
1. Elasticsearch写⼊数据流程
1. 客户端随机选择⼀个ES集群中的节点,发送POST/PUT请求,被选择的节点为协调节点(coordinating node)
2. 协调节点查询集群状态信息并计算路由,将请求发送到真正处理请求的节点(primary shard所在的节点)
3. 包含primary shard的节点处理写⼊请求,并将数据同步到包含replica shard的节点
4. coordinating node收到包含primary shard的节点的相应信息,将最终结果返回给Client端
2. Elasticsearch读取数据流程
1. 客户端随机选择⼀个ES集群中的节点,发送GET请求,被选择的节点为协调节点(coordinating node)
2. 协调节点查询集群状态信息并使⽤round-robin随机轮询算法计算出去此次请求的节点,将请求发送到真正处理请求的节点(主分⽚节
点和副本节点随机分配)
3. 处理读请求的节点将数据返回给协调节点
4. 协调节点会把⽂档信息返回给Client
3. Elasticsearch检索数据流程
1. 客户端发送请求到⼀个协调节点
2. 协调节点将搜索请求转发到所有的shard对应的primary shard或replica shard也可以
3. 每个shard将⾃⼰的搜索结果(其实就是⼀些doc id),返回给协调节点,由协调节点进⾏数据的合并、排序、分页等操作,产出最终
结果
4. 接着由协调节点,根据doc id去各个节点上拉取实际的document数据,最终返回给客户端
这⾥需要注意,分页查询,当from特别⼤时会造成⼤量⽆⽤数据返回到协调节点,谨慎使⽤。
4. 数据索引底层原理
1. 先写⼊buffer,在buffer⾥的时候数据是搜索不到的;同时将数据写⼊translog⽇志⽂件。
2. 如果buffer到达阈值,或者到⼀定时间,ES会将buffer中的数据refresh到⼀个新的segment file中,但是此时数据不是直接进⼊segment
file的磁盘⽂件的,⽽是先进⼊os cache的。
这个过程就是refresh。
每隔1秒钟,es就会将buffer中的数据写⼊到⼀个新的segment file,因此每秒钟产⽣⼀个新的磁盘⽂件(segment file),这个segment file中就存储最近1秒内buffer中写⼊的数据。
如果buffer⾥⾯此时没有数据,就不会执⾏refresh操作;如果buffer⾥⾯有数据,默认1秒钟执⾏⼀次refresh操作,刷⼊⼀个新的segment file中。
操作系统⾥⾯存在操作系统缓存(os cache),数据写⼊磁盘⽂件之前会先进⼊os cache,先进⼊操作系统级别的⼀个内存缓存中。
只要buffer中的数据被refresh到os cache中,数据就可以被检索到了。
可以通过es的restful api或者java api,⼿动执⾏⼀次refresh操作,就是⼿动将buffer中的数据刷⼊os cache中,让数据⽴马就可以被搜索到。
只要数据被输⼊os cache中,buffer就会被清空了,因为不需要保留buffer了,数据在translog⾥⾯已经持久化到磁盘去⼀份了
性能调优
1.系统层⾯
系统层⾯的调优主要是内存的设定与避免交换内存。
ES 安装后默认设置的堆内存是 1GB,这很明显是不够的,那么接下来就会有⼀个问题出现:我们要设置多少内存给 ES 呢?其实这是要看我们集群节点的内存⼤⼩,还取决于我们是否在服务器节点上还是否要部署其他服务。
如果内存相对很⼤,如 64G 及以上,并且不在 ES 集群上部署其他服务,那么建议 ES 内存可以设置为 31G-32G,因为这⾥有⼀个
32G 性能瓶颈问题,直⽩的说就是即使你给了 ES 集群⼤于 32G 的内存,其性能也不⼀定会更加优良,甚⾄会不如设置为 31G-32G 时候的性能。
设置 ES 集群内存的时候,还有⼀点就是确保堆内存最⼩值(Xms)与最⼤值(Xmx)的⼤⼩是相同的,防⽌程序在运⾏时改变堆内存⼤⼩,这是⼀个很耗系统资源的过程。
禁⽌swap,⼀旦允许内存与磁盘的交换,会引起致命的性能问题。
swap空间是⼀块磁盘空间,操作系统使⽤这块空间保存从内存中换出的操作系统不常⽤page数据,这样可以分配出更多的内存做page cache。
这样通常会提升系统的吞吐量和IO性能,但同样会产⽣很多问题。
页⾯频繁换⼊换出会产⽣IO读写、操作系统中断,这些都很影响系统的性能。
这个值越⼤操作系统就会更加积极的使⽤swap空间。
通过:在elasticsearch.yml 中 bootstrap.memory_lock: true,以保持JVM锁定内存,保证ES的性能。
2.分⽚及副本层⾯
ES 是⼀个分布式的搜索引擎, 索引通常都会分解成不同部分, 分布在不同节点的部分数据就是分⽚。
ES ⾃动管理和组织分⽚, 并在必要
的时候对分⽚数据进⾏再平衡分配, 所以⽤户基本上不⽤担⼼分⽚的处理细节。
创建索引时默认的分⽚数为 5 个,并且⼀旦创建不能更改。
ES 默认创建⼀份副本,就是说在 5 个主分⽚的基础上,每个主分⽚都相应的有⼀个副本分⽚。
额外的副本有利有弊,有副本可以有更强的故障恢复能⼒,但也占了相应副本倍数的磁盘空间。
对于副本数,⽐较好确定,可以根据我们集群节点的多少与我们的存储空间决定,我们的集群服务器多,并且有⾜够⼤多存储空间,可以多设置副本数,⼀般是 1-3 个副本数,如果集群服务器相对较少并且存储空间没有那么宽松,则可以只设定⼀份副本以保证容灾(副本数可以动态调整)。
对于分⽚数,是⽐较难确定的。
因为⼀个索引分⽚数⼀旦确定,就不能更改,所以我们在创建索引前,要充分的考虑到,以后我们创建的索引所存储的数据量,否则创建了不合适的分⽚数,会对我们的性能造成很⼤的影响。
查询⼤量⼩分⽚使得每个分⽚处理数据速度更快了,那是不是分⽚数越多,我们的查询就越快,ES 性能就越好呢?其实也不是,因为在查询过程中,有⼀个分⽚合并的过程,如果分⽚数不断的增加,合并的时间则会增加,⽽且随着更多的任务需要按顺序排队和处理,更多的⼩分⽚不⼀定要⽐查询较⼩数量的更⼤的分⽚更快。
如果有多个并发查询,则有很多⼩碎⽚也会降低查询吞吐量。
如果现在你的场景是分⽚数不合适了,但是⼜不知道如何调整,那么有⼀个好的解决⽅法就是按照时间创建索引,然后进⾏通配查询。
如果每天的数据量很⼤,则可以按天创建索引,如果是⼀个⽉积累起来导致数据量很⼤,则可以⼀个⽉创建⼀个索引。
如果要对现有索引进⾏重新分⽚,则需要重建索引,对于每个index的shard数量,可以根据数据总量、写⼊压⼒、节点数量等综合考量后设定,然后根据数据增长状态定期检测下shard数量是否合理。
腾讯云CES技术团队的推荐⽅案是:对于数据量较⼩(100GB以下)的index,往往写⼊压⼒查询压⼒相对较低,⼀般设置35个shard,number_of_replicas设置为1即可(也就是⼀主⼀从,共两副本)。
对于数据量较⼤(100GB以上)的index:⼀般把单个shard的数据量控制在(20GB50GB)让index压⼒分摊⾄多个节点:可通过
index.routing.allocation.total_shards_per_node参数,强制限定⼀个节点上该index的shard数量,让shard尽量分配到不同节点上综合考虑整个index的shard数量,如果shard数量(不包括副本)超过50个,就很可能引发拒绝率上升的问题,此时可考虑把该index拆分为多个独⽴的index,分摊数据量,同时配合routing使⽤,降低每个查询需要访问的shard数量。
3.ES⽅⾯调优
确定集群CPU占⽤率⾼的原因,使⽤GET _nodes/{node}/hot_threads,如果结果为elasticsearch[{node}][search][T#10]则为查询导致,如果结果为elasticsearch[{node}][bulk][T#1],则为写⼊导致
1. index.merge.scheduler.max_thread_count: 在实际调优中,cpu使⽤率很⾼,使⽤SSD替代机械硬盘。
SSD与机械磁盘相⽐,具有⾼
效的读写速度和稳定性。
如果不是SSD,建议设置index.merge.scheduler.max_thread_count: 1,即索引merge最⼤线程数设置为1个,该参数可以有效调节写⼊的性能。
因为在存储介质上并发写,由于寻址的原因,写⼊性能不会提升,只会降低。
当有多个磁盘时可以设置为对应的数量。
2. index.refresh_interval: 这个参数的意思是数据写⼊后⼏秒可以被搜索到,默认是 1s。
每次索引的 refresh 会产⽣⼀个新的 lucene 段,
这会导致频繁的合并⾏为,如果业务需求对实时性要求没那么⾼,可以将此参数调⼤。
3. indices.memory.index_buffer_size: 如果我们要进⾏⾮常重的⾼并发写⼊操作,那么最好将它调⼤⼀些,index buffer的⼤⼩是所有的
shard公⽤的,对于每个 shard来说,最多给512MB,因为再⼤性能就没什么提升了。
ES会将这个设置作为每个shard共享的index buffer,那些特别活跃的shard会更多的使⽤这个 buffer。
默认这个参数的值是10%,也就是jvm堆内存的10%。
4. translog: ES为了保证数据不丢失,每次index、bulk、delete、update完成的时候,⼀定会触发刷新translog到磁盘上。
在提⾼数据安
全性的同时当然也降低了性能。
如果你不在意这点可能性,还是希望性能优先,可以设置如下参数:
"index.translog": {
"sync_interval": "120s", #sync间隔调⾼
"durability": "async", # 异步更新
"flush_threshold_size":"1g" #log⽂件⼤⼩
}
这样设定的意思是开启异步写⼊磁盘,并设定写⼊的时间间隔与⼤⼩,有助于写⼊性能的提升。
1. replica数⽬: 为了让创建的es index在每台datanode上均匀分布,同⼀个datanode上同⼀个index的shard数⽬不应超过3个。
计算公式:
(number_of_shard*(1+number_of_replicas)) < 3*number_of_datanodes每台机器上分配的shard数
⽬,index.routing.allocation.total_shards_per_node: 2
2. merge相关参数
"index.merge.policy.floor_segment": "100mb"
"index.merge.scheduler.max_thread_count": "1"
"index.merge.policy.min_merge_size": "10mb"
1. 超时参数
discovery.zen.ping_timeout 判断 master 选举过程中,发现其他 node 存活的超时设置
discovery.zen.fd.ping_interval 节点被 ping 的频率,检测节点是否存活
discovery.zen.fd.ping_timeout 节点存活响应的时间,默认为 30s,如果⽹络可能存在隐患,可以适当调⼤
discovery.zen.fd.ping_retries ping 失败/超时多少导致节点被视为失败,默认为3
4.Linux层⾯相关调优
1. Linux中,每个进程默认打开的最⼤⽂件句柄数是1000,对于服务器进程来说,显然太⼩,通过修改/etc/security/limits.conf来增⼤打开
最⼤句柄数* - nofile 65535
2. vm.dirty_background_ratio: 这个参数指定了当⽂件系统缓存脏页数量达到系统内存百分之多少时(如5%)就会触发
pdflush/flush/kdmflush等后台回写进程运⾏,将⼀定缓存的脏页异步地刷⼊外存;
3. vm.dirty_ratio: 该参数则指定了当⽂件系统缓存脏页数量达到系统内存百分之多少时(如10%),系统不得不开始处理缓存脏页(因为
此时脏页数量已经⽐较多,为了避免数据丢失需要将⼀定脏页刷⼊外存);在此过程中很多应⽤进程可能会因为系统转⽽处理⽂件IO ⽽阻塞。
把该参数适当调⼩。
如果cached的脏数据所占⽐例(这⾥是占MemTotal的⽐例)超过这个设置,系统会停⽌所有的应⽤层的IO 写操作,等待刷完数据后恢复IO。
所以万⼀触发了系统的这个操作,对于⽤户来说影响⾮常⼤的。
sysctl -w vm.dirty_ratio=10
sysctl -w vm.dirty_background_ratio=5
可以修改/etc/sysctl.conf⽂件进⾏持久化。
ES使⽤建议
1.读数据
避免⼤结果集合深翻,ES 提供了 Scroll 和 Scroll-Scan 这两种查询模式。
Scroll:是为检索⼤量的结果⽽设计的。
例如,我们需要查询 1~100 页的数据,每页 100 条数据。
如果使⽤Search查询:每次都需要在每个分⽚上查询得分最⾼的 from+100 条数据,然后协同节点把收集到的 n×(from+100)条数据聚合起来再进⾏⼀次排序。
每次返回from+1开始的100条数据,并且要重复执⾏100次。
如果使⽤Scroll查询:在各个分⽚上查询10000条数据,协同节点聚合n×10000条数据进⾏合并、排序,并将排名前10000的结果快照起来。
这样做的好处是减少了查询和排序的次数。
2.插⼊索引⾃动⽣成 id
当写⼊端使⽤特定的 id 将数据写⼊ ES 时,ES 会检查对应的索引下是否存在相同的 id,这个操作会随着⽂档数量的增加使消耗越来越⼤,所以如果业务上没有硬性需求建议使⽤ ES ⾃动⽣成的 id,加快写⼊速率。
3.避免稀疏索引
索引稀疏之后,会导致索引⽂件增⼤。
ES的keyword,数组类型采⽤doc_values结构,即使字段是空值,每个⽂档也会占⽤⼀定的空间,所以稀疏索引会造成磁盘增⼤,导致查询和写⼊效率降低。
参数调优汇总
index.merge.scheduler.max_thread_count:1 # 索引 merge 最⼤线程数
indices.memory.index_buffer_size:30% # 内存
index.translog.durability:async # 这个可以异步写硬盘,增⼤写的速度
index.translog.sync_interval:120s #translog 间隔时间
discovery.zen.ping_timeout:120s # ⼼跳超时时间
discovery.zen.fd.ping_interval:120s # 节点检测时间
discovery.zen.fd.ping_timeout:120s #ping 超时时间
discovery.zen.fd.ping_retries:6 # ⼼跳重试次数
thread_pool.bulk.size:20 # 写⼊线程个数由于我们查询线程都是在代码⾥设定好的,我这⾥只调节了写⼊的线程数
thread_pool.bulk.queue_size:1000 # 写⼊线程队列⼤⼩
index.refresh_interval:300s #index 刷新间隔
bootstrap.memory_lock: true#以保持JVM锁定内存,保证ES的性能。