Elasticsearch各种优化操作
优化Refresh时间
ES在写入数据的时候,采用延迟写入的策略,数据会先写到内存中,当超过默认时间1秒 (index.refresh_interval,默认)会进行一次写入磁盘操作。如果对实时搜索要求不高的情况下,可以适当地将此值设置的高点,可以有效地减少 segment (一个segment是一个完备的lucene倒排索引,而倒排索引是通过词典(Term Dictionary)到文档列表(Postings List)的映射关系,快速做查询的)合并压力。通过如下方式设置:
1、创建索引时指定
2、对已有的索引进行设置
查看索引
合理设置副本数量
过多的副本数量会严重影响写索引的效率,因为在写索引时,会把写入的数据同步到所有的副本节点上,如果副本数多那么写的效率肯定会降低。一般在初始大量写入ES操作时,应该先将副本数设置为0,等数据写入完毕后再设置副本正常值。
设置副本数量,创建时:
对于现有索引设置副本数量:
查看索引情况:
内存设置
ES默认安装后设置的内存是 1GB,对于任何一个现实业务来说,这个设置都太小了。 如果是通过解压安装的 ES,则在 ES 安装文件中包含一个 jvm.option 文件,添加如下命令来设置 ES 的堆大小,Xms 表示堆的初始大小,Xmx 表示可分配的最大内存,都是 1GB。
%es_home%\config\jvm.options该文件中都是对jvm相关的配置,包括OOM及垃圾回收的配置
确保Xmx和Xms的大小是相同的,其目的是为了能够在 Java 垃圾回收机制清理完堆区后不需要重新分隔计算堆区的大小而浪费资源,可以减轻伸缩堆大小带来的压力。 假设你有一个 64G 内存的机器,按照正常思维思考,你可能会认为把 64G 内存都给 ES 比较好,但现实是这样吗, 越大越好?虽然内存对 ES 来说是非常重要的,但是答案是否定的。
因为 ES堆内存的分配需要满足以下两个原则:
1、不要超过物理内存的 50%:Lucene 的设计目的是把底层 OS 里的数据缓存到内存中。 Lucene 的段是分别存储到单个文件中的,这些文件都是不会变化的,所以很利于缓存,同时操作系统也会把这些段文件缓存起来,以便更快的访问。 如果我们设置的堆内存过大,Lucene 可用的内存将会减少,就会严重影响降低 Lucene 的全文本查询性能。
2、堆内存的大小最好不要超过 32GB:在 Java 中,所有对象都分配在堆上,然后有一个 Klass Pointer 指针指向它的类元数据。 这个指针在 64 位的操作系统上为 64 位,64 位的操作系统可以使用更多的内存(2^64)。在 32 位的系统上为 32 位,32 位的操作系统的最大寻址空间为 4GB(2^32)。 但是 64 位的指针意味着更大的浪费,因为你的指针本身大了。浪费内存不算,更糟糕的是,更大的指针在主内存和缓存器(例如 LLC, L1等)之间移动数据的时候,会占用更多的带宽。 最终我们都会采用 31 G 设置 -Xms 31g -Xmx 31g 假设你有个机器有 128 GB 的内存,你可以创建两个节点,每个节点内存分配不超过 32 GB。 也就是说不超过 64 GB 内存给 ES 的堆内存,剩下的超过 64 GB 的内存给 Lucene 。
批量操作
当你是大量的写入数据时,建议使用Bulk API进行批量操作。Bulk默认设置批量提交不能超过100M。
请求格式:
{"action": {}}
{data}
// action 取值:
create:文档不存在时创建
update:更新文档
index:创建新文档,或者替换已经有的文档
delete:删除一个文档
注意Content-Type:application/x-ndjson
每一行JSON结束必须有换行符\n(回车)。注意第七行也必须有个空行,不然报错。
批量查看文档
存储设备优化
ES是高频使用硬盘的应用,在segment合并操作时会频繁操作硬盘,对硬盘要求较高,如果硬盘速度越高,那么es的性能肯定就会高。
Mapping建模
- 尽量避免使用nested或parent/child,能不用就不用;nested 查询相对比较慢,parent/child query更慢,比nested query慢上百倍;因此能在mapping设计阶段搞定的(大宽表设计或采用比较smart的数据结构),就不要用父子关系的mapping。
嵌套查询的使用:
2. 如果一定要使用nested fields,保证nested fields字段不能过多,目前ES默认限制是50。参考:
index.mapping.nested_fields.limit :50。
创建索引时设置:
因为针对1个document,每一个nested field,都会生成一个独立的document,这将使Doc数量剧增,影响查询效率,尤其是JOIN的效率。
3. 避免使用动态值作为字段(key),动态递增的mapping,会导致集群崩溃;同样,也需要控制字段的数量,业务中不使用的字段,就不要索引。控制索引的字段数量、mapping深度、索引字段的类型,对于ES的性能优化是重中之重。以下是ES关于字段数、mapping深度的一些默认设置:
index.mapping.nested_objects.limit :10000
index.mapping.total_fields.limit:1000
index.mapping.depth.limit: 20
查询优化
1、query_string 或 multi_match的查询字段越多, 查询越慢。可以在mapping阶段,利用copy_to属性将多字段的值索引到一个新字段,multi_match时,用新的字段查询。
2、日期字段的查询, 尤其是用now的查询实际上是不存在缓存的,因此, 可以从业务的角度来考虑是否一定要用now,毕竟利用query cache是能够大大提高查询效率的。
3、查询结果集的大小不能随意设置成大得离谱的值, 如query.setSize(size)不能设置成Integer.MAX_VALUE, 因为ES内部需要建立一个数据结构来放指定大小的结果集数据。
4、避免层级过深的聚合查询, 层级过深的group by , 会导致内存、CPU消耗,建议在服务层通过程序来组装业务,也可以通过pipeline的方式来优化。