前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Elasticsearch 学习总结 - 相关配置补充说明

Elasticsearch 学习总结 - 相关配置补充说明

作者头像
洗尽了浮华
发布2019-05-25 16:50:48
1.2K0
发布2019-05-25 16:50:48
举报
文章被收录于专栏:散尽浮华散尽浮华

一. Elasticsearch的基本概念

term 索引词,在elasticsearch中索引词(term)是一个能够被索引的精确值。foo,Foo Foo几个单词是不相同的索引词。索引词(term)是可以通过term查询进行准确的搜索。

text 文本是一段普通的非结构化文字,通常,文本会被分析称一个个的索引词,存储在elasticsearch的索引库中,为了让文本能够进行搜索,文本字段需要事先进行分析;当对文本中的关键词进行查询的时候,搜索引擎应该根据搜索条件搜索出原文本。

analysis 分析是将文本转换为索引词的过程,分析的结果依赖于分词器,比如: FOO BAR, Foo-Bar, foo bar这几个单词有可能会被分析成相同的索引词foo和bar,这些索引词存储在elasticsearch的索引库中。当用 FoO:bAR进行全文搜索的时候,搜索引擎根据匹配计算也能在索引库中搜索出之前的内容。这就是elasticsearch的搜索分析。

cluster 代表一个集群,集群中有多个节点,其中有一个为主节点,这个主节点是可以通过选举产生的,主从节点是对于集群内部来说的。es的一个概念就是去中心化,字面上理解就是无中心节点,这是对于集群外部来说的,因为从外部来看es集群,在逻辑上是个整体,你与任何一个节点的通信和与整个es集群通信是等价的。

node 每一个运行实例称为一个节点,每一个运行实例既可以在同一机器上,也可以在不同的机器上。所谓运行实例,就是一个服务器进程,在测试环境中可以在一台服务器上运行多个服务器进程,在生产环境中建议每台服务器运行一个服务器进程。

routing 路由,当存储一个文档的时候,他会存储在一个唯一的主分片中,具体哪个分片是通过散列值的进行选择。默认情况下,这个值是由文档的id生成。如果文档有一个指定的父文档,从父文档ID中生成,该值可以在存储文档的时候进行修改。

index Elasticsearch里的索引概念是名词而不是动词,在elasticsearch里它支持多个索引。 一个索引就是一个拥有相似特征的文档的集合。比如说,你可以有一个客户数据的索引,另一个产品目录的索引,还有一个订单数据的索引。一个索引由一个名字来 标识(必须全部是小写字母的),并且当我们要对这个索引中的文档进行索引、搜索、更新和删除的时候,都要使用到这个名字。在一个集群中,你能够创建任意多个索引。

type 代表类型,在一个索引中,你可以定义一种或多种类型。一个类型是你的索引的一个逻辑上的分类/分区,其语义完全由你来定。通常,会为具有一组相同字段的文档定义一个类型。比如说,我们假设你运营一个博客平台 并且将你所有的数据存储到一个索引中。在这个索引中,你可以为用户数据定义一个类型,为博客数据定义另一个类型,当然,也可以为评论数据定义另一个类型。

document 一个文档是一个可被索引的基础信息单元。比如,你可以拥有某一个客户的文档、某一个产品的一个文档、某个订单的一个文档。文档以JSON格式来表示,而JSON是一个到处存在的互联网数据交互格式。在一个index/type里面,你可以存储任意多的文档。注意,一个文档物理上存在于一个索引之中,但文档必须被索引/赋予一个索引的type。

shards 代表索引分片,es可以把一个完整的索引分成多个分片,这样的好处是可以把一个大的索引拆分成多个,分布到不同的节点上。构成分布式搜索。分片的数量只能在索引创建前指定,并且索引创建后不能更改。

primary shard 主分片,每个文档都存储在一个分片中,当你存储一个文档的时候,系统会首先存储在主分片中,然后会复制到不同的副本中。默认情况下,一个索引有5个主分片。你可以在事先制定分片的数量,当分片一旦建立,分片的数量则不能修改。

replica shard 副本分片,每一个分片有零个或多个副本。副本主要是主分片的复制,其中有两个目的: -> 增加高可用性:当主分片失败的时候,可以从副本分片中选择一个作为主分片。 -> 提高性能:当查询的时候可以到主分片或者副本分片中进行查询。默认情况下,一个主分配有一个副本,但副本的数量可以在后面动态的配置增加。副本必须部署在不同的节点上,不能部署在和主分片相同的节点上。

template 索引可使用预定义的模板进行创建,这个模板称作Index templates。模板设置包括settings和mappings。

mapping 映射像关系数据库中的表结构,每一个索引都有一个映射,它定义了索引中的每一个字段类型,以及一个索引范围内的设置。一个映射可以事先被定义,或者在第一次存储文档的时候自动识别。

field 一个文档中包含零个或者多个字段,字段可以是一个简单的值(例如字符串、整数、日期),也可以是一个数组或对象的嵌套结构。字段类似于关系数据库中的表中的列。每个字段都对应一个字段类型,例如整数、字符串、对象等。字段还可以指定如何分析该字段的值。

source field 默认情况下,你的原文档将被存储在_source这个字段中,当你查询的时候也是返回这个字段。这允许您可以从搜索结果中访问原始的对象,这个对象返回一个精确的json字符串,这个对象不显示索引分析后的其他任何数据。

id id是一个文件的唯一标识,如果在存库的时候没有提供id,系统会自动生成一个id,文档的index/type/id必须是唯一的。

recovery 代表数据恢复或叫数据重新分布,es在有节点加入或退出时会根据机器的负载对索引分片进行重新分配,挂掉的节点重新启动时也会进行数据恢复。

River 代表es的一个数据源,也是其它存储方式(如:数据库)同步数据到es的一个方法。它是以插件方式存在的一个es服务,通过读取river中的数据并把它索引到es中,官方的river有couchDB的,RabbitMQ的,Twitter的,Wikipedia的,river这个功能将会在后面的文件中重点说到。

gateway 代表es索引的持久化存储方式,es默认是先把索引存放到内存中,当内存满了时再持久化到硬盘。当这个es集群关闭再重新启动时就会从gateway中读取索引数据。es支持多种类型的gateway,有本地文件系统(默认),分布式文件系统,Hadoop的HDFS和amazon的s3云存储服务。

discovery.zen 代表es的自动发现节点机制,es是一个基于p2p的系统,它先通过广播寻找存在的节点,再通过多播协议来进行节点之间的通信,同时也支持点对点的交互。

Transport 代表es内部节点或集群与客户端的交互方式,默认内部是使用tcp协议进行交互,同时它支持http协议(json格式)、thrift、servlet、memcached、zeroMQ等的传输协议(通过插件方式集成)。

二、Elasticsearch 的配置文件详细说明

elasticsearch的配置文件是在elasticsearch目录下的config文件下的elasticsearch.yml,同时它的日志文件在elasticsearch目录下的logs,由于elasticsearch的日志也是使用log4j来写日志的,所以其配置模式与log4j基本相同。

Cluster部分 cluster.name: kevin-elk (默认值:elasticsearch) cluster.name可以确定你的集群名称,当你的elasticsearch集群在同一个网段中elasticsearch会自动的找到具有相同cluster.name 的elasticsearch服务。所以当同一个网段具有多个elasticsearch集群时cluster.name就成为同一个集群的标识。

Node部分 node.name: "elk-node01"  节点名,可自动生成也可手动配置。 node.master: true (默认值:true)  允许一个节点是否可以成为一个master节点,es是默认集群中的第一台机器为master,如果这台机器停止就会重新选举master。 node.client  当该值设置为true时,node.master值自动设置为false,不参加master选举。 node.data: true (默认值:true)  允许该节点存储数据。 node.rack  无默认值,为节点添加自定义属性。 node.max_local_storage_nodes: 1 (默认值:1) 设置能运行的节点数目,一般采用默认的1即可,因为我们一般也只在一台机子上部署一个节点。

配置文件中给出了三种配置高性能集群拓扑结构的模式,如下: -> workhorse:如果想让节点从不选举为主节点,只用来存储数据,可作为负载器 node.master: false node.data: true -> coordinator:如果想让节点成为主节点,且不存储任何数据,并保有空闲资源,可作为协调器 node.master: true node.data: false -> search load balancer:(fetching data from nodes, aggregating results, etc.理解为搜索的负载均衡节点,从其他的节点收集数据或聚集后的结果等),客户端节点可以直接将请求发到数据存在的节点,而不用查询所有的数据节点,另外可以在它的上面可以进行数据的汇总工作,可以减轻数据节点的压力。 node.master: false node.data: false

另外配置文件提到了几种监控es集群的API或方法: Cluster Health API:http://127.0.0.1:9200/_cluster/health Node Info API:http://127.0.0.1:9200/_nodes

还有图形化工具: https://www.elastic.co/products/marvel https://github.com/karmi/elasticsearch-paramedic https://github.com/hlstudio/bigdesk https://github.com/mobz/elasticsearch-head

Indices部分

代码语言:javascript
复制
index.number_of_shards: 5 (默认值为5)    设置默认索引分片个数。
index.number_of_replicas: 1(默认值为1)    设置索引的副本个数

服务器够多,可以将分片提高,尽量将数据平均分布到集群中,增加副本数量可以有效的提高搜索性能。 需要注意: "number_of_shards" 是索引创建后一次生成的,后续不可更改设置 "number_of_replicas" 是可以通过update-index-settings API实时修改设置。

Indices Circuit Breaker elasticsearch包含多个circuit breaker来避免操作的内存溢出。每个breaker都指定可以使用内存的限制。另外有一个父级breaker指定所有的breaker可以使用的总内存

代码语言:javascript
复制
indices.breaker.total.limit  所有breaker使用的内存值,默认值为 JVM 堆内存的70%,当内存达到最高值时会触发内存回收。

Field data circuit breaker 允许elasticsearch预算待加载field的内存,防止field数据加载引发异常

代码语言:javascript
复制
indices.breaker.fielddata.limit     field数据使用内存限制,默认为JVM 堆的60%。
indices.breaker.fielddata.overhead  elasticsearch使用这个常数乘以所有fielddata的实际值作field的估算值。默认为 1.03。

请求断路器(Request circuit breaker) 允许elasticsearch防止每个请求的数据结构超过了一定量的内存

代码语言:javascript
复制
indices.breaker.request.limit     request数量使用内存限制,默认为JVM堆的40%。
indices.breaker.request.overhead   elasticsearch使用这个常数乘以所有request占用内存的实际值作为最后的估算值。默认为 1。

Indices Fielddata cache 字段数据缓存主要用于排序字段和计算聚合。将所有的字段值加载到内存中,以便提供基于文档快速访问这些值

代码语言:javascript
复制
indices.fielddata.cache.size:unbounded 
设置字段数据缓存的最大值,值可以设置为节点堆空间的百分比,例:30%,可以值绝对值,例:12g。默认为无限。
该设置是静态设置,必须配置到集群的每个节点。

Indices Node query cache query cache负责缓存查询结果,每个节点都有一个查询缓存共享给所有的分片。缓存实现一个LRU驱逐策略:当缓存使用已满,最近最少使用的数据将被删除,来缓存新的数据。query cache只缓存过滤过的上下文

代码语言:javascript
复制
indices.queries.cache.size
查询请求缓存大小,默认为10%。也可以写为绝对值,例:512m。
该设置是静态设置,必须配置到集群的每个数据节点。

Indexing Buffer 索引缓冲区用于存储新索引的文档。缓冲区写满,缓冲区的文件才会写到硬盘。缓冲区划分给节点上的所有分片。 Indexing Buffer的配置是静态配置,必须配置都集群中的所有数据节点

代码语言:javascript
复制
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。

Indices Shard request cache 当一个搜索请求是对一个索引或者多个索引的时候,每一个分片都是进行它自己内容的搜索然后把结果返回到协调节点,然后把这些结果合并到一起统一对外提供。分片缓存模块缓存了这个分片的搜索结果。这使得搜索频率高的请求会立即返回。

注意:请求缓存只缓存查询条件 size=0的搜索,缓存的内容有hits.total, aggregations, suggestions,不缓存原始的hits。通过now查询的结果将不缓存。 缓存失效:只有在分片的数据实际上发生了变化的时候刷新分片缓存才会失效。刷新的时间间隔越长,缓存的数据越多,当缓存不够的时候,最少使用的数据将被删除。

缓存过期可以手工设置,例如:

代码语言:javascript
复制
curl -XPOST 'localhost:9200/kimchy,elasticsearch/_cache/clear?request_cache=true'

默认情况下缓存未启用,但在创建新的索引时可启用,例如:

代码语言:javascript
复制
curl -XPUT localhost:9200/my_index -d'
{
  "settings": {
    "index.requests.cache.enable": true
  }
} 
'

当然也可以通过动态参数配置来进行设置:

代码语言:javascript
复制
curl -XPUT localhost:9200/my_index/_settings -d'
{ "index.requests.cache.enable": true }
'

每请求启用缓存,查询字符串参数request_cache可用于启用或禁用每个请求的缓存。例如:

代码语言:javascript
复制
curl 'localhost:9200/my_index/_search?request_cache=true' -d'
{
  "size": 0,
  "aggs": {
    "popular_colors": {
      "terms": {
        "field": "colors"
      }
    }
  }
}
'

注意:如果你的查询使用了一个脚本,其结果是不确定的(例如,它使用一个随机函数或引用当前时间)应该设置 request_cache=false 禁用请求缓存。

缓存key,数据的缓存是整个JSON,这意味着如果JSON发生了变化 ,例如如果输出的顺序顺序不同,缓存的内容江将会不同。不过大多数JSON库对JSON键的顺序是固定的。

分片请求缓存是在节点级别进行管理的,并有一个默认的值是JVM堆内存大小的1%,可以通过配置文件进行修改。 例如: indices.requests.cache.size: 2%

分片缓存大小的查看方式:

代码语言:javascript
复制
curl 'localhost:9200/_stats/request_cache?pretty&human'

或者

代码语言:javascript
复制
curl 'localhost:9200/_nodes/stats/indices/request_cache?pretty&human'

Indices Recovery

代码语言:javascript
复制
indices.recovery.concurrent_streams  限制从其它分片恢复数据时最大同时打开并发流的个数。默认为 3。
indices.recovery.concurrent_small_file_streams  从其他的分片恢复时打开每个节点的小文件(小于5M)流的数目。默认为 2。
indices.recovery.file_chunk_size  默认为 512kb。
indices.recovery.translog_ops  默认为 1000。
indices.recovery.translog_size  默认为 512kb。
indices.recovery.compress  恢复分片时,是否启用压缩。默认为 true。
indices.recovery.max_bytes_per_sec  限制从其它分片恢复数据时每秒的最大传输速度。默认为 40mb。

Indices TTL interval

代码语言:javascript
复制
indices.ttl.interval 允许设置多久过期的文件会被自动删除。默认值是60s。
indices.ttl.bulk_size 设置批量删除请求的数量。默认值为1000。

Paths部分

代码语言:javascript
复制
path.conf: /path/to/conf  配置文件存储位置。
path.data: /path/to/data  数据存储位置,索引数据可以有多个路径,使用逗号隔开。
path.work: /path/to/work  临时文件的路径 。
path.logs: /path/to/logs  日志文件的路径 。
path.plugins: /path/to/plugins  插件安装路径 。

Memory部分 bootstrap.mlockall: true(默认为false) 锁住内存,当JVM进行内存转换的时候,es的性能会降低,所以可以使用这个属性锁住内存。同时也要允许elasticsearch的进程可以锁住内存,linux下可以通过`ulimit -l unlimited`命令,或者在/etc/sysconfig/elasticsearch文件中取消 MAX_LOCKED_MEMORY=unlimited 的注释即可。如果使用该配置则ES_HEAP_SIZE必须设置,设置为当前可用内存的50%,最大不能超过31G,默认配置最小为256M,最大为1G。

可以通过请求查看mlockall的值是否设定:

代码语言:javascript
复制
curl http://localhost:9200/_nodes/process?pretty

如果mlockall的值是false,则设置失败。可能是由于elasticsearch的临时目录(/tmp)挂载的时候没有可执行权限。 可以使用下面的命令来更改临时目录:

代码语言:javascript
复制
./bin/elasticsearch -Djna.tmpdir=/path/to/new/dir

Network 、Transport and HTTP 部分

network.bind_host 设置绑定的ip地址,可以是ipv4或ipv6的。

network.publish_host 设置其它节点和该节点交互的ip地址,如果不设置它会自动设置,值必须是个真实的ip地址。

network.host 同时设置bind_host和publish_host两个参数,值可以为网卡接口、127.0.0.1、私有地址以及公有地址。

http_port 接收http请求的绑定端口。可以为一个值或端口范围,如果是一个端口范围,节点将绑定到第一个可用端口。默认为:9200-9300。

transport.tcp.port 节点通信的绑定端口。可以为一个值或端口范围,如果是一个端口范围,节点将绑定到第一个可用端口。默认为:9300-9400。

transport.tcp.connect_timeout 套接字连接超时设置,默认为 30s。

transport.tcp.compress 设置为true启用节点之间传输的压缩(LZF),默认为false。

transport.ping_schedule 定时发送ping消息保持连接,默认transport客户端为5s,其他为-1(禁用)。

httpd.enabled 是否使用http协议提供服务。默认为:true(开启)。

http.max_content_length 最大http请求内容。默认为100MB。如果设置超过100MB,将会被MAX_VALUE重置为100MB。

http.max_initial_line_length http的url的最大长度。默认为:4kb。

http.max_header_size http中header的最大值。默认为8kb。

http.compression 支持压缩(Accept-Encoding)。默认为:false。

http.compression_level 定义压缩等级。默认为:6。

http.cors.enabled 启用或禁用跨域资源共享。默认为:false。

http.cors.allow-origin 启用跨域资源共享后,默认没有源站被允许。在//中填写域名支持正则,例如 /https?:\/\/localhost(:[0-9]+)?/。 * 是有效的值,但是开放任何域名的跨域请求被认为是有安全风险的elasticsearch实例。

http.cors.max-age 浏览器发送‘preflight’OPTIONS-request 来确定CORS设置。max-age 定义缓存的时间。默认为:1728000 (20天)。

http.cors.allow-methods 允许的http方法。默认为OPTIONS、HEAD、GET、POST、PUT、DELETE。

http.cors.allow-headers 允许的header。默认 X-Requested-With, Content-Type, Content-Length。

http.cors.allow-credentials 是否允许返回Access-Control-Allow-Credentials头部。默认为:false。

http.detailed_errors.enabled 启用或禁用输出详细的错误信息和堆栈跟踪响应输出。默认为:true。

http.pipelining 启用或禁用http管线化。默认为:true。

http.pipelining.max_events 一个http连接关闭之前最大内存中的时间队列。默认为:10000。

Discovery部分

discovery.zen.minimum_master_nodes: 3 预防脑裂(split brain)通过配置大多数节点(总节点数/2+1)。默认为3。

discovery.zen.ping.multicast.enabled: false 设置是否打开组播发现节点。默认false。

discovery.zen.ping.unicast.host 单播发现所使用的主机列表,可以设置一个属组,或者以逗号分隔。每个值格式为 host:port 或 host(端口默认为:9300)。默认为 127.0.0.1,[::1]。

discovery.zen.ping.timeout: 3s 设置集群中自动发现其它节点时ping连接超时时间,默认为3秒,对于比较差的网络环境可以高点的值来防止自动发现时出错。

discovery.zen.join_timeout 节点加入到集群中后,发送请求到master的超时时间,默认值为ping.timeout的20倍。

discovery.zen.master_election.filter_client:true 当值为true时,所有客户端节点(node.client:true或node.date,node.master值都为false)将不参加master选举。默认值为:true。

discovery.zen.master_election.filter_data:false 当值为true时,不合格的master节点(node.data:true和node.master:false)将不参加选举。默认值为:false。

discovery.zen.fd.ping_interval 发送ping监测的时间间隔。默认为:1s。

discovery.zen.fd.ping_timeout ping的响应超时时间。默认为30s。

discovery.zen.fd.ping_retries ping监测失败、超时的次数后,节点连接失败。默认为3。

discovery.zen.publish_timeout 通过集群api动态更新设置的超时时间,默认为30s。

discovery.zen.no_master_block 设置无master时,哪些操作将被拒绝。all 所有节点的读、写操作都将被拒绝。write 写操作将被拒绝,可以读取最后已知的集群配置。默认为:write。

Gateway部分

gateway.expected_nodes: 0 设置这个集群中节点的数量,默认为0,一旦这N个节点启动,就会立即进行数据恢复。

gateway.expected_master_nodes 设置这个集群中主节点的数量,默认为0,一旦这N个节点启动,就会立即进行数据恢复。

gateway.expected_data_nodes 设置这个集群中数据节点的数量,默认为0,一旦这N个节点启动,就会立即进行数据恢复。

gateway.recover_after_time: 5m 设置初始化数据恢复进程的超时时间,默认是5分钟。

gateway.recover_after_nodes 设置集群中N个节点启动时进行数据恢复。

gateway.recover_after_master_nodes 设置集群中N个主节点启动时进行数据恢复。

gateway.recover_after_data_nodes 设置集群中N个数据节点启动时进行数据恢复。

三. Elasticsearch常用插件

1) elasticsearch-head 插件 一个elasticsearch的集群管理工具,它是完全由html5编写的独立网页程序,你可以通过插件把它集成到es。 项目地址:https://github.com/mobz/elasticsearch-head

插件安装方法1

代码语言:javascript
复制
elasticsearch/bin/plugin install mobz/elasticsearch-head
重启elasticsearch
打开http://localhost:9200/_plugin/head/

插件安装方法2

代码语言:javascript
复制
根据地址https://github.com/mobz/elasticsearch-head 下载zip解压
建立elasticsearch/plugins/head/_site文件
将解压后的elasticsearch-head-master文件夹下的文件copy到_site
重启elasticsearch
打开http://localhost:9200/_plugin/head/

2) bigdesk插件 elasticsearch的一个集群监控工具,可以通过它来查看es集群的各种状态,如:cpu、内存使用情况,索引数据、搜索情况,http连接数等。 项目地址: https://github.com/hlstudio/bigdesk

插件安装方法1

代码语言:javascript
复制
elasticsearch/bin/plugin install hlstudio/bigdesk
重启elasticsearch
打开http://localhost:9200/_plugin/bigdesk/ 

插件安装方法2

代码语言:javascript
复制
https://github.com/hlstudio/bigdesk下载zip 解压
建立elasticsearch-1.0.0\plugins\bigdesk\_site文件
将解压后的bigdesk-master文件夹下的文件copy到_site
重启elasticsearch
打开http://localhost:9200/_plugin/bigdesk/ 

3) Kopf 插件 一个ElasticSearch的管理工具,它也提供了对ES集群操作的API。 项目地址:https://github.com/lmenezes/elasticsearch-kopf

插件安装方法

代码语言:javascript
复制
elasticsearch/bin/plugin install lmenezes/elasticsearch-kopf
重启elasticsearch
打开http://localhost:9200/_plugin/kopf/

四. Elasticsearch 的fielddata内存控制、预加载以及circuit breaker断路器

fielddata核心原理 fielddata加载到内存的过程是lazy加载的,对一个analzyed field执行聚合时,才会加载,而且是field-level加载的一个index的一个field,所有doc都会被加载,而不是少数doc不是index-time创建,是query-time创建

fielddata内存限制 elasticsearch.yml: indices.fielddata.cache.size: 20%,超出限制,清除内存已有fielddata数据fielddata占用的内存超出了这个比例的限制,那么就清除掉内存中已有的fielddata数据默认无限制,限制内存使用,但是会导致频繁evict和reload,大量IO性能损耗,以及内存碎片和gc

监控fielddata内存使用

代码语言:javascript
复制
#各个分片、索引的fielddata在内存中的占用情况
[root@elk-node03 ~]# curl -X GET 'http://10.0.8.47:9200/_stats/fielddata?fields=*'     

#每个node的fielddata在内存中的占用情况
[root@elk-node03 ~]# curl -X GET 'http://10.0.8.47:9200/_nodes/stats/indices/fielddata?fields=*'

#每个node中的每个索引的fielddata在内存中的占用情况
[root@elk-node03 ~]# curl -X GET 'http://10.0.8.47:9200/_nodes/stats/indices/fielddata?level=indices&fields=*'

circuit breaker断路由 如果一次query load的feilddata超过总内存,就会oom --> 内存溢出; circuit breaker会估算query要加载的fielddata大小,如果超出总内存,就短路,query直接失败; 在elasticsearch.yml文件中配置如下内容: indices.breaker.fielddata.limit: fielddata的内存限制,默认60% indices.breaker.request.limit: 执行聚合的内存限制,默认40% indices.breaker.total.limit: 综合上面两个,限制在70%以内

限制内存使用 (Elasticsearch聚合限制内存使用)

通常为了让聚合(或者任何需要访问字段值的请求)能够快点,访问fielddata一定会快点, 这就是为什么加载到内存的原因。但是加载太多的数据到内存会导致垃圾回收(gc)缓慢, 因为JVM试着发现堆里面的额外空间,甚至导致OutOfMemory (即OOM)异常。

然而让人吃惊的发现, Elaticsearch不是只把符合你的查询的值加载到fielddata. 而是把index里的所document都加载到内存,甚至是不同的 _type 的document。逻辑是这样的,如果你在这个查询需要访问documents X,Y和Z, 你可能在下一次查询就需要访问别documents。而一次把所有的值都加载并保存在内存 , 比每次查询都去扫描倒排索引要更方便。

JVM堆是一个有限制的资源需要聪明的使用。有许多现成的机制去限制fielddata对堆内存使用的影响。这些限制非常重要,因为滥用堆将会导致节点的不稳定(多亏缓慢的垃圾回收)或者甚至节点死亡(因为OutOfMemory异常);但是垃圾回收时间过长,在垃圾回收期间,ES节点的性能就会大打折扣,查询就会非常缓慢,直到最后超时。

如何设置堆大小 对于环境变量 $ES_HEAP_SIZE 在设置Elasticsearch堆大小的时候有2个法则可以运用:

1) 不超过RAM的50% Lucene很好的利用了文件系统cache,文件系统cache是由内核管理的。如果没有足够的文件系统cache空间,性能就会变差;

2) 不超过32G 如果堆小于32GB,JVM能够使用压缩的指针,这会节省许多内存:每个指针就会使用4字节而不是8字节。把对内存从32GB增加到34GB将意味着你将有更少的内存可用,因为所有的指针占用了双倍的空间。同样,更大的堆,垃圾回收变得代价更大并且可能导致节点不稳定;这个限制主要是大内存对fielddata影响比较大。

Fielddata大小 参数 indices.fielddata.cache.size 控制有多少堆内存是分配给fielddata。当你执行一个查询需要访问新的字段值的时候,将会把值加载到内存,然后试着把它们加入到fielddata。如果结果的fielddata大小超过指定的大小 ,为了腾出空间,别的值就会被驱逐出去。默认情况下,这个参数设置的是无限制 — Elasticsearch将永远不会把数据从fielddata里替换出去。

这个默认值是故意选择的:fielddata不是临时的cache。它是一个在内存里为了快速执行必须能被访问的数据结构,而且构建它代价非常昂贵。如果你每个请求都要重新加载数据,性能就会很差。

一个有限的大小强迫数据结构去替换数据。下面来看看什么时候去设置下面的值,首先看一个警告: 这个设置是一个保护措施,而不是一个内存不足的解决方案!

如果你没有足够的内存区保存你的fielddata到内存里,Elasticsearch将会经常性的从磁盘重新加载数据,并且驱逐别的数据区腾出空间。这种数据的驱逐会导致严重的磁盘I/O,并且在内存里产生大量的垃圾,这个会在后面被垃圾回收。

假设你在索引日志,每天使用给一个新的索引。通常情况下你只会对过去1天或者2天的数据感兴趣。即使你把老的索引数据保留着,你也很少查询它们。尽管如此,使用默认的设置, 来自老索引的fielddata也不会被清除出去!fielddata会一直增长直到它触发fielddata circuit breaker --参考断路器--它将阻止你继续加载fielddata。在那个时候你被卡住了。即使你仍然能够执行访问老的索引里的fielddata的查询, 你再也不能加载任何新的值了。相反,我们应该把老的值清除出去给新的值腾出空间。为了防止这种情景,通过在elasticsearch.yml文件里加上如下的配置给fielddata 设置一个上限: indices.fielddata.cache.size: 40% ,可以设置成堆大小的百分比,也可以是一个具体的值,比如 8gb;通过适当设置这个值,最近被访问的fielddata将被清除出去,给新加载数据腾出空间。

在网上可能还会看到另外一个设置参数: indices.fielddata.cache.expire 千万不要使用这个设置!这个设置高版本已经废弃!!! 这个设置告诉Elasticsearch把比过期时间老的数据从fielddata里驱逐出去,而不管这个值是否被用到。这对性能是非常可怕的 。驱逐数据是有代价的,并且这个有目的的高效的安排驱逐数据并没有任何真正的收获。没有任何理由去使用这个设置!!!! 我们一点也不能从理论上制造一个假设的有用的情景。现阶段存 在只是为了向后兼容。我们在这个书里提到这个设置是因为这个设置曾经在网络上的各种文章里 被作为一个 ``性能小窍门'' 被推荐过。记住永远不要使用它!!!!

监控fielddata (上面提到了) 监控fielddata使用了多少内存以及是否有数据被驱逐是非常重要的。大量的数据被驱逐会导致严重的资源问题以及不好的性能。

Fielddata使用可以通过下面的方式来监控: 对于单个索引使用 {ref}indices-stats.html[indices-stats API]:

代码语言:javascript
复制
[root@elk-node03 ~]# curl -X GET 'http://10.0.8.47:9200/_stats/fielddata?fields=

对于单个节点使用 {ref}cluster-nodes-stats.html[nodes-stats API]:

代码语言:javascript
复制
[root@elk-node03 ~]# curl -X GET 'http://10.0.8.47:9200/_nodes/stats/indices/fielddata?fields=*'

或者甚至单个节点单个索引

代码语言:javascript
复制
[root@elk-node03 ~]# curl -X GET 'http://10.0.8.47:9200/_nodes/stats/indices/fielddata?level=indices&fields=*'

通过设置 ?fields=* 内存使用按照每个字段分解了.

断路器(breaker) fielddata的大小是在数据被加载之后才校验的。如果一个查询尝试加载到fielddata的数据比可用的内存大会发生什么情况?答案是不客观的:你将会获得一个OutOfMemory异常。

Elasticsearch包含了一个 fielddata断路器 ,这个就是设计来处理这种情况的。断路器通过检查涉及的字段(它们的类型,基数,大小等等)来估计查询需要的内存。然后检查加 载需要的fielddata会不会导致总的fielddata大小超过设置的堆的百分比。

如果估计的查询大小超过限制,断路器就会触发并且查询会被抛弃返回一个异常。这个发生在数据被加载之前,这就意味着你不会遇到OutOfMemory异常。

Elasticsearch拥有一系列的断路器,所有的这些都是用来保证内存限制不会被突破: indices.breaker.fielddata.limit 这个 fielddata 断路器限制fielddata的大小为堆大小的60%,默认情况下。

indices.breaker.request.limit 这个 request 断路器估算完成查询的其他部分要求的结构的大小,比如创建一个聚集通, 以及限制它们到堆大小的40%,默认情况下。

indices.breaker.total.limit 这个total断路器封装了 request 和 fielddata 断路器去确保默认情况下这2个 使用的总内存不超过堆大小的70%。

断路器限制可以通过文件 config/elasticsearch.yml 指定,也可以在集群上动态更新:

代码语言:javascript
复制
curl -PUT 'http://10.0.8.47:9200/_cluster/settings{
"persistent" : {
"indices.breaker.fielddata.limit" : 40% (1)
}
}

这个限制设置的是堆的百分比。

最好把断路器设置成一个相对保守的值。记住fielddata需要和堆共享 request 断路器, 索引内存缓冲区,过滤器缓存,打开的索引的Lucene数据结构,以及各种各样别的临时数据 结构。所以默认为相对保守的60%。过分乐观的设置可能会导致潜在的OOM异常,从而导致整 个节点挂掉。从另一方面来说,一个过分保守的值将会简单的返回一个查询异常,这个异常会被应用处理。 异常总比挂掉好。这些异常也会促使你重新评估你的查询:为什么单个的查询需要超过60%的 堆空间。

断路器和Fielddata大小 在 Fielddata大小部分我们谈到了要给fielddata大小增加一个限制去保证老的不使用 的fielddata被驱逐出去。indices.fielddata.cache.size 和 indices.breaker.fielddata.limit 的关系是非常重要的。如果断路器限制比缓冲区大小要小,就会没有数据会被驱逐。为了能够 让它正确的工作,断路器限制必须比缓冲区大小要大。

我们注意到断路器是和总共的堆大小对比查询大小,而不是和真正已经使用的堆内存区比较。 这样做是有一系列技术原因的(比如,堆可能看起来是满的,但是实际上可能正在等待垃圾 回收,这个很难准确的估算)。但是作为终端用户,这意味着设置必须是保守的,因为它是 和整个堆大小比较,而不是空闲的堆比较。

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2019-04-10 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
相关产品与服务
文件存储
文件存储(Cloud File Storage,CFS)为您提供安全可靠、可扩展的共享文件存储服务。文件存储可与腾讯云服务器、容器服务、批量计算等服务搭配使用,为多个计算节点提供容量和性能可弹性扩展的高性能共享存储。腾讯云文件存储的管理界面简单、易使用,可实现对现有应用的无缝集成;按实际用量付费,为您节约成本,简化 IT 运维工作。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档