ElasticSearch调优

1、设计阶段调优

1.每天定期段合并Segments

  • 查看内存占用:
curl -s 'http://192.168.10.202:9200/_cat/indices?v'

            备注:store.size 代表副分片内存占用 ; pri.store.size 代表主分片内存占用

  • 合并Segments:   
 curl -s -XPOST 'http://192.168.10.202:9200/index_name/_forcemerge?max_num_segments=1'

            备注:段合并执行需要在空闲的时候,因为段合并会阻塞该索引的其他查询

2.根据业务增量需求,采取基于模板创建索引,通过roll over API滚动索引

1) 基于序号滚动生成

        在生产环境中,默认是写数据用写索引别名logs-write , 读数据用读索引logs-read 。因为写索引只能指向一个索引,即最新的生成的索引,而读索引指向所有索引,更新数据也是用读索引。一般情况下,会将rollover封装成脚本,使用crontab定时器定时触发(附带脚本文件)。

# 创建模版
PUT _template/logs-template
{
  "template": "logs-*",
  "settings": {
    "number_of_shards":   3,
    "number_of_replicas": 1
  },
  "aliases": {
    "logs-write":  {},
    "logs-read":  {}
  },
   "mappings": {
     "logs-type":{
       "dynamic" : false,
          "properties": {
              "title":{
                  "type":"keyword"
              }
          }
     }
   }
}

# 首次创建索引
PUT /logs-000001


# 插入数据
POST /logs-write/logs-write/_bulk
{"index":{"_index":"logs-write","_type":"logs-write"}}
{"title":"深圳1"}
{"index":{"_index":"logs-write","_type":"logs-write"}}
{"title":"深圳2"}
{"index":{"_index":"logs-write","_type":"logs-write"}}
{"title":"深圳3"}
{"index":{"_index":"logs-write","_type":"logs-write"}}
{"title":"深圳4"}
{"index":{"_index":"logs-write","_type":"logs-write"}}
{"title":"深圳5"}
{"index":{"_index":"logs-write","_type":"logs-write"}}
{"title":"深圳6"}

# 触发滚动生成索引
POST /logs-write/_rollover
{
  "conditions": {
    "max_age": "7d",
    "max_docs": 5
  }
}

# 读索引别名
POST /_aliases
{
    "actions": [
        { "add":    { "index": "logs-000001", "alias": "logs-read" }}
    ]
}

# 查看该别名下的索引有哪些
GET /*/_alias/logs-write
GET /*/_alias/logs-read

定时器脚本(yum -y install jq):

#!/bin/bash

write_index=logs-write
read_index=logs-read
max_age=7d
max_docs=5
base_url=http://localhost:9200

# 定义请求数据(单引号里面不用引用变量,双引号才可以)
json_data='{"conditions": {"max_age":"'${max_age}'","max_docs":'${max_docs}'}}'

resp=$(curl -i -s -X POST -H "'Content-type':'application/json'" -d "$json_data"  "${base_url}/${write_index}/_rollover" | grep old_index | jq .old_index,.new_index,.rolled_over)

# 请求结果转换成数组
array=(${resp//,/ })


# 别名定义
if [ "${array[2]}" == "true" ]
then
    json_data='{"actions": [{"add":{"index": '${array[0]}',"alias":"'${read_index}'"}}]}'
    curl -i -s -X POST -H "'Content-type':'application/json'" -d "$json_data"  "${base_url}/_aliases" >> /dev/null
    echo "写索引别名l${write_index}指向${array[1]},读索引别名${read_index}添加新索引${array[0]}"
fi

备注:首次创建索引,索引名称必须以数字结尾,rollover滚动时才能加1(默认为6位数)

2) 基于日期滚动生成

# 与基于条件的方式一样,唯一不同点如下

# 首次创建索引
# URI 编码工具:http://tool.oschina.net/encode?type=4 
# 输入:<logs-{now/d}-000001> 
# 输出:%3Clogs-%7Bnow%2Fd%7D-000001%3E
# /d 被转义为 %2Fd
PUT /%3Clogs-%7Bnow%2Fd%7D-000001%3E
{
  "aliases": {
    "logs_write": {}
  }
}

注意,可能感觉到日期没有变更困惑的问题解释如下: 
1)如果立即执行,new_index的名字就是当前的日期:logs-2019.01.26-000002。 
2)如果24小时候后执行,new_index的名字就是+1天后的日期:logs-2019.01.26-000002。

3) 基于冷热数据处理

      详情请见:https://elasticsearch.cn/article/6127

                       

3.采取curator进行索引的生命周期管理

4.使用别名进行索引管理

5.针对需要分词的字段,合理的设置分词器

6.采取冷热分离机制,热数据存储到SSD,提高检索效率;冷数据定期进行shrink操作,以缩减存储

7.禁用内存交换

vi  /etc/sysctl.conf
在这个文档的最后加上这样一行:
vm.swappiness=1

      内存交换是指当操作系统内存不足时,释放掉一些暂时没有使用到的数据,并将这些数据写入磁盘swapp out,以释放内存来运行新的程序,运行完再swapp in,这对磁盘会有很大的消耗。

     swappiness 设置为 1 比设置为 0 要好,因为在一些内核版本 swappiness 设置为 0 会触发系统 OOM-killer(注:Linux 内核的 Out of Memory(OOM)killer 机制)  

     如果以上条件不允许,可以将打开配置文件中的 mlockall 开关。 它的作用就是允许 JVM 锁住内存,禁止操作系统交换出去。在elasticsearch.yml 文件中,设置如下:

  bootstrap.mlockall: true

二、写入调优

1.写入前副本数设置为0

PUT /logs-000001/_settings
{
    "number_of_replicas": 0
}

操作:在大量写入数据前设置副本为0 , 写入完成后恢复副本数(主分片数不可以改变,副本数可以改变)
目的:有副本存在的时候,导入数据需要同步到副本,并且副本也要完成分析,索引和段合并的操作,影响导入性能

2.写入前关闭refresh_interval设置为-1,禁用刷新机制

PUT /logs-000001/_settings
{
    "refresh_interval": -1
}

       默认的refresh间隔是1s,用index.refresh_interval参数可以设置,这样会其强迫es每秒中都将内存中的数据写入磁盘中,创建一个新的segment file。正是这个间隔,让我们每次写入数据后,1s以后才能看到。但是如果我们将这个间隔调大,比如30s,可以接受写入的数据30s后才看到,那么我们就可以获取更大的写入吞吐量,因为30s内都是写内存的,每隔30s才会创建一个segment file。

3.写入过程中:采取多线程bulk批量写入

        如果要知道一个bulk请求最佳的大小,需要对单个es node的单个shard做压测 。

       单线程发送bulk请求是无法最大化es集群写入的吞吐量的。如果要利用集群的所有资源,就需要使用多线程并发将数据bulk写入集群中。为了更好的利用集群的资源,这样多线程并发写入,可以减少每次底层磁盘fsync的次数和开销。首先对单个es节点的单个shard做压测,比如说,先是2个线程,然后是4个线程,然后是8个线程,16个,每次线程数量倍增。一旦发现es返回了TOO_MANY_REQUESTS的错误,JavaClient也就是EsRejectedExecutionException。此时那么就说明es是说已经到了一个并发写入的最大瓶颈了,此时我们就知道最多只能支撑这么高的并发写入了。

4.尽量使用自动生成的id

       如果我们要手动给es document设置一个id,那么es需要每次都去确认一下那个id是否存在,这个过程是比较耗费时间的。如果我们使用自动生成的id,那么es就可以跳过这个步骤,写入性能会更好。

5.设置节点缓冲区大小

indices.memory.index_buffer_size
允许配置百分比和字节大小的值。默认10%,节点总内存堆的10%用作索引缓冲区大小。

indices.memory.min_index_buffer_size
如果index_buffer_size被设置为一个百分比,这个设置可以指定一个最小值。默认为 48mb。

indices.memory.max_index_buffer_size
如果index_buffer_size被设置为一个百分比,这个设置可以指定一个最小值。默认为无限。

indices.memory.min_shard_index_buffer_size
设置每个分片的最小索引缓冲区大小。默认为4mb。

三、查询调优

1.禁用wildcard查询,模糊查询底层会构建复杂的DFA,输入的字符串越长,CPU消耗越大

2.禁用批量terms(成百上千的场景),即包含大量term限制的dsl

3.充分利用倒排索引机制,能keyword类型尽量keyword

猜你喜欢

转载自blog.csdn.net/qq_23160237/article/details/86619212