ES 性能优化总结 03

https://www.cnblogs.com/zhangan/p/11231990.html

一、节点层 :

  不要为bulk和search分配过大的队列。队列越大,GC的压力也会越大

  搜索操作很依赖对系统cache的命中,标准的建议是把50%的可用内存作为ES的堆内存,为Lucene保留剩下的50%,用作系统cache

二、索引设计优化

    1.尽量避免使用嵌套或者parent/child,性能会变慢很多

Lucene中, 倒排索引一旦被创建就不可改变, 要添加或修改文档, 就需要重建整个倒排索引, 这就对一个index所能包含的数据量, 或index可以被更新的频率造成了很大的限制.

为了在保留不变性的前提下实现倒排索引的更新, Lucene引入了一个新思路: 使用更多的索引, 也就是通过增加新的补充索引来反映最新的修改, 而不是直接重写整个倒排索引.

1,添加document的流程

① 将数据写入buffer(内存缓冲区);

② 执行commit操作: buffer空间被占满, 其中的数据将作为新的 index segment 被commit到文件系统的cache(缓存)中;

③ cache中的index segment通过fsync强制flush到系统的磁盘上;

④ 写入磁盘的所有segment将被记录到commit point(提交点)中, 并写入磁盘;

④ 新的index segment被打开, 以备外部检索使用;

⑤ 清空当前buffer缓冲区, 等待接收新的文档.

indexing buffer优化说明如下:

(a) fsync是一个Unix系统调用函数, 用来将内存缓冲区buffer中的数据存储到文件系统. 这里作了优化, 是指将文件缓存cache中的所有segment刷新到磁盘的操作.

(b)  修改index_buffer_size 的设置,可以设置成百分数,也可设置成具体的大小,大小可根据集群的规模做不同的设置测试。indices.memory.index_buffer_size:10%(默认,可优化30%写入配置文件)

(c) 每个Shard都有一个提交点(commit point), 其中保存了当前Shard成功写入磁盘的所有segment.

+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

2,优化写入流程 - 实现近实时搜索

(1) 现有流程的问题:

插入的新文档必须等待fsync操作将segment强制写入磁盘后, 才可以提供搜索.而 fsync操作的代价很大, 使得搜索不够实时.

(2) 改进写入流程:

① 将数据写入buffer(内存缓冲区);

② 不等buffer空间被占满, 而是每隔一定时间(默认1s), 其中的数据就作为新的index segment被commit到文件系统的cache(缓存)中;

③ index segment 一旦被写入cache(缓存), 就立即打开该segment供搜索使用;

④ 清空当前buffer缓冲区, 等待接收新的文档.

优化的地方: 过程②和过程③:

segment进入操作系统的缓存中就可以提供搜索, 这个写入和打开新segment的轻量过程被称为refresh.

优化refresh的间隔:

Elasticsearch中, 每个Shard每秒都会自动refresh一次, 所以ES是近实时的, 数据插入到可以被搜索的间隔默认是1秒

(1) 手动refresh —— 测试时使用, 正式生产中请减少使用:

# 刷新所有索引:
POST _refresh
# 刷新某一个索引: 
POST index/_refresh

(2) 手动设置refresh间隔 —— 若要优化索引速度, 而不注重实时性, 可以降低刷新频率:

# 在已有索引中设置, 间隔10秒: 
PUT /_all/_settings
{
  "index":{
    "refresh_interval":"120s"
  }
}

(3) 当你在生产环境中建立一个大的新索引时, 可以先关闭自动刷新, 要开始使用该索引时再改回来:

# 关闭自动刷新: 
PUT /_all/_settings
{
    "refresh_interval": -1 
} 

(4)调小索引副本数,通过增大refresh间隔周期,同时不设置副本来提高写性能。

+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

3. 优化写入流程 - 实现持久化变更

Elasticsearch通过事务日志(translog)来防止数据的丢失 —— durability持久化.

(1)文档持久化到磁盘的流程

① 索引数据在写入内存buffer(缓冲区)的同时, 也写入到translog日志文件中;

② 每隔refresh_interval的时间就执行一次refresh:

(a) 将buffer中的数据作为新的 index segment, 刷到文件系统的cache(缓存)中;

(b) index segment一旦被写入文件cache(缓存), 就立即打开该segment供搜索使用;

③ 清空当前内存buffer(缓冲区), 等待接收新的文档;

④ 重复①~③, translog文件中的数据不断增加;

⑤ 每隔一定时间(默认30分钟), 或者当translog文件达到一定大小时, 发生flush操作, 并执行一次全量提交:

4,基于translog和commit point的数据恢复

(1) 关于translog的配置:

flush操作 = 将translog中的记录刷到磁盘上 + 更新commit point信息 + 清空translog文件.

(2) 数据的故障恢复:

① 增删改操作成功的标志: segment被成功刷新到Primary Shard和其对应的Replica Shard的磁盘上, 对应的操作才算成功.

② translog文件中存储了上一次flush(即上一个commit point)到当前时间的所有数据的变更记录. —— 即translog中存储的是还没有被刷到磁盘的所有最新变更记录.

③ ES发生故障, 或重启ES时, 将根据磁盘中的commit point去加载已经写入磁盘的segment, 并重做translog文件中的所有操作, 从而保证数据的一致性.

(3) 异步刷新translog:

为了保证不丢失数据, 就要保护translog文件的安全:

Elasticsearch 2.0之后, 每次写请求(如index、delete、update、bulk等)完成时, 都会触发fsync将translog中的segment刷到磁盘, 然后才会返回200 OK的响应;

或者: 默认每隔5s就将translog中的数据通过fsync强制刷新到磁盘.

—— 提高数据安全性的同时, 降低了一点性能.

==> 频繁地执行fsync操作, 可能会产生阻塞导致部分操作耗时较久. 如果允许部分数据丢失, 可设置异步刷新translog来提高效率.优化如下:

5,优化写入流程 - 实现海量segment文件的归并

由上述近实时性搜索的描述, 可知ES默认每秒都会产生一个新的segment文件, 而每次搜索时都要遍历所有的segment, 这非常影响搜索性能.

为解决这一问题, ES会对这些零散的segment进行merge(归并)操作, 尽量让索引中只保有少量的、体积较大的segment文件.

这个过程由独立的merge线程负责, 不会影响新segment的产生.

同时, 在merge段文件(segment)的过程中, 被标记为deleted的document也会被彻底物理删除.

1,merge操作的流程

① 选择一些有相似大小的segment, merge成一个大的segment;
② 将新的segment刷新到磁盘上;
③ 更新commit文件: 写一个新的commit point, 包括了新的segment, 并删除旧的segment;
④ 打开新的segment, 完成搜索请求的转移;
⑤ 删除旧的小segment.

2,优化merge的配置项

segment的归并是一个非常消耗系统CPU和磁盘IO资源的任务, 所以ES对归并线程提供了限速机制, 确保这个任务不会过分影响到其他任务.

segment合并;索引节点粒度配置,segment默认最小值2M,不过有时候合并会拖累写入速率

(1) 归并线程的数目:

推荐设置为CPU核心数的一半, 如果磁盘性能较差, 可以适当降低配置, 避免发生磁盘IO堵塞,所以我们需要降低每个索引并发访问磁盘的线程数。这个设置允许 max_thread_count + 2 个线程同时进行磁盘操作,也就是设置为 1 允许三个线程。

五,Cache的设置及使用:

a) QueryCache: ES查询的时候,使用filter查询会使用query cache, 如果业务场景中的过滤查询比较多,建议将querycache设置大一些,以提高查询速度。
indices.queries.cache.size: 10%(默认),可设置成百分比,也可设置成具体值,如256mb。(可写入配置文件)
当然也可以禁用查询缓存(默认是开启), 通过index.queries.cache.enabled:false设置。
b) FieldDataCache: 在聚类或排序时,field data cache会使用频繁,因此,设置字段数据缓存的大小,在聚类或排序场景较多的情形下很有必要,可通过indices.fielddata.cache.size:30% 或具体值10GB来设置。但是如果场景或数据变更比较频繁,设置cache并不是好的做法,因为缓存加载的开销也是特别大的。
c) ShardRequestCache: 查询请求发起后,每个分片会将结果返回给协调节点(Coordinating Node), 由协调节点将结果整合。
如果有需求,可以设置开启;  通过设置index.requests.cache.enable: true来开启。
不过,shard request cache只缓存hits.total, aggregations, suggestions类型的数据,并不会缓存hits的内容。也可以通过设置indices.requests.cache.size: 1%(默认)来控制缓存空间大小。

+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

ElasticSearch 深度分页

常见深度分页方式 from+size
es 默认采用的分页方式是 from+ size 的形式,在深度分页的情况下,这种使用方式效率是非常低的,比如

from = 5000, size=10, es 需要在各个分片上匹配排序并得到5000*10条有效数据,然后在结果集中取最后10条

数据返回,这种方式类似于mongo的 skip + size。

除了效率上的问题,还有一个无法解决的问题是,es 目前支持最大的 skip 值是 max_result_window ,默认

为 10000 。也就是当 from + size > max_result_window 时,es 将返回错误

另一种分页方式 scroll
为了满足深度分页的场景,es 提供了 scroll 的方式进行分页读取。原理上是对某次查询生成一个游标 scroll_id , 后续的查询只需要根据这个游标去取数据,直到结果集中返回的 hits 字段为空,就表示遍历结束。scroll_id 的生成可以理解为建立了一个临时的历史快照,在此之后的增删改查等操作不会影响到这个快照的结果。

使用 curl 进行分页读取过程如下:

先获取第一个 scroll_id,url 参数包括 /index/_type/ 和 scroll,scroll 字段指定了scroll_id 的有效生存期,以分钟为单位,过期之后会被es 自动清理。如果文档不需要特定排序,可以指定按照文档创建的时间返回会使迭代更高效。
 

所有文档获取完毕之后,需要手动清理掉 scroll_id 。虽然es 会有自动清理机制,但是 srcoll_id 的存在会耗费大量的资源来保存一份当前查询结果集映像,并且会占用文件描述符。所以用完之后要及时清理。使用 es 提供的 CLEAR_API 来删除指定的 scroll_id

发布了331 篇原创文章 · 获赞 1 · 访问量 3496

猜你喜欢

转载自blog.csdn.net/kuaipao19950507/article/details/103841306