elasticsearch高可靠方案(Elasticsearch优化汇总二)

1.1 实施过程1.1.1 ES版本选择

在使用Elasticsearch之前,我们总是要先选择一个版本,选择最新的版本永远是一个不会错的选择。因为,Elasticsearch作为一个社区活跃的开源软件,有着极快的迭代着开发->反馈->再开发的模式。每个版本都可能有着大量的改进或者新功能,特别是大数据领域本身也在不停的进化着方法论,Elasticsearch从一个开始的只能做全文检索,到增加了列式存储、聚合框架、BKD Tree和优化稀疏数据存储,再到支持向量相似性,可以搜索语音、图片,直到几乎变成软件开发中搜索的代名词.

因此,在最新的版本上做开发,基本上你会被Elasticsearch带着前进。ES最早的版本我们不在关注,目前官网于2022年2月11号发布的ES8.0.0版本,于3月23号发布的ES8.1.1的版本,中间相隔4个版本,更新特别快,为了版本稳定我们选择ES7.8.1作为我们平台此次实施的重点技术研究对象。在功能验证,版本稳定后,更换最新版本。下面附一张ES6与ES7的对比表:

1.1.2 ES集群搭建

本篇介绍公司测试环境,docker容器化ES集群环境的搭建过程,具体如下:

1、ES镜像下载: docker pull elasticsearch:7.8.1,若无法上网,需提前准备好ES的镜像文件,在上网机pull下来后,运行:docker save elasticsearch:7.8.1 -o elasticsearch.tar ,构建elasticsearch.tar镜像包。

2、ES镜像导入:docker load -i elasticsearch.tar

3、安装ES准备:确保镜像中包含JDK环境,JDK版本在1.8以上。

4、设置ES容器网络:

docker network create --driver bridge --subnet 172.20.0.0/24 --gateway 172.20.0.1 es-default

5、修改Linux内核:

vi /etc/sysctl.conf

vm.max_map_count=262144

#使配置文件生效

sysctl –p

6、创建集群目录

mkdir -p /home/geocube/workspace/es/es01

mkdir -p /home/geocube/workspace/es/es02

mkdir -p /home/geocube/workspace/es/es03

7、创建配置文件目录/数据目录/日志目录

//ES01

cd /home/geocube/workspace/es/es01

mkdir conf data01 logs

chmod 777 -R conf

chmod 777 -R data01

chmod 777 -R logs

//ES02

cd /home/geocube/workspace/es/es02

mkdir conf data02 logs

chmod 777 -R conf

chmod 777 -R data02

chmod 777 -R logs

//ES03

cd /home/geocube/workspace/es/es03

mkdir conf data03 logs

chmod 777 -R conf

chmod 777 -R data03

chmod 777 -R logs

8、分别配置ES01、ES02、ES03的application.yml,更改node.name、network.publish_host、http.port等实际值

# 集群名称 3个配置文件要一直

cluster.name: elk202

# 节点名称 3个配置文件要不一致 本例对应es01 es02 es03

node.name: es01

# 是否主节点 具体配置可参考网上 本例三个节点都是主节点 也都是数据节点

node.master: true

# # 是否数据节点

node.data: true

#新版本不能在配置文件设置index开头的属性

#设置分片数 默认5

# index.number_of_shards: 3

# 副本个数

# index.number_of_replicas: 1

# 存储设置

path.data: /usr/share/elasticsearch/data

path.logs: /usr/share/elasticsearch/logs

# 设置日志级别 调试可以用trace或者debug

logger.org.elasticsearch.transport: debug

# # 网络设置 publish_host写容器启动时通过--ip设置的对应的IPV4地址 如 192.168.31.108

# 由于9300端口和publish_host都是内部通信用的所以考虑使用容器内部地址更安全也更方便

network.publish_host: 192.168.31.108

# 这里配置0.0.0.0是为了外部可以通过localhost或者具体IP都可以访问

network.host: 0.0.0.0

# 集群间通信端口

transport.tcp.port: 9300

# 是否压缩 这个属性后期会废弃掉

transport.tcp.compress: true

# 对外暴露的端口

http.port: 9200

http.max_content_length: 100mb

# 初始化数据恢复时并发恢复线程的个数 默认是4

#cluster.routing.allocation.node_initial_primaries_recoveries: 4

#添加删除节点或负载均衡时并发恢复线程的个数 默认是4

#cluster.routing.allocation.node_concurrent_recoveries: 2

#设置数据恢复时限制的带宽 0为无限制 之前版本使用 max_size_per_sec

indices.recovery.max_bytes_per_sec: 0

#设置这个参数保证集群中的节点可以知道其他N个有master资格的节点 默认为1

discovery.zen.minimum_master_nodes: 1

#集群中自动发现其他节点时ping连接超时时间 默认为3s

discovery.zen.ping_timeout: 3s

#这个超时新版本改了 改成了上面的discovery.zen.ping_timeout

#discovery.zen.ping.timeout: 3s

#是否打开多播发现节点 默认是true

#discovery.zen.ping.multicast.enabled: false

#设置新节点启动时能够发现的主节点列表 一定要注意这里的端口是9300对应的端口

discovery.zen.ping.unicast.hosts: ['192.168.31.108:9300']

#新版本增加了初始化主节点配置 可以使用节点名称标识

cluster.initial_master_nodes: ['es01']

9、分别配置ES01、ES02、ES03的JVM.options

默认情况下,Elasticsearch告诉JVM使用最小和最大大小为1 GB的堆,在进入生产阶段时,配置堆大小以确保Elasticsearch有足够的可用堆非常重要。。

设置标准如下:

l 将最小堆大小(Xms)和最大堆大小(Xmx)设置为彼此相等。

l Elasticsearch可用的堆越多,可用于缓存的内存就越多。但是请注意,过多的堆可能会使您长时间停滞垃圾回收。

l 设置Xmx为不超过物理RAM的50%,以确保有足够的物理RAM用于内核文件系统缓存。

l 请勿将其设置Xmx为高于JVM用于压缩对象指针(压缩oop)的临界值;确切的截止时间有所不同,但接近32 GB。可以通过在日志中查找如下一行来验证您是否处于限制范围内。

l 尝试保持在基于零的压缩oop的阈值以下;确切的截止时间有所不同,但是在大多数系统上26 GB是安全的,但是在某些系统上可以达到30 GB。您可以通过使用JVM选项启动Elasticsearch -XX: UnlockDiagnosticVMOptions -XX: PrintCompressedOopsMode并查找类似于以下内容的行来验证您是否处于限制之下

# 根据机器的配置酌情设置 单位可以是 m g

-Xmx2g

-Xms2g

10、 分别配置ES01、ES02、ES03的log4j2.properties

注意每行末尾不要有空格,否则报错

appender.rolling.type = RollingFile

appender.rolling.name = rolling

appender.rolling.fileName = ${sys:es.logs.base_path}${sys:file.separator}${sys:es.logs.cluster_name}_server.json

appender.rolling.layout.type = ESJsonLayout

appender.rolling.layout.type_name = server

appender.rolling.filePattern = ${sys:es.logs.base_path}${sys:file.separator}${sys:es.logs.cluster_name}-%d{yyyy-MM-dd}-%i.json.gz

appender.rolling.policies.type = Policies

appender.rolling.policies.time.type = TimeBasedTriggeringPolicy

appender.rolling.policies.time.interval = 1

appender.rolling.policies.time.modulate = true

appender.rolling.policies.size.type = SizeBasedTriggeringPolicy

appender.rolling.policies.size.size = 256MB

appender.rolling.strategy.type = DefaultRolloverStrategy

appender.rolling.strategy.fileIndex = nomax

appender.rolling.strategy.action.type = Delete

appender.rolling.strategy.action.basepath = ${sys:es.logs.base_path}

appender.rolling.strategy.action.condition.type = IfFileName

appender.rolling.strategy.action.condition.glob = ${sys:es.logs.cluster_name}-*

appender.rolling.strategy.action.condition.nested_condition.type = IfAccumulatedFileSize

appender.rolling.strategy.action.condition.nested_condition.exceeds = 2GB

11、 启动es集群

#ES01

docker run -p 9200:9200 --name es-01 --restart always \

--net es-default --ip 172.20.0.2 \

-v /home/geocube/workspace/es/es01/data01:/usr/share/elasticsearch/data \

-v /home/geocube/workspace/es/es01/conf:/usr/share/elasticsearch/config \

-v /home/geocube/workspace/es/es01/logs:/usr/share/elasticsearch/logs \

-d elasticsearch:7.8.1

#ES02

docker run --name es-02 -p 9202:9200 --restart always \

--net es-default --ip 172.20.0.3 \

-v /home/geocube/workspace/es/es02/data02:/usr/share/elasticsearch/data \

-v /home/geocube/workspace/es/es02/conf:/usr/share/elasticsearch/config \

-v /home/geocube/workspace/es/es01/logs:/usr/share/elasticsearch/logs \

-d elasticsearch:7.8.1

#ES03

docker run -p 9203:9200 --name es-03 --restart always \

--net es-default --ip 172.20.0.4 \

-v /home/geocube/workspace/es/es03/data03:/usr/share/elasticsearch/data \

-v /home/geocube/workspace/es/es03/conf:/usr/share/elasticsearch/config \

-v /home/geocube/workspace/es/es01/logs:/usr/share/elasticsearch/logs \

-d elasticsearch:7.8.1

12、 如果一切正常,默认会在9200、9202、9203端口运行,打开浏览器输入默认的地址http://localhost:9200/ 即可得到说明信息:

elasticsearch高可靠方案(Elasticsearch优化汇总二)(1)

1.1.3 ES-head集群监控

ealsticsearch只是后端提供各种api,那么怎么直观地使用它呢?elasticsearch-head将是一款专门针对于elasticsearch的客户端工具。安装过程如下:

1、ES-head镜像下载: docker pull mobz/elasticsearch-head:5,若无法上网,需提前准备好ES的镜像文件,在上网机pull下来后,运行:docker save mobz/elasticsearch-head:5 -o elasticsearch-head.tar ,构建elasticsearch-head.tar镜像包。

2、ES镜像导入:docker load -i elasticsearch-head.tar

3、运行head插件:

docker run -it --name elasticsearch-head --restart always -p 9100:9100 -d mobz/elasticsearch-head:5

4、注意问题:如果链接es集群报错cors,因为跨域,无法成功连接到elasticsearch,需要在es主节点的elasticsearch.yml中添加如下内容。

http.cors.enabled: true

http.cors.allow-origin: "*"

#修改设置默认检索结果不能超过1000条便于测试

PUT ds_product_imagery/_settings

{

"index":{

"max_result_window":100000000

}

}

打开浏览器输入地址http://localhost:9100/ 即可进入使用界面,绿色表明已成功连接。

elasticsearch高可靠方案(Elasticsearch优化汇总二)(2)

1.1.4 Kibana安装

Elastic Search和Kibana是什么关系呢?简单讲,Elastic Search本身是搜索服务器,与这个服务器交互唯一的方式是通过HTTP请求。然而很多情况下,用户不仅需要用HTTP请求把数据放入Elastic服务器,或用HTTP请求进行搜索。同时用户希望简化操作,或者将数据可视化。相比之前安装的elasticsearch-head,Kibana的界面更为友好。有了Kibana之后,数据请求就不用在命令行或HTTP客户端发送HTTP请求了,而可以到Kibana的页面,以更用户友好的方式来使用Elastic Search。Kibana安装过程如下:

1、Kibana镜像下载: docker pull kibana:7.4.1,若无法上网,需提前准备好ES的镜像文件,在上网机pull下来后,运行:docker save kibana:7.4.1 -o Kibana.tar ,构建Kibana.tar镜像包。

2、ES镜像导入:docker load -i Kibana.tar

3、编辑kibana.yml配置文件:

注意:elasticsearch.hosts为Elasticsearch实例

server.name: kibana

server.host: "0"

elasticsearch.hosts: [ "http://192.168.12.183:9200" ]

xpack.monitoring.ui.container.elasticsearch.enabled: true

4、运行 Kibana

docker run -d --restart=always --log-driver json-file --log-opt max-size=100m --log-opt max-file=2 --name xinyar-kibana -p 5601:5601 -v /data/elk/kibana.yml:/usr/share/kibana/config/kibana.yml kibana:7.4.1

5、启动kibana

默认的访问地址http://localhost:5601, 回车即可到达操作页面。

elasticsearch高可靠方案(Elasticsearch优化汇总二)(3)

点击左侧菜单倒数第三个的Dev Tools即可进入操作页面,进行查询等操作。

elasticsearch高可靠方案(Elasticsearch优化汇总二)(4)

利用postman发送get请求测试(必须用get方式携带body请求)

elasticsearch高可靠方案(Elasticsearch优化汇总二)(5)

1.2 ES优化

ES性能优化是没有什么捷径的,我们不要期待着随手调一个参数,就可以万能的应对所有的性能慢的场景。也许有的场景是你换个参数,或者调整一下语法,就可以搞定,但是绝对不是所有场景都可以这样。往ES里写的数据,实际上都写到磁盘文件里去了,磁盘文件里的数据操作系统会自动将里面的数据缓存到os cache里面去,ES的搜索引擎严重依赖于底层的filesystem cache,你如果给filesystem cache更多的内存,尽量让内存可以容纳所有的indx segment file索引数据文件,那么你搜索的时候就基本都是走内存的,性能会非常高。

性能差距可以有很大,基于我们做的200万和2000万数据量的测试和压测,如果走磁盘一般肯定上秒,搜索性能绝对是秒级别的,1秒,5秒,10秒。但是如果是走filesystem cache,是走纯内存的,那么一般来说性能比走磁盘要高一个数量级,基本上就是毫秒级的,从几毫秒到几百毫秒不等。

ES内存有个算法可以帮助我们更好的理解文件缓存,例如ES节点有3台机器,每台机器,看起来内存很多,64G,总内存,64 * 3 = 192g,每台机器给es jvm heap是32G,那么剩下来留给filesystem cache的就是每台机器才32g,总共集群里给filesystem cache的就是32 * 3 = 96g内存,如果你此时,你整个,磁盘上索引数据文件,在3台机器上,一共占用了1T的磁盘容量,你的es数据量是1t,每台机器的数据量是300g,你觉得你的性能能好吗?filesystem cache的内存才100g,十分之一的数据可以放内存,其他的都在磁盘,然后你执行搜索操作,大部分操作都是走磁盘,性能肯定差。

归根结底,你要让es性能要好,最佳的情况下,就是你的机器的内存,至少可以容纳你的总数据量的一半。 最佳的情况下,我们自己的生产环境实践经验,所以说我们当时的策略,是仅仅在es中就存少量的数据,就是你要用来搜索的那些索引,内存留给filesystem cache的,就100G,那么你就控制在100gb以内,相当于是,你的数据几乎全部走内存来搜索,性能非常之高,一般可以在1秒以内。

1.2.1 常规优化

1、不要返回数据量非常大的结果集

2、避免出现大文档,即单条索引记录的体积不要过大

3、默认情况下, http.max_content_length 设置为100mb,限制单个索引记录的大小不能超过100mb。

4、使用更高性能的查询API。比如多用filter少用query。

5、使用足够的最小数字类型有利于节约磁盘空间: 数字数据选择的字段类型会对磁盘使用产生重大影响。 特别是,整数应使用整数类型(字节、短整型、整数或长整型)存储,浮点数应存储在 scaled_float 中(如果合适)或适合用例的最小类型:使用 float over double,或 half_float over float 将有助于节省存储空间。

6、document模型设计:es里面的复杂的关联查询,复杂的查询语法,尽量别用,一旦用了性能一般都不太好,搜索尽可能少的字段。document模型设计是非常重要的,很多操作,不要在搜索的时候才想去执行各种复杂的乱七八糟的操作。es能支持的操作就是那么多,不要考虑用es做一些它不好操作的事情。如果真的有那种操作,尽量在document模型设计的时候,写入的时候就完成。另外对于一些太复杂的操作,比如join,nested,parent-child搜索都要尽量避免,性能都很差的。很多复杂的乱七八糟的一些操作,如何执行,在写入数据的时候,就设计好模型,加几个字段,把处理好的数据写入加的字段里面,自己用java程序封装,es能做的,用es来做,搜索出来的数据,在java程序里面去做,比如说我们,基于es,用java封装一些特别复杂的操作

1.2.2 分片优化

Elasticsearch中的数据组织成索引。每一个索引由一个或多个分片组成。每个分片是Luncene索引的一个实例,你可以把实例理解成自管理的搜索引擎,用于在Elasticsearch集群中对一部分数据进行索引和处理查询。当数据写入分片时,它会定期地发布到磁盘上的新的不可变的Lucene段中,此时它可用于查询。随着分段数(segment)的增长,这些segment被定期地整合到较大的segments。

当索引越来越大,单个 shard 也很巨大,查询速度也越来越慢。这时候,是选择分索引还是更多的shards?更多的 shards 会带来额外的索引压力,即 IO 压力。我们应尽量选择分索引。比如按照每个大分类一个索引,或者主要的大城市一个索引。然后将他们进行合并查询。

分片是Elasticsearch在集群周围分发数据的单位。Elasticsearch在重新平衡数据时 (例如 发生故障后) 移动分片的速度取决于分片的大小和数量以及网络和磁盘性能。在优化过程中,应避免有非常大的分片,因为大的分片可能会对集群从故障中恢复的能力产生负面影响。 对于多大的分片没有固定的限制,但是分片大小为50GB通常被界定为适用于各种用例的限制。

Es 中一个分片一般设置多大合适呢?ES 的每个分片(shard)都是lucene的一个index,而lucene的一个index只能存储20亿个文档,所以一个分片也只能最多存储20亿个文档。 另外,我们也建议一个分片的大小在10G-50G之间,太大的话查询时会比较慢,另外在做副本修复的时,耗时比较多;分片太小的话,会导致一个索引的分片数目很多,查询时带来的fanin-fanout 太大。

经测试2000万的空间数据大约为80G,我们的目标是8000万数据量,总大小约为360G,因此每个分片估算30G,设置12个分片为合适值。

1.2.3 索引优化

索引速度提高与否?主要是看瓶颈在什么地方,若是 Read DB(产生DOC)的速度比较慢,那瓶颈不在 ElasticSearch 时,优化就没那么大的动力。实际上 Elasticsearch 的索引速度还是非常快的。

若是 Elasticsearch 普通索引IO 压力已经见顶,这时候bulk也无法提供帮助,SSD 应该是很好的选择。在 create doc 速度能跟上的时候,bulk是可以提高速度的。

ü SSD选择:SSD是经济压力能承受情况下的不二选择。减少碎片也可以提高索引速度,每天进行优化还是很有必要的。

ü Replica设置:在初次索引的时候,把 replica 设置为 0,也能提高索引速度,一般我们在进行大量数据的同步任务和加载的时候,可以先设置index.refresh_interval=-1,index.number_of_replicas=0,关闭自动刷新并将索引的副本数设置为0。待完成数据同步后,再调整回正常值。另外设置正确的副本数能够提高吞吐量,正确的副本数量是多少?如果您的集群有 num_nodes 个节点、num_primaries 主分片,并且您希望最多同时处理 max_failures 个节点故障,那么适合您的副本数为 max(max_failures, ceil(num_nodes / num_primaries) - 1)。

ü 足够的内存:分配足够的内存给文件系统缓存,文件系统缓存将用于缓冲 I/O 操作。应该确保将运行 Elasticsearch 的计算机的至少一半内存提供给文件系统缓存。

ü 使用自动生成ID:在索引具有显式 id 的文档时,Elasticsearch 需要检查具有相同 id 的文档是否已存在于同一个分片中,这是一项代价高昂的操作,并且随着索引的增长而变得更加昂贵。通过使用自动生成的 id,Elasticsearch 可以跳过此检查,从而加快索引速度。

ü 索引缓冲区:索引缓冲大小index_buffer_size,索引缓冲区index.memory.index_buffer_size默认值是 10%,例如,如果 JVM 10GB 的内存,它将为索引缓冲区提供 1GB。

ü 使用跨集群复制,避免争抢资源

ü 禁用你不需要的特性:不作为查询条件的属性,可以添加在mapping中声明:"index": false;text类型字段如果你只匹配而不关注匹配的分数,可以将类型声明为match_only_text,此字段类型通过删除评分和位置信息来节省大量空间。

ü 不要使用默认的动态字符串映射: 默认的动态字符串映射会将字符串字段同时作为text和keyword进行索引。 如果您只需要其中之一,这是一种浪费。 通常,id 字段只需要作为关键字索引,而 body 字段只需要作为文本字段索引。

ü 禁用 _source: _source 字段存储文档的原始 JSON 正文。 如果您不需要访问它,您可以禁用它。 但是,需要访问 _source 的 API(例如 update 和 reindex)将不起作用。

ü 压缩器best_compression: _source 和 stored 字段可以很容易地占用不可忽略的磁盘空间量。 可以使用 best_compression 编解码器更积极地压缩它们。

1.2.4 查询优化

ü Routing关键:查询关键是什么,routing,routing,还是 routing。为了提高查询速度,减少慢查询,结合自己的业务实践,使用多个集群,每个集群使用不同的 routing。比如,用户是一个routing维度。

ü 预处理:预处理索引数据,减少查询过程中的计算消耗,优先考虑将索引的mapping中的标识符字段(如id字段)设置为keyword类型,numeric 类型适合范围查询range queries,keyword 类型适合等值查询term queries。

ü 预热文件系统缓存:如果重新启动运行 Elasticsearch 的机器,文件系统缓存将是空的,因此操作系统将索引的热点区域加载到内存中需要一些时间,以便快速搜索操作。 您可以使用 index.store.preload 设置根据文件扩展名明确告诉操作系统哪些文件应该立即加载到内存中。

ü 设置索引存储时的排序方式加快连接查询性能。

ü 数据预热:对于那些你觉得比较热的,经常会有人访问的数据,最好做一个专门的缓存预热子系统,就是对热数据,每隔一段时间,你就提前访问一下,让数据进入filesystem cache里面去。这样期待下次别人访问的时候,一定性能会好一些。

ü 冷热分离:将冷数据写入一个索引中,然后热数据写入另外一个索引中,这样可以确保热数据在被预热之后,尽量都让他们留在filesystem os cache里,别让冷数据给冲刷掉。假设你有6台机器,2个索引,一个放冷数据,一个放热数据,每个索引3个shard,3台机器放热数据index;另外3台机器放冷数据index,然后这样的话,你大量的时候是在访问热数据index,热数据可能就占总数据量的10%,此时数据量很少,几乎全都保留在filesystem cache里面了,就可以确保热数据的访问性能是很高的。对于冷数据而言,是在别的index里的,跟热数据index都不再相同的机器上,大家互相之间都没什么联系了。如果有人访问冷数据,可能大量数据是在磁盘上的,此时性能差点,就10%的人去访问冷数据;90%的人在访问热数据。

1.2.5 分页优化

es的分页是较坑的,为啥呢?举个例子吧,假如你每页是10条数据,你现在要查询第100页,实际上是会把每个shard上存储的前1000条数据都查到一个协调节点上,如果你有个5个shard,那么就有5000条数据,接着协调节点对这5000条数据进行一些合并、处理,再获取到最终第100页的10条数据。

分布式的,你要查第100页的10条数据,你是不可能说从5个shard,每个shard就查2条数据?最后到协调节点合并成10条数据?你必须得从每个shard都查1000条数据过来,然后根据你的需求进行排序、筛选等等操作,最后再次分页,拿到里面第100页的数据。

你翻页的时候,翻的越深,每个shard返回的数据就越多,而且协调节点处理的时间越长。非常坑爹。所以用es做分页的时候,你会发现越翻到后面,就越是慢。

因此我们应尽量避免深度分页/默认深度分页性能很惨,可以实现类似于app里的推荐商品不断下拉出来一页一页的。scroll会一次性给你生成所有数据的一个快照,然后每次翻页就是通过游标移动,获取下一页下一页这样子,性能会比上面说的那种分页性能也高很多很多

,

免责声明:本文仅代表文章作者的个人观点,与本站无关。其原创性、真实性以及文中陈述文字和内容未经本站证实,对本文以及其中全部或者部分内容文字的真实性、完整性和原创性本站不作任何保证或承诺,请读者仅作参考,并自行核实相关内容。文章投诉邮箱:anhduc.ph@yahoo.com

    分享
    投诉
    首页