赞
踩
单个分片
7.0开始,新创建一个索引时,默认只有一个主分片。单个分片,查询算分,聚合不准的问题都可以得以避免
单个索引,单个分片时候,集群无法实现水平扩展。即使增加新的节点,无法实现水平扩展
两个分片
集群增加一个节点后,Elasticsearch 会自动进行分片的移动,也叫 Shard Rebalancing
如何设计分片数
当分片数>节点数时,一旦集群中有新的数据节点加入,分片就可以自动进行分配,分片在重新分配时,系统不会有downtime
多分片的好处: 一个索引如果分布在不同的节点,多个节点可以并行执行;查询可以并行执行,数据写入可以分散到多个机器
分片过多所带来的副作用
Shard是Elasticsearch 实现集群水平扩展的最小单位。过多设置分片数会带来一些潜在的问题:
如何确定主分片数
从存储的物理角度看:
搜索类应用,单个分片不要超过20 GB
日志类应用,单个分片不要大于50 GB
为什么要控制分片存储大小:
提高Update 的性能
进行Merge 时,减少所需的资源
丢失节点后,具备更快的恢复速度
便于分片在集群内 Rebalancing
如何确定副本分片数
副本是主分片的拷贝:
对性能的影响:
ES的分片策略会尽量保证节点上的分片数大致相同,但是有些场景下会导致分配不均匀:
可以通过调整分片总数,避免分配不均衡
“index.routing.allocation.total_shards_per_node”,index级别的,表示这个index每个Node总共允许存在多少个shard,默认值是-1表示无穷多个;
“cluster.routing.allocation.total_shards_per_node”,cluster级别,表示集群范围内每个Node允许存在有多少个shard。默认值是-1表示无穷多个。
如果目标Node的Shard数超过了配置的上限,则不允许分配Shard到该Node上。注意:index级别的配置会覆盖cluster级别的配置。
生产环境中要适当调大这个index.routing.allocation.total_shards_per_node,避免有节点下线时,分片无法正常迁移
写请求是写入 primary shard,然后同步给所有的 replica shard;读请求可以从 primary shard 或 replica shard 读取,采用的是随机轮询算法。
ES写入数据的过程
ES读取数据的过程
根据id查询数据的过程
根据 doc id 进行 hash,判断出来当时把 doc id 分配到了哪个 shard 上面去,从那个 shard 去查询。
5. 客户端发送请求到任意一个 node,成为 coordinate node 。
6. coordinate node 对 doc id 进行哈希路由,将请求转发到对应的 node,此时会使用 round-robin 随机轮询算法,在 primary shard 以及其所有 replica 中随机选择一个,让读请求负载均衡。
7. 接收请求的 node 返回 document 给 coordinate node 。
8. coordinate node 返回 document 给客户端。
根据关键词查询数据的过程
核心概念:
segment file: 存储倒排索引的文件,每个segment本质上就是一个倒排索引,每秒都会生成一个segment文件,当文件过多时es会自动进行segment merge(合并文件),合并时会同时将已经标注删除的文档物理删除。
commit point: 记录当前所有可用的segment,每个commit point都会维护一个.del文件,即每个.del文件都有一个commit point文件(es删除数据本质是不属于物理删除),当es做删改操作时首先会在.del文件中声明某个document已经被删除,文件内记录了在某个segment内某个文档已经被删除,当查询请求过来时在segment中被删除的文件是能够查出来的,但是当返回结果时会根据commit point维护的那个.del文件把已经删除的文档过滤掉
translog日志文件: 为了防止elasticsearch宕机造成数据丢失保证可靠存储,es会将每次写入数据同时写到translog日志中。
os cache:操作系统里面,磁盘文件其实都有一个东西,叫做os cache,操作系统缓存,就是说数据写入磁盘文件之前,会先进入os cache,先进入操作系统级别的一个内存缓存中去
Refresh:
将文档先保存在Index buffer中,以refresh_interval为间隔时间,定期清空buffer,生成 segment,借助文件系统缓存的特性,先将segment放在文件系统缓存中,并开放查询,以提升搜索的实时性
Translog:
Segment没有写入磁盘,即便发生了当机,重启后,数据也能恢复,从ES6.0开始默认配置是每次请求都会落盘
Flush
删除旧的translog 文件,生成Segment并写入磁盘│更新commit point并写入磁盘。ES自动完成,可优化点不多
优化分片
#手动force merge
POST /my_index/_forcemerge
提升写入性能
写性能优化的目标: 增大写吞吐量,越高越好
注意:ES 的默认设置,已经综合考虑了数据可靠性,搜索的实时性,写入速度,一般不要盲目修改。一切优化,都要基于高质量的数据建模。
建模时的优化
如果需要追求极致的写入速度,可以牺牲数据可靠性及搜索实时性以换取性能:
降低 Refresh的频率
增加refresh_interval 的数值。默认为1s ,如果设置成-1,会禁止自动refresh。避免过于频繁的refresh,而生成过多的segment 文件。但是会降低搜索的实时性
PUT /my_index/_settings
{
"index" : {
"refresh_interval" : "10s"
}
}
增大静态配置参数indices.memory.index_buffer_size 默认是10%,会导致自动触发refresh
降低Translog写磁盘的频率,但是会降低容灾能力
分片设定
PUT myindex { "settings": { "index": { "refresh_interval": "30s", #30s一次refresh "number_of_shards": "2" }, "routing": { "allocation": { "total_shards_per_node": "3" #控制分片,避免数据热点 } }, "translog": { "sync_interval": "30s", "durability": "async" #降低translog落盘频率 }, "number_of_replicas": 0 }, "mappings": { "dynamic": false, #避免不必要的字段索引,必要时可以通过update by query 索引必要的字段 "properties": {} } }
启用 eager global ordinals
提升高基数聚合性能。适用场景:高基数聚合 。高基数聚合场景中的高基数含义:一个字段包含很大比例的唯一值。
global ordinals 中文翻译成全局序号,是一种数据结构,应用场景如下:
global ordinals 使用一个数值代表字段中的字符串值,然后为每一个数值分配一个 bucket(分桶)。
global ordinals 的本质是:启用 eager_global_ordinals 时,会在刷新(refresh)分片时 构建全局序号。这将构建全局序号的成本从搜索阶段转移到了数据索引化(写入)阶段。
创建索引的同时开启:eager_global_ordinals
PUT /my‐index
{
"mappings": {
"properties": {
"tags": {
"type": "keyword",
"eager_global_ordinals": true
}
}
}
注意:开启 eager_global_ordinals 会影响写入性能,因为每次刷新时都会创建新的全局序 号。为了最大程度地减少由于频繁刷新建立全局序号而导致的额外开销,请调大刷新间隔refresh_interval。该招数的本质是:以空间换时间。
插入数据时对索引进行预排序
PUT /my_index { "settings": { "index":{ "sort.field": "create_time", "sort.order": "desc" } }, "mappings": { "properties": { "create_time":{ "type": "date" } } } }
注意:预排序将增加 Elasticsearch 写入的成本。在某些用户特定场景下,开启索引预排序会导致大约 40%-50% 的写性能下降。也就是说,如果用户场景更关注写性能的业务,开启索引预排序不是一个很好的选择。
使用节点查询缓存
节点查询缓存(Node query cache)可用于有效缓存过滤器(filter)操作的结果。如果多次执行同一 filter 操作,这将很有效,但是即便更改过滤器中的某一个值,也将意味着需要计算新的过滤器结果。
例如,由于 “now” 值一直在变化,因此无法缓存在过滤器上下文中使用 “now” 的查询。
那怎么使用缓存呢?通过在 now 字段上应用 datemath 格式将其四舍五入到最接近的分钟/小时等,可以使此类请求更具可缓存性,以便可以对筛选结果进行缓存。
PUT /my_index/_doc/1 { "create_time":"2022-05-11T16:30:55.328Z" } #下面的示例无法使用缓存 GET /my_index/_search { "query":{ "constant_score": { "filter": { "range": { "create_time": { "gte": "now-1h", "lte": "now" } } } } } } # 下面的示例就可以使用节点查询缓存。 GET /my_index/_search { "query":{ "constant_score": { "filter": { "range": { "create_time": { "gte": "now-1h/m", "lte": "now/m" } } } } } }
如果当前时间 now 是:16:31:29,那么range query 将匹配 my_date 介于:15:31:00 和 15:31:59 之间的时间数据。同理,聚合的前半部分 query 中如果有基于时间查询,或者后半部分 aggs 部分中有基于时间聚合的,建议都使用 datemath 方式做缓存处理以优化性能。
使用分片请求缓存
聚合语句中,设置:size:0,就会使用分片请求缓存缓存结果。size = 0 的含义是:只返回聚合结果,不返回查询结果。
GET /es_db/_search
{
"size": 0,
"aggs": {
"remark_agg": {
"terms": {
"field": "remark.keyword"
}
}
}
}
拆分聚合,使聚合并行化
Elasticsearch 查询条件中同时有多个条件聚合,默认情况下聚合不是并行运行的。当为每个聚合提供自己的查询并执行 msearch 时,性能会有显著提升。因此,在 CPU 资源不是瓶颈的前提下,如果想缩短响应时间,可以将多个聚合拆分为多个查询,借助:msearch 实现并行聚合
。
#常规的多条件聚合实现 GET /employees/_search { "size": 0, "aggs": { "job_agg": { "terms": { "field": "job.keyword" } }, "max_salary":{ "max": { "field": "salary" } } } } # msearch 拆分多个语句的聚合实现 GET _msearch {"index":"employees"} {"size":0,"aggs":{"job_agg":{"terms":{"field": "job.keyword"}}}} {"index":"employees"} {"size":0,"aggs":{"max_salary":{"max":{"field": "salary"}}}}
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。