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