最近埋点日志量翻了一倍,但是ES写入却很慢,刚开始以为是ES的问题,一直在优化ES,最后问题的原因是filebeat导致写入过慢,添加了一台filebeat后就解决了,ES的写入qps从2000到现在的4万。总结下filebeat的最大量,监控四个文件,每天两亿条数据,就是filebeat的最大值,到达这个值就需要再增加一台filebeat。

一、ES部分

说完解决办法,心急的就可以去解决 自己的ES写入问题了,一般问题都出在生产者上,ES还是很高效的。说下我的ES集群配置:

实例

5个

每个实例核数

32核

内存

128G

硬盘类型

机械硬盘

以上就是我的ES集群配置,目前理论最大QPS4万/秒,这可是机械硬盘,如果换成SSD固态硬盘,速度是现在的3倍,换成固态就是每天112亿条数据。我的ES都是用docker起的,因为要单独配置文件挂在目录,所以没用docker swarm。下面是docker启动的脚本:

#!/bin/sh

docker run -dit \
    --name tmp_es\
    --network=host \
    --restart=always \
    --ulimit memlock=-1:-1 \
    -v /data/elasticsearch/tmp_es/conf/elasticsearch7.yml:/usr/share/elasticsearch/config/elasticsearch.yml \
    -v /data/elasticsearch/tmp_es/conf/jvm.options:/usr/share/elasticsearch/config/jvm.options \
    -v /data/elasticsearch/tmp_es/data:/usr/share/elasticsearch/data \
    -v /data/elasticsearch/tmp_es/logs:/usr/share/elasticsearch/logs \
    -v /usr/share/zoneinfo/Asia/Shanghai:/etc/localtime \
    elasticsearch:7.4.0

下面贴出es配置文件,如果你不知道怎么配置可以直接复制我的,最少可以达到1万qps:

cluster.name: tmp-es-cluster
# 节点名称
node.name: tmp-es-node1
# 是否可以成为master节点
node.master: true
# 是否允许该节点存储数据,默认开启
node.data: true
# 网络绑定
network.host: 192.0.209.14
network.publish_host: 192.0.209.14
# 设置对外服务的http端口
http.port: 9200
# 设置节点间交互的tcp端口
transport.port: 9300
# 集群发现
discovery.seed_hosts:
  - 192.0.13.2
  - 192.0.209.14
  - 192.0.209.15
# 手动指定可以成为 mater 的所有节点的 name 或者 ip,这些配置将会在第一次选举中进行计算
cluster.initial_master_nodes:
  - tmp-es-node1
# 支持跨域访问
http.cors.enabled: true
http.cors.allow-origin: "*"
# 安全认证
xpack.security.enabled: false
#http.cors.allow-headers: "Authorization"

indices.queries.cache.size: 20%
#query请求可使用的jvm内存限制,默认是10%。
indices.requests.cache.size: 5%
#查询request请求的DSL语句缓存,被缓存的DSL语句下次请求时不会被二次解析,可提升检索性能,默认值是1%。
indices.fielddata.cache.size: 30%
#设置字段缓存的最大值,默认无限制。

indices.memory.index_buffer_size : 30%

bootstrap.memory_lock: true
#设置为true锁住内存,当服务混合部署了多个组件及服务时,应开启此操作,允许es占用足够多的内存。

thread_pool.write.queue_size: 1000
# 增大写队列为1000,写入和索引队列都是这个参数,es7之后已经没有bulk这个参数了

再贴出jvm配置文件,我的服务器内存比较大所以配置的30G,要依据你服务器的内存配置,不要超过机器内存的一半并且不要超过32G,超过32G反而不好,官网有解释,因为会造成GC时间过长:

## JVM configuration

################################################################
## IMPORTANT: JVM heap size
################################################################
##
## You should always set the min and max JVM heap
## size to the same value. For example, to set
## the heap to 4 GB, set:
##
## -Xms4g
## -Xmx4g
##
## See https://www.elastic.co/guide/en/elasticsearch/reference/current/heap-size.html
## for more information
##
################################################################

# Xms represents the initial size of total heap space
# Xmx represents the maximum size of total heap space

-Xms30g
-Xmx30g

################################################################
## Expert settings
################################################################
##
## All settings below this section are considered
## expert settings. Don't tamper with them unless
## you understand what you are doing
##
################################################################

## GC configuration
-XX:+UseConcMarkSweepGC
-XX:CMSInitiatingOccupancyFraction=75
-XX:+UseCMSInitiatingOccupancyOnly

## G1GC Configuration
# NOTE: G1GC is only supported on JDK version 10 or later.
# To use G1GC uncomment the lines below.
# 10-:-XX:-UseConcMarkSweepGC
# 10-:-XX:-UseCMSInitiatingOccupancyOnly
# 10-:-XX:+UseG1GC
# 10-:-XX:G1ReservePercent=25
# 10-:-XX:InitiatingHeapOccupancyPercent=30

## DNS cache policy
# cache ttl in seconds for positive DNS lookups noting that this overrides the
# JDK security property networkaddress.cache.ttl; set to -1 to cache forever
-Des.networkaddress.cache.ttl=60
# cache ttl in seconds for negative DNS lookups noting that this overrides the
# JDK security property networkaddress.cache.negative ttl; set to -1 to cache
# forever
-Des.networkaddress.cache.negative.ttl=10

## optimizations

# pre-touch memory pages used by the JVM during initialization
-XX:+AlwaysPreTouch

## basic

# explicitly set the stack size
-Xss1m

# set to headless, just in case
-Djava.awt.headless=true

# ensure UTF-8 encoding by default (e.g. filenames)
-Dfile.encoding=UTF-8

# use our provided JNA always versus the system one
-Djna.nosys=true

# turn off a JDK optimization that throws away stack traces for common
# exceptions because stack traces are important for debugging
-XX:-OmitStackTraceInFastThrow

# flags to configure Netty
-Dio.netty.noUnsafe=true
-Dio.netty.noKeySetOptimization=true
-Dio.netty.recycler.maxCapacityPerThread=0
-Dio.netty.allocator.numDirectArenas=0

# log4j 2
-Dlog4j.shutdownHookEnabled=false
-Dlog4j2.disable.jmx=true

-Djava.io.tmpdir=${ES_TMPDIR}

## heap dumps

# generate a heap dump when an allocation from the Java heap fails
# heap dumps are created in the working directory of the JVM
-XX:+HeapDumpOnOutOfMemoryError

# specify an alternative path for heap dumps; ensure the directory exists and
# has sufficient space
-XX:HeapDumpPath=data

# specify an alternative path for JVM fatal error logs
-XX:ErrorFile=logs/hs_err_pid%p.log

## JDK 8 GC logging

8:-XX:+PrintGCDetails
8:-XX:+PrintGCDateStamps
8:-XX:+PrintTenuringDistribution
8:-XX:+PrintGCApplicationStoppedTime
8:-Xloggc:logs/gc.log
8:-XX:+UseGCLogFileRotation
8:-XX:NumberOfGCLogFiles=32
8:-XX:GCLogFileSize=64m

以上是ES的配置,优化过程中还对索引的配置进行了优化,但是基本又没啥用,下面我也列出来,你可以试试,这些都基于你的ES集群本身能力就强,如果ES集群不行,再优化索引也徒劳,以下是我在网上找的优化办法,基本没啥用:

1、

PUT /_all/_settings
{
"index.translog.durability": "async",
"index.translog.sync_interval": "1m",
"index.translog.flush_threshold_size": "1024mb"
}

2、

PUT /_cluster/settings
{
"persistent" : {
"index.merge.scheduler.max_thread_count" : 1
}
}

3、

文件索引存储方式改成nio

PUT /car-2021.03.25/_settings
{
"index.store.type": "niofs"
}

 

4、存为text类型的字段(string字段默认类型为text): 做分词后存储倒排索引,支持全文检索,可以通过下面几个参数优化其存储方式: norms:用于在搜索时计算该doc的_score(代表这条数据与搜索条件的相关度),如果不需要评分,可以将其关闭。 index_options:控制倒排索引中包括哪些信息(docs、freqs、positions、offsets)。对于不太注重_score/highlighting的使用场景,可以设为 docs来降低内存/磁盘资源消耗。 fields: 用于添加子字段。对于有sort和聚合查询需求的场景,可以添加一个keyword子字段以支持这两种功能。(这个最扯,复杂且没用)

5、这个收效甚微

PUT index/_settings
 {
   "refresh_interval": "1m"
 }

以上是ES集群和索引优化,主要还是ES集群优化,对搜索和索引都有较大提高。

二、logstash部分

我是用logstash写入es顺便说下logstash,logstash基本不用优化,默认的就够通,我用的三个logstash写入es,实际上2个就够。

docker启动脚本:

#!/bin/sh
docker run -dit \
    --name tmp_logstash \
    -m 10g \
    --cpus=8 \
    -v /data/logstash/tmp_logstash/conf/:/usr/share/logstash/config/ \
    -v /usr/share/zoneinfo/Asia/Shanghai:/etc/localtime \
    logstash:7.2.0

logstash 配置logstash.yml:

pipeline.batch.size: 3000
pipeline.batch.delay: 10

logstash 配置pipelines.yml:

- pipeline.id: my-logstash
  path.config: "/usr/share/logstash/config/*.conf"
  pipeline.workers: 8
  pipeline.batch.size: 3000

 

以上是logstash配置,目前够用。

三、kibana

kibana可以监控ES集群、logstash、filebeat等,很好用,给大家看下我现在的qps:

es match很慢 es写入速度慢_es match很慢

es match很慢 es写入速度慢_JVM_02

 

 

kibana用配套的7.4的就行。

 

总结下,我的ES优化基本解决了我目前的问题,也满足目前的需求,给大家分享下,避免走弯路,多看官方文档https://www.elastic.co/guide/en/elasticsearch/client/java-rest/7.4/java-rest-high-search.html