Elasticsearch学习笔记

6,614 阅读26分钟

前言

  • 为什么es查询和聚合都这么快?底层是如何实现的?
  • 数据在es集群中如何存储的?如何做到自动分布式的?
  • 为什么es的主分片数设置了之后就不能调整,而副本分片数可以调整?
  • 如何优化索引方式和查询方式,有效利用缓存,提高查询效率?
  • 如果保证不停服的情况下,平滑升级或扩容?
  • 如何优化查询效率?

相信看完Elasticsearch权威指南这本书,所有疑问都将得到解答

一. 基本概念

1. 分片

  • 最小级别的工作单元,保存索引中一部分数据。是一个Lucene实例,本身就是一个完整的搜索引擎。但是应用程序不会直接与分片通讯。
  • 可以想象成容器,节点间数据迁移以分片为单位
  • 分为主分片和副分片(主分片的副本)
  • 索引创建的时候,主分片的数量就固定了,但是副本分片数量可调整
  • 默认一个索引分配5个主分片
  • 主分片所在节点挂掉后,重新选举主节点,并将副分片升级为主分片
  • 故障节点重新启动后,会同步故障期间未同步到到数据

2. 文档

  • 根对象序列化成json对象
  • 每次对文档的操作(包括修改,删除),_version都会加一
  • 文档是不可修改的。update是先删除,再新建一个新的
  • 删除的文档并不会被立即移除,只是标记为删除。之后后台再清理
  • 自己设置文档的版本:添加version_type=external参数

3. 冲突解决

  • 通过版本号实现乐观锁解决冲突问题

4. 文档元数据

  • _index 文档存储的地方
  • _type 文档代表的对象的类(7.x的版本将去掉_type)
  • _id 文档的唯一标识。可手动设置也可自动生成(22位长)

5. 集群架构图

两个节点,三个主分片,一个副分片的效果图

扩展到三个节点到效果图

6. 集群状态

集群状态是一个数据结构,集群状态存在每个客户端中。保存以下信息

  • 级别设置
  • 集群节点
  • 索引以及相关的映射,别名等信息
  • 索引的分片,以及分配的节点

集群状态-status

  • green:所有主分片和副分片都已经分配
  • yellow:所有主分片都已分配,至少有一个副本分片没有分配
  • red:至少一个主分片(或全部副分片)缺失

二. 集群工作原理

1. 数据是如何在分布式系统存储的

  • 文档通过路由存放到分片
  • 通过以下算法得出该文档存储时的分片编号
    shard = hash(routing) % number_of_primary_shards
    
  • routing是任意字符串,默认是_id
  • 主分片的数量不可改变,否则之前的路由失效,文档就找不到了
  • 自定义路由可以保证有关联性的文档被保存在同一个分片

2. 主分片和复制分片如何交互?

  • 请求能够被发送给任意节点
  • 每个节点都有能力处理任意请求
  • 每个节点都知道任意文档所在节点(保存的集群状态),并转发请求
  • 发送请求时最好循环每个节点以负载均衡

2.1 write操作(新建、删除、索引)

顺序步骤

  • 客户端发送请求(新建,删除,索引)到node1节点
  • 节点使用hash算法得出分片编号0,因为分片0在节点3,将请求转发到节点3
  • node3成功保存数据到主分片,如果成功,转发请求到node1和node2到副节点
  • 所有复制节点成功,发送成功回复到请求节点1,节点1再返回给客户端
可调的参数
  • replication:默认为sync,主分片得到复制分片成功响应才返回。async表示请求在主分片执行成功就返回,依旧转发请求到副分片,不过不知道成功与否
  • consistency:主分片尝试写入时,需要规定数量(quorum)或过半的分片可用。可为one,all,quorum(默认)。quorum只有在number_of_replicas大于1时才生效
    int((primary[总是1] + number_of_replicas) /2 + 1)
    
  • timeout:分片不足时,等待时间。默认1min

2.2 read操作

  • 客户端发送get请求到node1
  • 节点使用hash算法,得到文档所属到主分片为0分片
  • 找到分片0的副分片所在节点为node1,node2,node3
  • 通过某种策略选定某个副分片节点,比如node2
  • node2返回文档给node1
  • 然后node1返回给客户端

2.3 update操作

顺序步骤

  • 客户端给node1发送更新请求
  • 通过哈希算法得到主分片位置,转发请求到node3
  • node3检索出文档,修改_source字段到json文档,然后重建索引。如果有其他进程修改了文档,它以retry_on_conflict设置的次数重复这一步,都未成功则放弃
  • node3更新成功则发送整个新文档(并不是修改请求)到node1和node2的复制节点重建索引,都成功则返回给node1,再返回给客户端

2.4 多文档模式

mget 多文档read

  • 客户端发送mget请求给node1
  • node1为每个分片构建一个请求,并转发到对应分片所在节点
  • 当所有回复被接收,node1组合这些响应,返回给客户端
bulk 多文档write

  • 客户端向node1发送请求
  • node1为每个分片构建批量请求,然后转发到这些主分片上
  • 主分片按序执行,每一个完成时,发送到副分片上
  • 所有操作都完成后节点整理响应返回给客户端

3. 索引是如何建立的

3.1 基本概念

  • 映射(mapping):用于字段确认,每个字段匹配为确认的数据类型
  • 分析(analysis):全文文本分词,以建立倒排索引
  • 倒排索引:由文档中单词的唯一列表和单词在文档中的位置组成,用于快速检索结果而设计

3.2 分析(analysis)

分析的过程
  • 分析由分析器(analyzer)完成
  • 分析过程先标记一段文本为单独的词(item)
  • 然后标准化(比如全部转为小写)item,以提高搜索性
  • 分析的详情可通过_analyze API查看
分析器包括的组件

es提供很多可用直接使用的组件,可自定义组合使用

  • 字符过滤器(character filter):字符串先经过这做一些过滤操作
  • 分词器(tokenizer):将文本切分为单词,比如空格,逗号等。中文可用专门的分词器
  • 标记过滤器(token filter):修改词语,比如转小写,去掉语气词,增加同义词
内置的分析器
  • 标准分析器:默认使用这个。标准切分,去掉大部分符号,最后转为小写
  • 空格分析器:按空格切分,不转换为小写
  • 语言分析器:根据特定语言的特性做分析
查询方式
  • 字段查询:精确匹配,查询前不会将被查询的字符串分析
  • 全文查询:查询前会先用分析器分析要查询的字符串
手动指定分析器
  • 当往es中加入字符串时,es会自动用标准分析器做分词,但是可能某些字符就是普通的id,标签等字段,不需要做分析,可手动指定映射
创建索引时查找分析器的顺序
  • mapping文件中指定字段的analyzer
  • 文档本身的_analyzer字段
  • mapping文件中指定类型的默认analyzer
  • mapping文件中全局默认的analyzer
  • 节点级别默认的analyzer
  • 标准analyzer
查找索引时查找分析器的顺序
  • 查询参数中的analyzer
  • mapping文件中指定字段的analyzer
  • mapping文件中指定类型的analyzer
  • mapping文件中全局默认的analyzer
  • 节点级别默认的analyzer
  • standard analyzer

3.3 映射

作用

定义字段类型,字段的数据类型以及被es处理的方式。

支持的字段类型
类型 表示的数据类型
String string
Whole Number byte short integer long
Floating point float double
Boolean boolean
Date date

新的字段如果没有配置映射,es会自动猜测字段类型

自定义字段映射可实现的功能
  • 区分全文字符串(需要分词)和精确字符串(不需要分词)
  • 使用特定语言的分析器
  • 优化部分匹配字段
  • 指定自定义日期格式
映射包含的参数
  • properties:列出了可能包含的每个字段的映射
  • 元数据字段:_type, _id, _source
  • dynamic:确定字段添加时的策略(_source会一直保存)
    • ture 自动添加
    • false 忽略字段
    • strict 抛出异常
  • 设置项:如analyzer
  • 其他设置
自定义字段映射注意点
  • 要映射的字段参数为type, 除了string外,很少需要映射其他type
  • string字段的index字段,控制字符串以什么方式被索引。
    含义
    analyzed 分词索引
    not_analyzed 不分词索引
    no 不索引
  • string字段选择anlyzed为index时,analyzer指定分析器。如:simple, english, whitespace
  • 更新映射只能添加字段,不能修改已经被添加的字段。否则会导致出错索引不到
文档字段的属性
  • type
  • index
  • analyzer
  • ip
  • geo_point
  • geo_shape
元数据_source字段
  • 作用: 用于保存原始json字段
  • 为什么需要
    • 搜索结果能得到完整文档
    • 缺少它,部分更新请求不起作用
    • 更新映射文件时,可直接取内容
    • 更易排查错误
  • 怎么禁用:enabled:false
  • 使用:搜索时可以通过_source指定只返回哪些列
元数据_all字段
  • 查询不知道指定哪个字段时,使用_all。也可禁用。使用_all时,会将其他所有字段的值作为一个大的字符串进行索引
动态模版

dynamic_templates 设置通过字段名或类型动态匹配不同的映射

  • match_mapping_type 模版使用的数据类型
  • match 模版使用的字段名
  • path 模版使用的字段全路径(嵌套json)

三. 结构化查询语言

1. 过滤

概述

文档的字段是否包含特定值,比查询更快,结果可缓存

原则上全文索引或者需要其他相关性评分的使用查询语句,其他情况都用过滤。

重要的过滤语句

  • term:精确匹配
  • terms:多个条件的精确匹配
  • range:范围过滤
  • exists:是否包含指定字段
  • missing:没有某个字段
  • bool:合并多个过滤查询结果
    • must:and
    • must_not:not
    • shoud:or

过滤顺序

  • 过滤顺序对性能有很大影响
  • 更详细的过滤条件应该放在最前,以便排除更多的文档
  • 被缓存的过滤应该放到不会缓存的过滤前面(缓存参考后面章节)

2. 查询

简述

每个文档的字段与特定字段的匹配程度如何,比过滤慢,结果不可缓存

重要的查询语句

  • math_all:查询所有文档
  • match:标准查询,全文和精确都支持

    match指定多个值时,内部分词后会执行多个match并放入bool查询。默认为or。可通过operator参数改为“and”

  • multi_match:同时搜索多个字段,支持通配符
  • bool:同bool过滤,多的是要计算_score

3. 相关性排序

排序方式

  • _score:默认排序方式,默认倒序

  • 字段排序:_score不需要计算,默认正序

  • 多级排序:可指定多个字段。先用第一个字段排序,第一个相同时排第二个

  • 字符串参数排序:

    被分析的字段进行强制排序会消耗大量内存

相关性简介

相似度算法:TF/IDF(检索词词频/反向文档频率)
  • TF: 词频,出现在当前文档次数越多,相关性越大
  • IDF:反转文档频率,所有文档数与出现这个词的文件数百分比,词出现频率越大,IDF越小
  • 由于性能问题,每个分片只会计算该分片内的IDF,而不是所有文档
  • boost参数可以设置权重

4. 分布式搜索的执行方式

概述

搜索包括查询多个分片,并将多个分片元信息合并,然后再根据元数据获取真正数据两个步骤。

查询多个索引和查询一个索引完全一致,无非是多查了几个分片。扩展的时候,可以不用将旧数据迁移到新索引,直接新建索引,然后查询两个索引,或者别名索引即可

查询(query)

  • 客户端发送search给node3,创建一个from+size的空优先级队列

  • 广播请求到每个分片,每个分片在本地执行查询,并放到一个大小为from+size的本地优先级队列里

  • 每个节点返回查询结果(id和_score)给node3,node3将结果全局排序

    • 多个请求会轮询所有的分片副本以负载均衡,提高系统吞吐率
    • 多索引的工作机制和单索引类似,只不过多了些分片
    • 深度分页会导致排序过程非常繁重,占用巨大cpu和宽带

取回(fetch)

  • 协调节点辨别出哪些文档需要取回,并向相应分片发送请求
  • 每个分片加载文档,并做相关处理(比如高亮),然后返回给协调节点
  • 协调节点将数据返回给客户端

搜索选项(可选参数)

  • preference:控制使用哪个分片或节点处理请求
  • timeout:协调节点等待多久就放弃其他节点的结果
  • routing:限制只搜索哪些分片,对于大规模系统很有用
  • search_type:query_then_fetch为默认的搜索类型
    • count:当不需要结果,只需要数量时
    • query_and_fetch:查询并且取回
    • dfs_query_and_fetch,dfs_query_then_fetch
    • scan:扫描,和scroll一起使用。可高效取回大量数据。禁用排序实现

扫描和滚屏

scroll

类似传统数据库的游标,搜索的是查询时的索引快照,查询结束之前的修改不会感知到

scan

不排序,只要有结果就返回

四. 分片内部原理

1. 索引动态更新原理

1.1 倒排索引-保证文档可被搜索

1.2 倒排索引的内容是不可变的

1.3 不可变的同时动态添加段

查询的时候,所有段依次查询,然后聚合结果,通过这种方式,新文档以最小代价加入文档

  • 新的文档首先写入内存区的索引缓存
  • buffer中包括新的段包含的倒排索引,段名等
  • buffer被提交
  • 新段被打开,文档可被索引
  • 内存缓存被清除,等待新文档

1.4 删除和更新

因为段不可变,更新和删除操作并不是真的删除,是通过新增.del文件和新建段文件,查询返回前将标记为del的文件从结果中删除

1.5 近实时搜索

因为从buffer刷入磁盘代价很大。es允许一旦一个文件被缓存,就可以设置段打开,文件可以被搜索到

1.6 刷新

每个分片默认每秒打开一个新段,所以新的改动需要1s后才能看到。可以设置refresh_interval减少刷新的频率

1.7 持久化变更

添加缓冲buffer的同时,通过添加事务日志(默认512M),保证数据被完整持久化。每次flush(每30分钟,或事务日志过大)到磁盘时,段被全部提交,清空事务日志

1.8 合并段

通过每秒自动刷新段,不用多久段数据就剧增。每个段消耗计算机资源,且每次查询都要依次检查每个段,段越多查询越慢。es后台合并段解决该问题。 合并大的段会消耗io和cpu资源。

1.9 Optimize API

强制合并段。对于数据不再变动的索引很有效,对数据还在动态增长的索引不要使用。

2. 缓存

概述

  • 缓存针对过滤查询
  • 核心是一个字节集保存哪些文档符合过滤条件
  • 缓存的字节集是增量更新的
  • 每个过滤器都是独立缓存的,且可复用
  • 大部分枝叶过滤器(如term)会被缓存,而组合过滤器(如bool)不会被缓存

不可被缓存的情况

  • 脚本过滤器,脚本对es是不透明的
  • Geo(地址)过滤器,不太会被重用
  • 日期范围精确到毫秒不会被缓存,整数会被缓存

过滤时间范围的使用建议

  • 对于时间精确到毫秒的查询,可拆分为日期+日期时间两个过滤条件,前者会被缓存,且顺序不要改变

五. 全文检索

1. 全文检索包括两个方面

  • 相关度(Relevance):TF/IDF,地理位置相近度,模糊相似度或其他算法
  • 分析(Analysis):分词,创建倒排索引

2. 全文查询分类

  • 低级查询:term查询。没有分析阶段,会精确匹配特定短语
  • 全文检索:match,query_string等查询。有分析阶段。
    • date,integer类型:精确查询
    • not_analyzed的string类型:分析查询词语(比如转小写),执行单个短语查询
    • analyzed的string类型:先解析查询语句,生成短语列表。查询后再合并查询结果

六. 聚合

1. 基本概念

桶(buckets)

满足特定条件的文档的集合。类似于sql里面的group by

指标(metrics)

对桶内的文档进行统计计算。类似sql里面的count,sum,max等统计方法

2. 近似聚合

2.1 概述

  • 分布式算法三个因子模型同时只能选择满足两项:精确,实时,大数据
  • ea选择大数据和实时。会提供准确但不是100%精确的结果,以牺牲一点小的估算错误作为代价,换来告诉的执行效率和极小的内存消耗
  • 两个近似算法:cardinality, percentiles

2.2 cardinality 基数度量

  • 类似sql的distinct
  • 是一个近似算法。基于HyperLogLot++(HLL)算法的。HLL先对输入做哈希运算,根据hash运算的记过中的bits做概率估算得到基数。HLL 论文
  • 算法特性
    • 可配置精度:参数为precision_threshold (精度更高=跟多内存)
    • precision_threshold范围为0-4000,数据结构使用内存为:precision_threshold * 8
    • 设置为100时,可保证百万级别的数据量误差维持5%以内
    • 小的数据集精度非常高
    • 可配置使用的固定内存量
  • 优化:预先计算hash值,不过性能的瓶颈由聚合时转移到索引时(必须重新建索引,添加hash字段),需要根据业务场景来确定。

2.3 percentiles 百分位数度量

  • 展现了以某个具体百分比执行观察到的数值,通常用于找出异常。
  • 也是一个近似算法。使用TDigest算法
  • 算法特性
    • 极端百分比的情况下,数据更准确。比如1%或99%。这由数据结构决定。
    • 小数据集精度非常准确

3. significant_terms

  • sigterms和其他聚合不同,用于发现数据集中医学异常指标
  • 通过统计数据并对比正常数据找到可能有异常频次的指标

4. 聚合的数据结构

4.1 Doc Values

  • 聚合,排序使用Doc Values的数据结构

  • 将文档映射到他们包含的词项

  • 在索引时和倒排索引同时生成。基于segment且不可变。

  • Doc values数据存放到磁盘中,不是由jvm管理。

  • 采用列式存储,数据整齐排布,便于压缩

  • 不支持analyzed字段

  • 除了analyzed的字符串,默认所有字段都开启doc values。如果你永远不会对某些字段进行聚合,排序操作,可以禁用doc values。可以节省磁盘空间和索引速度

4.2 Fielddata

  • anaylzed的字符串,使用Fielddata这种数据结构支持聚合,fielddata存储在内存堆中,旧版本没有doc values时是用的fielddata
  • anaylzed的过程会消耗极大内存,且生成大量token,对聚合很不友好
  • fieldata会一直存在内存中,直到被驱逐或节点崩溃。注意观察它的大小。
  • dielddata不会在建索引时存在,是查询时建立的
  • indices.fielddata.cache.size:百分比或实际大小。 控制为 fielddata 分配的堆空间大小。每次聚合查询时,分析字段会加载到Fielddata中,如果查询结果中 fielddata 大小超过了指定的大小 ,其他的值将会被回收从而获得空间。
  • 如果没有足够空间可以将 fielddata 保留在内存中,Elasticsearch 就会时刻从磁盘重载数据,并回收其他数据以获得更多空间。内存的回收机制会导致重度磁盘I/O,并且在内存中生成很多垃圾,这些垃圾必须在晚些时候被回收掉。
  • 监控filddata: GET /_stats/fielddata?fields=*

5. 聚合优化

  • 针对大量数据的嵌套聚合查询,效率极低。默认的查询方式为深度优先。
  • 可以使用广度优先处理聚合数量远远小于总组数的情况。参数为collect_mode: breadth_first

七. 地理位置

1. 设置字段类型为地理位置

地理坐标点不能被动态映射字段检测,需要显式申明对应字段类型(type参数)为geo_point

2. geo_point格式

  • 字符串: "40.715, -74.011", 维度在前,精度在后
  • 数组: [40.715, -74.011], 维度在前,精度在后
  • 对象: {"lat": 40.715, "lon": -74.011}

3. 过滤方式

  • geo_bounding_box :: 落在指定矩形框中的坐标点
  • geo_distance :: 给定距离内的点
  • geo_distance_range :: 距离范围内的点
  • geo_polygon :: 落在多边形中的点

4. 使用注意

  • 地理坐标过滤器使用代价很高,它会将所有文档的地理位置信息载入内存,然后计算。使用时谨慎,或放到过滤的最后
  • bool过滤器默认会将地理信息过滤排到最后
  • 默认是不被缓存的
  • 每个经纬度组合需要16自己的内存,可设置压缩格式,减少精度,减少内存
  • 合理设置精度:geohash_prefix和geohash_precision两个参数。再结合geohash过滤器可高效查询

5. geohash

  • 把世界分为4*8=32个单元的各自,每一个格子用一个字母或数字标识。这些单元有被继续分解成32个更小的单元,不断重复
  • 长度越长,精度越高
  • 有同一前缀的geohash,位置靠的近,共同前缀越长,距离越近
  • 刚好相邻的位置也有可能geohash完全不同

6. 地理位置聚合

  • geo_distance 距离聚合:将文档以指定中心店为圆心的圆环分组
  • geohash_grid网格聚合:将文档按geohash单元分组,以便在地图上呈现
  • geo_bounds: 边界聚合:包含坐标点的矩形框

7. 地理形状(geo_shape)

  • 地理形状是通过一个个geohash单元画出来的

八. 数据建模

1. 关联关系

关联关系的处理,使用扁平化的存储,将数据冗余到同一个索引,提高查询效率

2. 嵌套对象

设计

内部存储

普通对json含有数组时,内部存储会被扁平化,导致逻辑关系丢失。需改为nested关系,而不是默认的object。嵌套对象内部会被索引为分离的隐藏文档

查询

使用特殊的nested查询或nested过滤

排序

3. 父子关系

原理

  • 和nested差不多,区别是nested是存储在同一个文档中,而父子关系是完全不同的文档
  • 父子文档需存储在同一个分片中
  • 父子关系映射存储在doc-values的数据结构中,完全存在内存
  • 适合父文档少,子文档多的情况

优势

  • 更新父文档时,不用更新子文档索引
  • 创建删除修改子文档时,不影响父文档和其他文档

劣势

  • 查询速度比嵌套类型慢5-10倍
  • 不适合父文档多的情况

设计父子关系

  • 指定某一文档type为另一文档type的parent
  • 创建父文档时,和普通文档没区别
  • 创建子文档时,必须通过parent指定父文档id。作用是创建关联关系并保证分配到同一个分片(使用父文档id做hash计算)
  • 尽量少使用父子关系,仅父文档比较少的时候

4. 扩容设计

扩容思路

  • 首先查看是否有低效率的查询可以优化
  • 是否缺少足够的内存
  • 是否开启了swap
  • 已经建立好的索引,不可修改分片数,可通过重新索引,将旧数据迁移到新索引中
  • 搜索性能取决于最慢节点的响应时间,合理设置分片使之负载均衡
  • 因为单索引和多索引没有区别,可通过设置多索引以扩容

分片数量设置

  • 基于现有的数据量和定期的增长量,预估数据总量
  • 基于现有的硬件信息,设置单个分片,0个副本,找到单个分片在当前硬件条件下能支持的最大文档数
  • 用总数量/单个分片的最大数,大致可估算出分片数

基于时间的数据流场景优化

  • 按时间切分索引
  • 旧数据不会被改变,使用optimize api进行段合并。
  • 大多数索引会有大概 50–150 个段,哪怕它们存有 TB 级别的数十亿条文档。段数量过大表明合并出现了问题(比如,合并速度跟不上段的创建)
  • 不过段合并消耗掉你节点上全部的I/O资源,从而有可能使集群失去响应。 如果你想要对索引执行optimize,你需要先把索引移到一个安全的节点,再执行。
  • 为了不影响正常索引,段合并后台限制磁盘读写速率为20MB/s,可根据实际情况调整,比如SSD盘,参数为indices.store.throttle.max_bytes_per_sec。甚至在没有查询时,设置为none,即没有限制,合并完再改回去。
  • 并且,对还在写数据的索引进行优化(Optimize)操作将会是一个糟糕的想法, 因为优化操作将消耗节点上大量 I/O 并对现有索引造成冲击
  • 我们可以临时移除副本分片,进行优化,然后再恢复副本分片
  • 去除副本之前,可通过snapshot restore api备份数据
  • 更旧的不会被使用的数据,关闭索引。关闭后除了磁盘,不会占用其他资源。flush(清空事务日志)->close(关闭索引)
  • 数据归档:snapshot restore api将数据存储到hdfs或其他地方

基于用户的数据流场景

  • 指定路由:保证同类数据会分发到同一分片。查询时也传入路由参数,确保只查询特定的分片,多分片查询带来的性能损耗
  • 使用别名,指定特定的名字对应特定的路由值和过滤器。以达到多个名称共享一个索引的效果。看起来像多个索引一样。
  • 当某个分片数据量剧增到需要单独建索引时,使用_alias操作:指定action的remove和add参数,实现平滑迁移。

九. 管理,监控

1. 重要的参数配置

  • cluster.name
  • node.name
  • path.data
  • path.logs
  • path.plugins
  • discovery.zen.minum_master_nodes: 最小主节点数,防止脑裂(多个主节点)
  • discover.zen.ping.unicast.hosts: 集群单播列表
  • gateway.recover_after_nodes 至少多少个节点,集群才可用
  • gateway.expected_node 集群期待有多少个节点
  • gateway.recover_fater_time 等待多久才进行数据恢复
  • logger.discovery 日志级别
  • index.search.slowlog.threshold.query.warn : "10s" 查询慢与10s的输出warn日志
  • index.search.slowlog.threshold.fetch.debug: "500ms" 查询慢与500ms的输出debug日志
  • index.indexing.slowlog.threshold.index.info: "5s 查询慢与5s的输出info日志
  • index.unassigned.node_left.delayed_timeout 修改延时分片时间
  • cluster.routing.allocation.enable" : "none" 禁止分片分配

2. 不要修改的配置

  • 不要更改垃圾回收器,默认使用CMS。不要更换为新一代的G1
  • 线程数量,默认为cpu核数。IO操作是由Lucene线程完成,不是es。

3. 堆内存的配置

  • 默认为1G,实际生产环境必须修改
  • 保证Xms和Xmx一样,防止运行时改变堆内存大小,这非常消耗资源
  • 内存分片不要超过本机内存的一半。因为Lucene本身也会需要内存和缓存。
  • 如果不需要对分词做聚合运算,可降低堆内存。堆内存越小,Elasticsearch(更快的 GC)和 Lucene(更多的内存用于缓存)的性能越好。
  • 内存不要超过32G。每个对象的指针都变长了,就会使用更多的 CPU 内存带宽,也就是说你实际上失去了更多的内存。果你想保证其安全可靠,设置堆内存为 31 GB 是一个安全的选择
  • 如果内存很大,可以考虑给一个机器分配多个es实例,但总的堆内存还是不要超过一半。同时配置cluster.routing.allocation.same_shard.host: true。防止同一个分片(主副)在一个机器上
  • 设置bootstrap.mlockall: true,锁住内存,不让发生内存swapping

4. 运维及优化

  • 日志文件默认存放在安装目录下的logs文件里,"logger.discovery" : "DEBUG"可设置日志级别
  • 可以设置输出慢查询日志
  • 如果不需要实时准确,把index.refresh_interval改到30s,做大批量倒入时,把这个值设为-1,倒入完毕后重新设置回来
  • 大批量倒入时,index.number_of_replicas设为0,关闭副本,提高效率
  • 尽量使用es自动生成的id,避免版本查找影响效率。如果使用自己的id,使用压缩性能良好的,避免使用太过随机的id
  • 延迟分片:防止节点掉线然后又重启导致的大量数据迁移问题。因为掉线的节点上的数据可能会因为失效而全部被删除,然后重新复制。参数为index.unassigned.node_left.delayed_timeout

5. 滚动重启

  • 保证不停集群功能的情况下逐一对每个节点进行升级或维护
  • 先停止索引新的数据
  • 禁止分片分配。cluster.routing.allocation.enable" : "none"
  • 关闭单个节点,并执行升级维护
  • 启动节点,并等待加入集群
  • 重启分片分配。cluster.routing.allocation.enable" : "all"
  • 对其他节点重复以上步骤
  • 恢复索引更新数据