前言
- 为什么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
值 | 含义 |
---|---|
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"
- 对其他节点重复以上步骤
- 恢复索引更新数据