当前位置: 首页 > news >正文

分布式专题——55 ElasticSearch性能调优最佳实践

1 ES底层读写工作原理分析

1.1 分片路由

  • Elasticsearch(ES)不是通过随机或轮询的方式选择存储节点,而是通过计算公式确定文档所属的分片(进而确定存储节点),公式为:textshard_num = hash(_routing) % num_primary_shards

    • _routing:路由字段,默认取值为文档的_id字段,也可以由用户自定义;

    • num_primary_shards:索引的主分片数量(如下图中blogs索引的主分片数为3,即P0、P1、P2);

    • 逻辑:对_routing做哈希运算后,取模主分片数量,得到的结果就是文档要存储的主分片编号,进而确定对应的存储节点;

    在这里插入图片描述

  • **为什么主分片创建后不能修改?**如果修改了主分片数量num_primary_shards,会导致上述路由公式的计算结果完全失效——原本基于旧分片数计算出的“分片-文档”映射关系被破坏,ES将无法找到文档的存储位置。因此,索引的主分片数量在创建后不允许修改

  • 读写请求的处理逻辑

    • 写请求:必须先写入主分片(primary shard),再由主分片同步到所有副本分片(replica shard,如图中R0、R1、R2

    • 读请求:可以从主分片(primary shard)或副本分片(replica shard)中读取,ES采用随机轮询算法在可用的主分片和副本分片中选择读取源,以实现负载均衡。

1.2 ES写入数据的过程

  • 选择协调节点:客户端选择一个ES节点发送写入请求,该节点成为coordinating node(协调节点)

  • 路由与转发请求:协调节点对要写入的document进行路由(通过hash(_routing) % num_primary_shards公式确定目标主分片),并将请求转发给对应主分片所在的节点

  • 主分片处理并同步副本:目标节点上的primary shard(主分片)处理写入请求,然后将数据同步到对应的replica node(副本分片所在节点)

  • 确认并返回结果:协调节点确认primary shard所有replica shard都完成写入后,向客户端返回请求结果

    在这里插入图片描述

1.3 ES读取数据的过程

  • 根据doc id查询数据的过程

    • 选择协调节点:客户端发送请求到任意一个ES节点,该节点成为coordinate node(协调节点)

    • 路由与负载均衡转发:协调节点对doc id进行哈希路由(hash(_id) % shards_size),确定目标分片后,采用round-robin随机轮询算法,在primary shard及其所有replica shard中随机选择一个,将请求转发过去(实现读请求的负载均衡)

    • 返回文档给协调节点:接收请求的分片所在节点返回document给协调节点

    • 返回结果给客户端:协调节点将document返回给客户端

  • 根据关键词查询数据的过程(全文检索场景)

    • 选择协调节点:客户端发送请求到一个coordinate node(协调节点)

    • 转发搜索请求到所有分片:协调节点将搜索请求转发到所有相关分片对应的primary shardreplica shard(任意其一即可)

    • query phase(查询阶段):每个分片执行本地搜索,将自己的搜索结果返回给协调节点;协调节点对这些结果进行合并、排序、分页等操作,产出初步的搜索结果(如匹配的doc id列表)

    • fetch phase(获取阶段):协调节点根据初步结果中的doc id,去各个节点上拉取实际的document数据,最终返回给客户端

1.4 写数据底层原理

  • 核心概念

    • segment file:存储倒排索引的文件,每个segment本质是一个倒排索引。ES每秒生成一个segment文件;当文件过多或过大时,会自动执行segment merge(合并文件),合并时会物理删除已标注删除的文档;

    • commit point:记录当前所有可用的segment,每个commit point维护一个.del文件。ES删除数据时,先在.del文件中声明文档已删除;查询时,会根据commit point.del文件过滤掉已删除的文档(逻辑删除,而非物理删除);

    • translog日志文件:为防止ES宕机导致数据丢失,每次写入数据时会同时写入translog日志(保障数据可靠存储)。从ES6.0开始,默认每次请求都会将translog落盘;

    • os cache:操作系统缓存,数据写入磁盘前会先进入os cache(内存级缓存),这是ES实现“近实时查询”的关键之一;

  • 写入流程

    • 写入buffer与translog:写入操作先将数据写入内存buffer缓冲区(此时数据不可搜索),同时将操作写入translog日志文件(磁盘存储,保障可靠性);

    • Refresh操作:生成segment并进入os cache:当buffer满了或每隔1秒(由refresh_interval控制),会触发Refresh操作

      • buffer中的数据生成新的segment文件,并写入os cache(此时segment在文件系统缓存中,可开放查询,这是ES“近实时查询”的原因);
      • 同时更新commit point(但未持久化到磁盘),之后清空buffer
    • Flush操作:持久化到磁盘:默认每隔30分钟,或当translog足够大时(默认512M),触发Flush操作(提交commit)

      • 执行fsync,将os cache中的segment filecommit point强制刷入磁盘;
      • 清空translog日志文件,完成一次持久化流程;

    在这里插入图片描述

  • 关键操作说明

    • Refresh

      • 流程:将index buffer中的数据生成segment,放入os cache并开放查询,清空buffer
      • 作用:提升搜索实时性,让新写入的数据能被查询到;
    • Translog:即便segment未写入磁盘,宕机重启后也能通过translog恢复数据(ES6.0后默认每次请求落盘,可靠性更高);

    • Flush

      • 流程:删除旧translog、生成并写入segment到磁盘、更新并写入commit point到磁盘;
      • 作用:完成数据的持久化,保障数据最终一致性。

2 提升集群读写性能的方法

2.1 数据建模优化

  • 尽量将数据先行计算后存储,避免查询时使用Script计算(如示例中查询时的脚本逻辑会增加开销)

    #避免查询时脚本
    GET blogs/_search
    {"query": {"bool": {"must": [{"match": {"title": "elasticsearch"}}],"filter": {"script": {"script": {"source": "doc['title.keyword'].value.length()>5"}}}}}
    }
    
  • 尽量使用Filter Context:利用缓存机制减少不必要的算分,提升查询效率

  • 结合profileexplain API分析慢查询,持续优化数据模型

  • 避免使用*开头的通配符查询(如示例中的wildcard查询“*白云+”,这类查询性能差)

    GET /es_db/_search
    {"query": {"wildcard": {"address": {"value": "*白云*"}}}
    }
    

2.2 优化分片

  • 避免Over Sharing:分片过多会导致查询时需要访问每个分片,增加不必要的开销

  • 控制单个分片大小:结合场景控制,如Search场景建议单个分片20GB,Logging场景建议50GB

  • Force-merge只读索引:对于基于时间序列的只读索引,执行force merge减少segment数量,提升查询效率(示例中通过POST /my_index/_forcemerge手动触发)

    # 手动force merge
    POST /my_index/_forcemerge
    

2.3 提升写入性能的方法

  • 写入性能优化目标:增大写入吞吐量,尽可能提升写入效率

  • 客户端优化:

    • 多线程、批量写:通过性能测试确定最佳文档数量
    • 多线程需观察是否有HTTP 429(Too Many Requests)返回,实现重试和线程数自动调节
  • 服务器端优化:先分解单个节点的性能问题,结合测试压榨硬件资源(如观察CPU、I/O阻塞、线程切换、堆栈状况,使用更好的硬件)

2.4 服务器端优化写入性能的手段

  • 降低I/O操作

    • 使用ES自动生成的文档ID(减少路由计算开销)

    • 调整相关配置(如Refresh Interval

  • 降低CPU和存储开销

    • 减少不必要的分词:避免对无需分词的字段执行分词操作

    • 避免不需要的doc_values:减少内存和存储消耗

    • 保证文档字段顺序一致:提升文档压缩率,减少存储占用

  • 写入和分片的均衡负载:实现水平扩展,如使用Shard FilteringWrite Load Balancer

  • 调整Bulk线程池和队列

    • 客户端:

      • 单个bulk请求体数据量建议5-15m;
      • 写入端bulk请求超时建议设置60s以上;
      • 尽量将数据轮询打到不同节点;
    • 服务器端:

      • 索引创建是计算密集型任务,线程池配置为“CPU核心数+1”避免上下文切换;
      • 队列大小适当增加但不要过大(避免内存成为GC负担);
    • ES线程池设置:Elasticsearch线程池调优指南-CSDN博客;

  • 注意:ES 的默认设置,已经综合考虑了数据可靠性,搜索的实时性,写入速度,一般不要盲目修改。一切优化,都要基于高质量的数据建模。

2.5 建模时的优化

  • 只需聚合不需要搜索的字段,将index设置为false(减少索引开销)

  • 不要对字符串使用默认的dynamic mapping:避免字段数量过多影响性能

  • 通过index_options控制倒排索引的内容,减少索引存储和查询开销

  • 极致写入速度的权衡(牺牲可靠性/实时性)

    • 牺牲可靠性:写入时将副本分片设为0,完成后再调整回去

    • 牺牲搜索实时性:增加Refresh Interval的时间(减少segment生成频率)

    • 牺牲可靠性:修改Translog配置(如异步落盘)

2.6 关键配置调整

  • 降低Refresh的频率

    • 增加refresh_interval的数值(默认1s,设置为-1可禁止自动refresh):避免频繁生成segment文件,但会降低搜索实时性(示例中通过PUT /my_index/_settingsrefresh_interval设为10s)

      PUT /my_index/_settings
      {"index" : {"refresh_interval" : "10s"}
      }
      
    • 增大静态配置参数indices.memory.index_buffer_size(默认10%,避免因内存触发自动refresh)

  • 降低Translog写磁盘的频率(牺牲容灾能力)

    • index.translog.durability:默认request(每个请求落盘),可设置为async(异步写入)

    • index.translog.sync_interval:设置为60s(每分钟执行一次同步)

    • index.translog.flush_threshold_size:默认512m,可适当调大,超过该值触发flush

  • 分片设定

    • 副本在写入时设为0,完成后再增加(减少写入时的副本同步开销)

    • 合理设置主分片数,确保均匀分配在所有数据节点上

    • 通过index.routing.allocation.total_shards_per_node限定每个索引在每个节点上的主分片数(避免数据热点)

2.7 例

DELETE myindex
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": {}}
}
  • settings.index 模块

    • refresh_interval": "30s":控制ES的Refresh操作频率,默认是1秒生成一次segment并放入os cache供查询。这里设置为30秒一次,减少了segment的生成频率,降低了CPU和I/O开销,提升写入性能;但会牺牲部分搜索实时性(新写入数据需等30秒后才能被查询到);
  • number_of_shards": "2":设置索引的主分片数量为2。主分片数决定了数据的分片分布,需结合数据量和节点数合理设置,这里选择2个主分片是为了在“数据拆分粒度”和“查询开销”之间做平衡(分片过多会增加查询时的分片聚合开销);

  • settings.routing.allocation 模块

    • total_shards_per_node": "3":限制每个节点上该索引可分配的主分片+副本分片总数为3。通过这种方式避免单个节点被该索引的分片过度占用,防止数据热点,保障集群资源的均衡分配;
  • settings.translog 模块

    • sync_interval": "30s"translog(事务日志)的同步间隔,默认是实时同步。设置为30秒后,translog每30秒才会同步一次,降低了磁盘I/O频率,提升写入性能;
  • durability": "async":控制translog的落盘策略,默认是request(每个写入请求都落盘,保障数据可靠性)。设置为async后,translog异步落盘,进一步提升写入吞吐量,但会牺牲部分容灾能力(宕机时可能丢失少量未落盘的写入数据);

  • settings.number_of_replicas 模块

    • number_of_replicas": 0:设置索引的副本分片数量为0。副本主要用于高可用和读负载均衡,写入时副本需要同步数据,因此设置为0可以最大化写入性能;待写入完成后,可再将副本数调整回去以保障可靠性;
  • mappings 模块

    • dynamic": false:禁用“动态映射”,即ES不会自动为文档中新增的字段创建索引。这样可以避免因字段数量爆炸导致的性能开销,仅对properties中声明的字段建立索引;若后续需要为新字段建索引,可通过update by query手动处理;
  • properties": {}:这里可以定义具体的字段映射规则(如字段类型、分词器等),当前为空表示暂时未定义具体字段的精细化映射;

  • 整体优化目标:这段配置是一套**“极致写入性能优先”**的优化策略,通过减少Refresh频率、异步落盘translog、关闭副本、禁用动态映射等手段,大幅提升写入吞吐量;同时通过控制分片分布避免数据热点,保障集群资源的均衡性。适合“写入压力大、对实时查询要求不高、后续可补全副本和索引”的业务场景。

http://www.dtcms.com/a/600960.html

相关文章:

  • 深入理解HarmonyOS Calendar组件:高级日期选择实现与优化
  • 中小企业的网站建设 论文怎么免费建自己的网站
  • Qt配置安卓开发环境
  • 塔吉特采购“安全密码”:自养号技术如何规避砍单风险提高成功率
  • 「腾讯云NoSQL」技术之MongoDB篇:MongoDB 5.0→8.0 balance性能提升40%内幕揭秘
  • Blender入门学习11 - 合成器
  • Blender 漫游车设计与渲染环境塑造
  • 怀柔营销型网站建设腾讯专门做数据标注的网站是
  • 阿里云做网站选择服务器湖北省住房和城乡建设厅官方网站
  • 比 Cursor 更丝滑的 AI DevOps 编程智能体 - CodeRider-Kilo 正式发布!
  • 【C++设计模式之单例模式SingletonPattern】
  • Blob格式的PDF文件调用打印,浏览器文件打印(兼容)
  • 软件单例模式
  • 微服务搭建----springboot接入Nacos2.x
  • DataWhale-HelloAgents(第二部分:大语言模型基础)
  • 批量转换论文正文引用为上标后转PDF保持上标
  • 一个简洁的独立站(带产品管理功能+双语言)
  • 阿里巴巴网站建设建议广州网页设计机构
  • Hello-agents TASK02 第三章节 大模型基础
  • 深入理解cursor 中的mcp工作原理
  • Nginx 基础教程:从安装到核心配置(视频教程)
  • 实战指南:使用 CAN FD LIN网关进行嵌入式开发与协议转换
  • Excel插件:学校成绩统计与排名介绍
  • IBMS三维可视化集成系统产品介绍
  • 网站负责人核验现场拍摄照片电子件网站建设总结经验
  • 宇宙膨胀速度的光速极限:基于张祥前统一场论的第一性原理推导与观测验证
  • Custom SRP - 14 Multiple Cameras
  • QT开发汇总(更新2025.11.12)
  • HTML5 MathML:现代网页中的数学表达利器
  • wordpress admin head简述搜索引擎优化