【工作】ElasticSearch技术全脉络

概述和摘要

ES已经发展了10年,版本来到7.x,Lucene发展了20年,版本来到8.x。
期间的技术迭代与更新真是数不胜数。。。

我个人从2016年开始接触ES,从《Lucene原理与代码分析》这本书开始,了解分词,倒排,跳跃表,相关度计算等全文检索领域。随后调研ES2.x版本,仍记得当时阅读源码,记录调用栈,分析类体系的过程。以及使用原生Lucene API解读生成的shard文件,分析其存储的terms,docs。

随后并没有一直跟进(后来主要做spark离线计算,流计算,图库,对象存储之类去了),小公司也很难专人投入去追逐最新技术发展。只是陆陆续续根据项目需要,了解新版本的一些特性,选择性的在新集群部署。大概也很难有人能记录完整个ES版本的更新历史吧。。。实在是一个庞大的工程。

而且说实话我觉得ES发展的特性太多了,其实很多人根本不会用到许多功能。因此下文会有很多我觉得不重要或者很小众的地方就略过了,看场景吧。

我认为,学好ES有几个思路

  • 核心需要去理解term,即检索的基本单元词。所有扩展出来的Query类型,都是基于词去描述的,模糊,精确,前缀,词距等。词怎么来,词怎么存。
  • 而在这之上,则去理解shard,即文档和词的物理分布。需要关注shard多大,内存开销,分散在哪些节点,因为这会影响综合性能。参数设置不当,会导致元数据请求压力,内存管理压力,数据热点问题。
  • 最后则是ES的官方文档。大部分人都没有认真的梳理并完整阅读过ES的参考手册。实际上,使用ES所能遇到的问题,ES官方文档几乎都会涉及,包括概念,API,优化思路,功能特性。。。而可惜实在太多,很多人没有耐心。说实话,认真看完那些文档的人,可以自称专家,已经比90%研究并使用ES的人专业了。
  • 其他团队或公司的实践分享 看PPT去吧

本文摘要

  • 梳理ES的大方向上的各版本新特性变化过程(Lucene就不弄了,太多了。。。)小版本也不弄,只做参考吧。
  • 整理当前最新版本官方文档目录,做为了解技术进展的依据。
  • ES源码大概脉络与部分代码走读笔记
  • 项目经验以及优化实践总结

ES的各主要版本新特性

PS:实际收集起来发现还是太麻烦了。。。看到啥记录啥吧,大概有个脉络就行

1. ES 2.x --> ES 5.x

  • 其实,elasticsearch5.x 和 elasticsearch2.x 并不区别很大。是因为,ELK里之前版本各种很混乱,直接升级到5.0了。
  • Elasticsearch5.0率先集成了Lucene6版本,其中最重要的特性就是 Dimensional Point Fields,多维浮点字段,ES里面相关的字段如date, numeric,ip 和 Geospatial 都将大大提升性能。这么说吧,磁盘空间少一半;索引时间少一半;查询性能提升25%;IPV6也支持了。为什么快,底层使用的是Block k-d trees,核心思想是将数字类型编码成定长的字节数组,对定长的字节数组内容进行编码排序,然后来构建二叉树,然后依次递归构建,目前底层支持8个维度和最多每个维度16个字节,基本满足大部分场景
  • ES5.0在Internal engine级别移除了用于避免同一文档并发更新的竞争锁,带来15%-20%的性能提升
  • Sliced Scroll类型 用过Scroll接口吧,很慢?如果你数据量很大,用Scroll遍历数据那确实是接受不了,现在Scroll接口可以并发来进行数据遍历了。每个Scroll请求,可以分成多个Slice请求,可以理解为切片,各Slice独立并行,利用Scroll重建或者遍历要快很多倍
  • 新增了一个Profile API 玩过SQL的人都知道,数据库服务的执行计划(execution plan)非常有用,可以看到那些查询走没走索引和执行时间,用来调优,elasticsearch现在提供了Profile API来进行查询的优化,只需要在查询的时候开启profile:true就可以了,一个查询执行过程中的每个组件的性能消耗都能收集到
  • 新增了一个 Shrink API 它可将分片数进行收缩成它的因数,如之前你是15个分片,你可以收缩成5个或者3个又或者1个
  • 新增:Reindex
  • 新增: Ingest Node 大家之前如果需要对数据进行加工,都是在索引之前进行处理,比如logstash可以对日志进行结构化和转换,现在直接在es就可以处理了,目前es提供了一些常用的诸如convert、grok之类的处理器,在使用的时候,先定义一个pipeline管道,里面设置文档的加工逻辑,在建索引的时候指定pipeline名称,那么这个索引就会按照预先定义好的pipeline来处理了
  • 新增Painless Script 基于安全和性能方面,我们自己开发了一个新的脚本引擎,名字就叫Painless,顾名思义,简单安全,无痛使用,和Groove的沙盒机制不一样,Painless使用白名单来限制函数与字段的访问,针对es的场景来进行优化,只做es数据的操作,更加轻量级,速度要快好几倍,并且支持Java静态类型,语法保持Groove类似,还支持Java的lambda表达式
  • 新增:Task Manager 这个是5.0 引入任务调度管理机制,用来做 离线任务的管理,比如长时间运行的reindex和update_by_query等都是运行在TaskManager机制之上的,并且任务是可管理的,你可以随时cancel掉,并且任务状态持久化,支持故障恢复
  • 新增 : Cluster allocation explain API 『谁能给我一个shard不能分配的理由』,现在有了,大家如果之前遇到过分片不能正常分配的问题,但是不知道是什么原因,只能尝试手动路由或者重启节点,但是不一定能解决,其实里面有很多原因,现在提供的这个explain接口就是告诉你目前为什么不能正常分配的原因,方便你去解决。
  • 为索引写操作添加顺序号 es是在primary上写完然后同步写副本,这些请求都是并发的,虽然可以通过version来控制冲突,但是没法保证其他副本的操作顺序,通过写的时候产生顺序号,并且在本地也写入checkpoint来记录操作点,这样在副本恢复的时候也可以知道当前副本的数据位置,而只需要从指定的数据开始恢复就行了,而不是像以前的粗暴的做完整的文件同步 ,另外这些顺序号也是持久化的,重启后也可以快速恢复副本信息
  • 引入新的字段类型 Text/Keyword来替换 String,将类型分开的好处就是使用起来更加简单清晰
  • _timestamp 和 _ttl 已经移除,需要在 Ingest 或者程序端处理。
  • ES 可直接用 HDFS 来进行备份还原( Snapshot/Restore )了
  • Delete-by-query 和 Update-by-query 重新回到 core ,以前是插件,现在可以直接使用了,也是构建在 Reindex 机制之上。(es1.x版本是直接支持,在es2.x中提取为插件,5.x继续回归直接支持)
  • 默认使用 BM25 评分算法,效果更佳,之前是 TF/IDF。
  • 限制单个请求的 shards 数量,默认 1000 个

2. ES 5.x --> ES 6.x

  • 稀疏性 Doc Values 的支持 优化存储空间
  • Index sorting,如果在索引的时候提取排好序,那么搜索或聚合的时候就会非常快,相应的直接走预先排序好的索引就行了。当然索引的时候会要增加额外开销,适合不怎么变化的索引的场景
  • Removal of types,在 6.0 里面,开始不支持一个 index 里面存在多个 type 了,所有的新的 index 都将只有一个虚拟的固定的 type: doc 来代替
  • Load aware shard routing, 基于负载的请求路由,目前的搜索请求是全节点轮询,那么性能最慢的节点往往会造成整体的延迟增加,新的实现方式将基于队列的耗费时间自动调节队列长度,负载高的节点的队列长度将减少,让其他节点分摊更多的压力,搜索和索引都将基于这种机制
  • 默认关闭_all元字段 索引的mapping中也不再支持include_in_all。
  • 从5.6版本开始,发布了Java High Level Client,使用Java High Level Client可以对Elasticsearch执行search, index, delete, update和bulk操作,和TransportClient 使用相同的Java核心类库。
    Java High Level Client的设计目标是在未来取代TransportClient 。
  • 取消对Content-Type的自动识别 所以现在REST请求需要加上 -H "Content-Type:application/json" 了
  • 索引存在api中不再接收ignore_unavailable和allow_no_indices参数
  • 之前不推荐使用的fielddata_fields已经被移除. 取而代之的是使用docvalue_fields
  • 在Elasticsearch之前的版本中,配置文件中可能会自动识别true, false, on, off, yes,no, 0, 1等字符串为boolean类型.在Elasticsearch 6.0中,仅仅会识别true and false作为布尔类型
  • 移除Groovy, JavaScript, and Python等脚本语言

3. ES 6.x --> ES 7.x

  • Elasticsearch 7.0 默认自带 JDK
  • 默认分片数改为1,不再是5。
  • Elasticsearch 7.0 没有 Type 了,包括 API 层面的。
  • 7.1开始,Security功能免费使用
  • 查询相关性速度优化,Weak-AND算法在Term Query查询场景有3700%的性能提升
  • 引入新的集群协调子系统
    移除 minimum_master_nodes 参数,让 Elasticsearch 自己选择可以形成仲裁的节点。典型的主节点选举现在只需要很短的时间就可以完成。
    集群的伸缩变得更安全、更容易,并且可能造成丢失数据的系统配置选项更少了。节点更清楚地记录它们的状态,有助于诊断为什么它们不能加入集群或为什么无法选举出主节点。
  • 不再内存溢出
    新的 Circuit Breaker 在JVM 堆栈层面监测内存使用,Elasticsearch 比之前更加健壮。设置indices.breaker.fielddata.limit的默认值已从JVM堆大小的60%降低到40%,当查询或聚合的数据量超出单机处理的最大内存限制时会被截断,并抛出异常(有点类似clickhouse)
  • 时间戳纳秒级支持,提升数据精度
  • 新增 alias、date_nanos、features、vector等数据类型。

ES 7.6官方文档目录

只关注:Elasticsearch Reference
其他诸如Clients、Script Language、Plugins、ELKB套餐等等不管。。。

1. Elasticsearch introduction

一些基本概述

2. Getting started with Elasticsearch

简单入门使用示例

3. Set up Elasticsearch

  • Installing Elasticsearch 多种平台/方式的安装部署
  • Configuring Elasticsearch 各类配置 日志、权限、系统参数、JVM、索引生命周期管理(ILM)、监控等
  • Important Elasticsearch configuration 一些重点配置项
  • Important System Configuration 重点系统设置 SWAP、虚拟内存、IO设置等
  • Bootstrap Checks ES进程安全性要求很高 会检查一大堆东西 不通过是启动不了的
  • Starting Elasticsearch 启动集群
  • Stopping Elasticsearch 停止集群
  • Adding nodes to your cluster 添加节点
  • Full-cluster restart and rolling restart 滚动重启
  • Set up X-Pack:官方主推插件 security, alerting, monitoring, reporting, machine learning, and many other capabilities
    需要重点说下其收费策略。license分为多种级别,basic永久免费 包含核心 security,一般情况够用了
  • Configuring X-Pack Java Clients 注:Deprecated in 7.0.0,即后面版本都合并到 Java High Level REST Client 去了
  • Bootstrap Checks for X-Pack 略

4. Upgrade Elasticsearch 略(一般人不会整天升级吧)

5. Aggregations 这章讲ES的查询聚合操作(不完全像数据库的分组,有更多玩法,需要的细看。)

主要分几大类 :

  • Metric 指标统计,单一输出 各种统计信息
  • Bucketing 类似分组 不同数据类型,可定制不同分组方式如IP区间;
  • Matrix 方差矩阵;相关系数等 比较偏的使用;
  • Pipeline 类似子查询的聚合??
【工作】ElasticSearch技术全脉络_第1张图片
Metric的一些细节
【工作】ElasticSearch技术全脉络_第2张图片
image.png

【工作】ElasticSearch技术全脉络_第3张图片
image.png

6. Query DSL (重点章节,查询描述语言。即各种检索怎么写)

  • Query and filter context 这里解释query与filter区别 即是否把谓词参与评分。有性能上的差异
  • Compound queries 查询的组合形态,如常用的bool,还有其他几种不太常用
    Full text queries 核心检索类型 分词匹配、短语、lucene语法
    Geo queries 地理位置的
    Shape queries 地理位置的
    Joining queries 子查询、关联(不太用 不关注)
    Match all 全匹配
    Span queries 略(不太用 不关注)
    Specialized queries 一些特殊检索(不太用 不关注)什么模糊词、近似词、脚本等
    Term-level queries 核心检索类型 词的各种检索。ID、前缀、精确、模糊、区间、正则等
    minimum_should_match parameter 设置命中多少词可返回
    rewrite parameter (不太用 不关注)
    Regular expression syntax (不太用 不关注)

7. Search across clusters 跨集群搜索

8. Scripting

挺强大的一个功能。可以对命中的文档做某种操作(包括对返回数据二次计算,更新文档值等)ES甚至为此设计了一个叫painless的简单语言。

9. Mapping 核心概念

  • Field datatypes 数据类型(真是蛋疼,很多没用过,越来越多了)
    总之有这些,看着办吧
    Alias,Arrays,Binary,Boolean,Date,Date nanoseconds,Dense vector,Histogram,Flattened,Geo-point,Geo-shape,IP,Join,Keyword,Nested,Numeric,Object,Percolator,Range,Rank feature,Rank features,Search-as-you-type,Sparse vector,Text,Token count,Shape
    不过这里要重点说下ES里的数组类型概念:In Elasticsearch, there is no dedicated array datatype. Any field can contain zero or more values by default, however, all values in the array must be of the same datatype
    即数组并不需要特意声明,每个文档每个字段本来就可以支持一个或多个值。可以独立索引,独立检索到。

  • Meta-Fields 内置生成的元数据字段,一般不用关注,可以取出来。
    _field_names field
    _ignored field
    _id field
    _index field
    _meta field
    _routing field
    _source field
    _type field

  • Mapping parameters这里其实很重要,需要精细优化字段存储空间,检索速度的话得研究一下
    analyzer,boost,coerce,copy_to,doc_values,dynamic,eager_global_ordinals,enabled,fielddata,format,ignore_above,ignore_malformed,index,index_options,index_phrases,index_prefixes,meta,fields,normalizer,norms,null_value,position_increment_gap,properties,search_analyzer,similarity,store,term_vector
    可以设置影响分词器、评分、聚合、高亮等各种相关选项。对存储空间,检索速度有很大影响。

  • Dynamic Mapping 动态更新mapping。并不建议用

10. Text analysis 分词器

本章可略。一般用户知道怎么选自己合适的分词器即可,常用的无非那几个。

11. Modules

这里就涉及ES原理了。包括物理分布、通信、分布式行为控制、线程池、插件等。ES可以理解为一堆Module模块组合起来提供服务的一个进程。
想深入理解ES,建议重点看

  • Discovery and cluster formation
    节点发现和联结,分布式怎么组起来的。怎么调节
  • Shard allocation and cluster-level routing
    shard是怎么分布与平衡、迁移的。可以自定义一些规则如标签,按用户希望把对应索引的shard放到何处。
  • Local Gateway 这里好像考虑索引目录冲突的问题
  • HTTP 对外服务模块
  • Indices 针对索引读写行为的一些设置。如缓存、请求断路器、恢复选项、查询选项等
  • Network Settings 网络通信设置
  • Node 略
  • Plugins 插件相关
  • Thread Pool 略
  • Transport 略
  • Remote clusters 略

12. Index modules

也是原理核心

  • Analysis 分词
  • Index Shard Allocation shard分配控制
  • Mapper mapping参数
  • Merge 合并控制。段越少越省空间,检索越快。建议对静态索引全部合并一下
  • Similarity module 评分器模块
  • Slow Log 慢查询日志
  • Store 略
  • Translog 略
  • History retention 略
  • Index Sorting 略

13. Ingest node 可以对文档做一些ETL、管道工作。略

14. Manage the index lifecycle (重点关注)

大规模ES集群尤其需要注意索引的生命周期维护。最终目标是使资源得到最合理利用。
通过创建Index lifecycle management (ILM) policies 策略交给ES去执行。
具体有:

  • Rollover 滚动性的产生新索引如固定日期 大小
  • Shrink 合并多个shard到一个shard(减少存储以及提升检索速度)
  • Force merge 段合并
  • Freeze 让索引只读并且大幅减少内存开销(当然会降低检索速度,适合使用频率很少的索引如历史性数据)
  • Delete 配置删除任务 生命周期控制

并且ILM功能可以做到索引的冷热分离,异构存储自动迁移(结合标签功能)
即定义一些stage(索引什么时候处于什么阶段,什么时候改变阶段如新建时hot、过几天warm、过一个月cold)与相应的action(在各个阶段做什么操作如合并如迁移)
看一个完整例子好理解一点:

PUT _ilm/policy/full_policy
{
  "policy": {
    "phases": {
      "hot": {
        "actions": {
          "rollover": {
            "max_age": "7d",
            "max_size": "50G"
          }
        }
      },
      "warm": {
        "min_age": "30d",
        "actions": {
          "forcemerge": {
            "max_num_segments": 1
          },
          "shrink": {
            "number_of_shards": 1
          },
          "allocate": {
            "number_of_replicas": 2
          }
        }
      },
      "cold": {
        "min_age": "60d",
        "actions": {
          "allocate": {
            "require": {
              "type": "cold"
            }
          }
        }
      },
      "delete": {
        "min_age": "90d",
        "actions": {
          "delete": {}
        }
      }
    }
  }
}

总的来说,有限制,但是合适的场景里很实用。。

15. SQL access(重点关注)

说实话ES的官方sql功能出的很晚,也不尽如人意。。。
这里要说个叫cratedb的产品,其基于ES核心功能上封装了强大完备的SQL语言支持。甚至包括各种操作如启动停止都能用SQL去操作。。

  • Overview
    原先xpack的组件。 REST interface, command-line or JDBC都支持,轻量
  • Getting Started with SQL
    使用CLI或者REST
    POST /_sql?format=txt
    {
    "query": "SELECT * FROM library WHERE release_date < '2000-01-01'"
    }
  • Conventions and Terminology
    一些约束
  • Security
    略。通常不开启
  • SQL REST API
    基本使用示例
    可以设置返回数据格式CSV/JSON等
    也可以分页返回大量数据 Cursor
    还可以结合es的dsl语法一起用(方便一些sql不好描述的谓词)
    可以按列组织返回数据
    支持的选项或参数,都比较好理解:
    query、fetch_size、filter、request_timeout、page_timeout、index_include_frozen、field_multi_value_leniency(多值相关)
  • SQL Translate API
    把SQL转换成DSL模式的json
  • SQL CLI
    ./bin/elasticsearch-sql-cli https://some.server:9200
  • SQL JDBC

  org.elasticsearch.plugin
  x-pack-sql-jdbc
  7.6.2

jdbc:es://http://server:3456/?timezone=UTC&page.size=250
同样可以设置超时等各种参数

  • SQL ODBC
    略。反正有就行
  • SQL Client Applications
    一些连接数据库的应用。。略
  • SQL Language
    总结:自成一体的sql语法 因为很多概念是自有的。另外不能建表删表,类似只读操作,总之就是一个复杂es DSL语法的简化使用方式,常用的语句倒也基本支持,看个人需求,如果都能满足,可以用
    • Lexical Structure 关键字 、常量 、数值 、转义 、操作符 、运算符等等
    • SQL Commands 只支持desc/select/show等
    • DESCRIBE TABLE
    • SELECT
      SELECT select_expr [, ...]
      [ FROM table_name ]
      [ WHERE condition ]
      [ GROUP BY grouping_element [, ...] ]
      [ HAVING condition]
      [ ORDER BY expression [ ASC | DESC ] [, ...] ]
      [ LIMIT [ count ] ]
      [ PIVOT ( aggregation_expr FOR column IN ( value [ [ AS ] alias ] [, ...] ) ) ]
    • SHOW COLUMNS
    • SHOW FUNCTIONS 主要是一些日期 聚合 数学 字符串函数
    • SHOW TABLES
    • Data Types ES类型到SQL类型的映射
    • Index patterns 可以一次查多个索引 * % 等
    • Frozen Indices 冻结索引 需要设置 index_include_frozen
  • Functions and Operators (这节是重点)
Comparison Operators 常规比较:
  =、!=、<、<=、 >、 >=、between、in、is not null、is null
Logical Operators 逻辑操作:
  AND OR NOT
Math Operators 数值计算:
  + - * / %
Cast Operators 转换操作:
  '123'::long 或者 as long 
LIKE and RLIKE Operators 模糊或正则(等同于部分全文检索):
  foo LIKE 'bar'   等价于  MATCH(foo, 'bar')
  foo LIKE 'bar' AND tar LIKE 'goo' 等价于 QUERY('foo: bar AND tar: goo')
  等等。需要注意的是 语义上似乎不太像数据库SQL的like rlike,更多细节需要使用时研究。。
Aggregate Functions 聚合,比传统数据库增加了很多:
 AVG MAX MIN SUM
 COUNT COUNT(ALL) COUNT(DISTINCT) FIRST/FIRST_VALUE 
 LAST/LAST_VALUE 
 Statistics KURTOSIS MAD PERCENTILE PERCENTILE_RANK SKEWNESS 
 STDDEV_POP SUM_OF_SQUARES VAR_POP
Grouping Functions 分组操作:
  与传统SQL也有较大区别 
  如直方图等概念,特定的聚合方式(按各种时间段等)
Date/Time and Interval Functions and Operators 日期计算:
  4d 
  INTERVAL 49 YEARS 
  等等大量的日期关键字和用法
Full-Text Search Functions 全文检索重点:
  具体有2种语法MATCH和QUERY,功能上query更强大(具体映射成DSL里的query_string检索),具体差别在于可支持检索类型和可设置参数不同。
  MATCH(field_exp,constant_exp [, options])  // 基本匹配 可以设置参数 (即DSL的一些参数)
  QUERY(constant_exp[, options]) 
  举例:
  SELECT author, name, SCORE() FROM library WHERE MATCH(name, 'to the star', 'operator=or;cutoff_frequency=0.2') // 用SCORE函数表示评分 可以指定参数另分词后and 还是 or 
Mathematical Functions 数学函数:
  平方、对数等等
String Functions  字符串函数:
  length trim replace等等 
Type Conversion Functions 转换函数 略
Geo Functions 地理位置函数 略 
Conditional Functions And Expressions 条件函数:
  case when / is null 等
System Functions 系统函数:
  database
  user
  等等
  • Reserved keywords
    关键字。与标准SQL多了挺多如日期时间相关,如全文特定的需要
  • SQL Limitations
    限制不少,主要是ES的类型多,语法多。如子文档、数组表示都和标准SQL差异

16. Monitor a cluster

略,收费的吧?
一般用户,顶多装个head之类的插件就好了。
高级点的,弄个prometheus + grafana 自动收集集群信息展示图表 都有社区现成的插件

17. Frozen indices

很实用的功能。尤其数据量庞大的时候,对使用很少的索引,不能轻易删掉但是又占大量内存空间。这时候可以用frozen功能冻结索引。
冻结的意思是,当查询到的时候,才去按需加载相关数据文件,并且查询完会释放。与一般索引不同,一般索引把很多数据结构常驻内存如词典树。使用很简单:

POST /my_index/_freeze
POST /my_index/_unfreeze

18. Roll up or transform your data

略,olap领域的玩法。就是各种复杂的维度的统计,可定制性

19. Set up a cluster for high availability

略,备份集群。奢侈

20. Snapshot and restore

略,快照和恢复集群。不太常用,需要再对着步骤操作就行了。

21. Secure a cluster 安全设置

重点特性。重不重要因场景而易,挺复杂的。很多概念和设置

22. Alerting on cluster and index events

略,大概也是收费吧。。monitor我都不关心何况alert。。

23. Command line tools

应该是ES 7后推出的一系列命令行工具。以前没见过

  • elasticsearch-certgen SSL加密通信相关的
  • elasticsearch-certutil SSL加密通信相关的
  • elasticsearch-croneval 检查定期任务配置是否合法。结合其他高级模块用的吧
  • elasticsearch-keystore SSL加密通信相关的
  • elasticsearch-migrate 用户角色迁移到新集群。。。
  • elasticsearch-node 可以对实例实时做一些不安全操作 高端慎用。。。
  • elasticsearch-saml-metadata
  • elasticsearch-setup-passwords 初始超级密码设置
  • elasticsearch-shard 某些情况用来修复shard的
  • elasticsearch-syskeygen 通信安全相关的
  • elasticsearch-users 用户管理 xpack的吧

24. How To (重点,关于如何使用和优化)

  • General recommendations
    不要返回太大结果集,如果需要,使用分页

不要索引超大文档(100MB + )

  • Recipes
    怎么用好评分
  • Tune for indexing speed
    bulk操作
    多线程加载、多节点并行加载
    调大 index.refresh_interval
    先撤销副本
    关闭操作系统swap
    让系统有足够的IO内存缓存
    使用自动生成的ID(因为自定义ID的话,需要先检索一遍。。)
    SSD
    调大indices.memory.index_buffer_size(默认100MB or jvm 10% )
    其他索引级的能减少存储空间的设置如mapping简化
  • Tune for search speed
    这节内容较少。值得关注的是新出的profile API,观察时间开销详情
  • Tune for disk usage
    包括merge/shrink/调整压缩/使用索引排序提升压缩比。。。

25. Glossary of terms 术语表

略 入门的来看吧。

26. REST APIs (重点章节)

  • API conventions
    通用性惯例,约束。如多索引可以一起操作
  • cat APIs
    最常用的查看集群、索引信息与状态的入口
  • Cluster APIs
    集群性的操作如迁移shard,集群状态,节点状态。重要的如allocation explain、task管理等。
  • Cross-cluster replication APIs
    略。多集群的操作
  • Document APIs
    文档级别的增删改查操作
  • Enrich APIs
    略。与ingest有关 数据预处理的
  • Explore API
    略。好像是某种遍历动作 检查数据成分什么的
  • Index APIs(重点)
    真对索引的各种操作
  • Index lifecycle management API
    生命周期管理策略相关
  • Ingest APIs
    略。预处理的
  • Info API
    查看你的特性是否支持
    Provides general information about the installed X-Pack features.
  • Licensing APIs
    证书的
  • Machine learning anomaly detection APIs
    略。机器学习的
  • Machine learning data frame analytics APIs
    略。机器学习的
  • Migration APIs
    略。
  • Reload search analyzers
    略。
  • Rollup APIs
    略。高级分析任务
  • Search APIs
    检索相关的。可能需要注意一下explain、profile两个用法
  • Security APIs
    用户角色相关
  • Snapshot lifecycle management API
    略。快照策略?
  • Transform APIs
    略。也是高端分析的用法吧。。搞的像spark RDD transform似的
  • Usage API
    Provides usage information about the installed X-Pack features.
  • Watcher APIs
    略。Watcher?又是什么玩法。。
  • Definitions
    权限相关的。
    ——————————————————————————————————

ES core 源码大纲与部分源码走读

core模块代码结构(基于5.6.8版本)

  org.elasticsearch:
    |
    action 高 定义了集群内外所有交互的请求和执行逻辑。
    |   底层抽象 GenericAction Action ActionRequest ActionResponse 
    |   上层实现 admin/bulk/delete/explain/get/index/search等各类Action以及相关的东西
    |
    bootstrap 低 进程启动器,检查和校验 
    |
    cli 低 一些es脚本执行器
    |
    client 高 定义了集群内外交互的客户端封装(tcp请求)继承体系:
    | ElasticsearchClient 基础接口 主要是execute方法 
    | \
    |  IndicesAdminClient 定义了索引相关元数据操作的客户端接口 由 IndicesAdmin 实现,在AbstractClient内部对象
    |  ClusterAdminClient 定义了集群相关元数据操作的客户端接口 由 ClusterAdmin 实现,在AbstractClient内部对象
    |  Client 定义了一系列通用操作如search/get/index/等等 封装了 AdminClient 
    |  \
    |   AbstractClient 定义了上面那些接口到Action的绑定关系
    |   \
    |    TransportClient 封装了节点连接信息的客户端,这是必然的。客户端肯定需要知道什么请求需要向哪些节点发
    |    
    |    实际上ES官方后续会弃用tcp客户端,而全部请求转为high level http客户端 
    |   
    cluster TODO 高 负责集群状态同步,集群元数据交互 事件广播
    |   重点:ClusterService
    |           1)通过里面的LocalNodeMasterListeners维护自己是否是master状态,触发添加的监听器
    |           2)最新状态保存在原子类AtomicReference state
    |           3)submitStateUpdateTask 可以提交集群状态更新事件。。
    |
    |        InternalClusterInfoService 实现了ClusterInfoService接口 用来给内部对象获取集群一些状态信息如磁盘,内部用的
    |           ClusterInfo:封装了各节点磁盘可用空间 shard大小 shard路由表
    |           The InternalClusterInfoService only runs on the master node.
    |           每30秒更新一次这些信息。主要是给AllocationService提供决策依据。
    |
    |        AllocationService 
    |           1)通过RoutingService起作用
    |           2)reroute 方法 
    |           
    common 低 一些公共定义和函数 算法 数据结构 不需要关注
    |
    discovery 低 集群发现和master选举的的 暂不需要关注
    |
    env TODO 低 维护进程环境变量的。关注 NodeEnvironment
    |
    gateway TODO 高 是集群状态同步的核心服务,负责持久化和同步集群state信息到本地文件
    |
    http 低 http底层传输接口。不需要关注 HttpServerTransport 
    |
    index TODO 高 索引/shard各种定义,封装 操作入口 
    |
    indices TODO 高 对整个索引层面的控制和维护(index更倾向于单个索引/shard)。内存使用,空闲索引关闭,flush控制,cache管理等
    |
    ingest 低 索引数据预处理的。不需要关注
    |
    monitor 低 维护了一些进程信息,gc信息,fs信息,os信息。核心MonitorService
    |
    node 高 封装了整个进程的所有层次的对象。Node.start() 真正启动整个服务 
    |
    plugins 高 提供了强大丰富的ES扩展方式,包括 网络交换,TCP/HTTP请求,节点发现,查询,类型解析,分词等。
    |       重点关注 ActionPlugin@getActions @getActionFilters @getRestHandlers 都是最常见的ES插件扩展方式      
    |
    repositories 低 不需关心 
    |
    rest 高 负责rest请求的路由和执行生命周期。
    |    核心 RestHandler是一个接口,每一个具体的rest请求都要implement,xxxRestAction等
    |        RestController实现了各个handler的注册和路由->dispatchRequest->handleRequest
    |
    script 低 扩展查询脚本的。不需关注 
    |
    search 低 待研究。集群检索服务,核心SearchService,负责执行底层的shard相关的各种查询request
    |
    snapshots 低 集群备份的
    |
    tasks 低 管理task的。核心TaskManager,暂不清楚task和各种request怎么联系起来
    |
    template 低 不需关注
    |
    transport 高 维护集群TCP交互的。核心TransportService,TransportRequest,TransportResponse
    |
    threadpool 低 管理进程线程池的。核心ThreadPool,提供方法获得不同类型executor,定时schedule某些任务
    |
    tribe 低 集群邦联的。只有一个服务类TribeService;维护其下所有child集群并合并它们的集群状态作为统一视图
    |
    watcher TODO ,低 ,核心是ResourceWatcherService,定期检查资源状态并提供监听器注册回调

写数据流程

1)TransportBulkAction.doExecute 
2)检查集群是否阻塞写请求
3)处理需要auto create的index
4) BulkOperation.duRun
5) 对IndexRequest的id/routing的属性填充
6)对bulk请求构造ShardId -> Operations mapping,合并到同一个shard的请求
7)BulkShardRequest
8)TransportShardBulkAction.execute(BulkShardRequest)
9)TransportReplicationAction(TransportShardBulkAction的父类)
         new ReroutePhase((ReplicationTask) task, request, listener).run();
         ReroutePhase.doRun 
10)检查集群、索引级的阻塞信息
11)找primary:ShardRouting primary = primary(state)
        找目标节点,确认是本地还是远程执行
          final DiscoveryNode node = state.nodes().get(primary.currentNodeId());
            if (primary.currentNodeId().equals(state.nodes().getLocalNodeId())) {
                performLocalAction(state, primary, node, indexMetaData);
            } else {
                performRemoteAction(state, primary, node);
            }

        performAction

        transportService.sendRequest(node, action, requestToPerform, transportOptions
        (实际封装成 ConcreteShardRequest)

12)transportService接收消息,路由到Handler
        【 在TransportReplicationAction注册的(transportService.registerRequestHandler)】
         >> -------------------
            transportService.registerRequestHandler(transportPrimaryAction, () -> new ConcreteShardRequest<>(request), executor,
                new PrimaryOperationTransportHandler());
            以及副本处理handler

            transportService.registerRequestHandler(transportReplicaAction,
            () -> new ConcreteShardRequest<>(replicaRequest),
            executor, true, true,
            new ReplicaOperationTransportHandler());    
            -------------------
        PrimaryOperationTransportHandler中
            new AsyncPrimaryAction(request.request, request.targetAllocationID, primaryTerm, channel, (ReplicationTask) task).run();
13)找到IndexShard对象 
        IndexShard indexShard = getIndexShard(shardId);
        校验IndexShard的primary合法性,primary term,allocationid (为什么?)
        返回到AsyncPrimaryAction的onResponse(拿到reference【PrimaryShardReference】后的动作,这一步封装是为了加锁和同步 状态控制等)

14)封装ReplicationOperation.execute
        checkActiveShardCount //检查写入必要条件 
        PrimaryShardReference.perform(request); //这里才到shard Lucene对象相关操作 ( BulkShardRequest )
            --> shardOperationOnPrimary
                executeBulkItemRequest
                executeIndexRequestOnPrimary // engine 操作 
                    --> 生成Engine内部操作封装类 Operation(这里是Index,此外还有Delete)这一步进行 parse/数据校验
                        如有必要 更新Mapping(新字段)
                    --> engine.index  IndexResult index(Index index)
                    --> engine.indexIntoLucene(Index index, IndexingStrategy plan)
                        indexWriter.addDocument
                        //如果没有IO异常、Lucene异常 返回IndexResult 
                        (IndexingStrategy干什么用?TODO)
                    --> 写translog(注:并不是先写translog。如果先写translog,写lucene 时失败,则还需要对translog进行回滚处理 写translog失败的概率更低?) 记录location 

                根据结果构造IndexResponse
15)返回 ReplicationOperation
        这时已经写完primary,拿到inSyncAllocationIds。
        ** Set inSyncAllocationIds = getInSyncAllocationIds(primaryId, clusterState);
        ** 如果active中的shard 不在inSyncAllocationIds集合里,标记为stale (作用?比如正在恢复或同步中的副本?)

        哪些replicate节点需要进行执行?除了跳过unassigned状态的shard,循环执行 并收集结果
            performOnReplicas 通过transportservice发送到目标节点
        如果副分片写失败,向master发送shardFailed消息下线处理

ES所有TCP请求 (ActionRequest) 的层次

    Action封装了请求到处理的路由逻辑。
    GenericAction 
    |
    \Action>extends GenericAction
    |
     \GetAction 等具体的Action。

    Action只是封装了一些对象,真正实现 ‘什么Transport请求去处理这个Action’ 是由ActionModule里面的依赖注入绑定的,如:

    actions.register(ClusterRerouteAction.INSTANCE, TransportClusterRerouteAction.class); 

    ActionRequest代表了请求的具体内容。
    TransportMessage implements Streamable, Writeable
    |
     \TransportRequest extends TransportMessage implements TaskAwareRequest
      |
       \ActionRequest extends TransportRequest
        |
         \GetRequest等具体的Request

    1)请求怎么构造?
        一系列路由和接口,依赖注入封装了绑定关系。照葫芦画瓢即可

    2)请求交给哪些节点执行?
        TransportNodesAction,看具体的Action是否继承了这个类。这个类表示往可选节点执行对应请求。
        如果不指定节点,默认是所有节点@TransportNodesAction.AsyncAction
            if (request.concreteNodes() == null) {
                resolveRequest(request, clusterService.state());
                assert request.concreteNodes() != null;
            }

es如何进行集群信息同步 clusterstate

clusterstate包含了整个ES进程的所有状态信息。重要的如:
    private final RoutingTable routingTable;
    private final DiscoveryNodes nodes;
    private final MetaData metaData;
    private final ClusterBlocks blocks;
    为了节省网络带宽,相邻的两个版本以增量方式发送 diff数据

    由ClusterService进行集群状态同步和信息发布
        ClusterService:SubmitStateUpdateTask---> runTasks
        1)根据最新状态重新确定和节点的连接
        2)把changeState推送到所有节点
        3)更新集群配置
        4)通知listeners变更

    具体发送的action是PublishClusterStateAction
        ES的Master要向所有node发送一个状态变更的时候,需要有两个过程。一个叫send一个叫commit。目的就是保证集群状态的一致性。master首先发送send请求,如果有足够的节点发送了响应,那接下来master节点再发送commit请求,这时候其它节点才开始执行。
        状态分发
            1.调用sendClusterStateToNode()向所有nodes发送这个状态变更的通知,其他节点接收到的话调用handleIncomingClusterStateRequest()来处理这个事件,这时它仅仅把这个事件存放在queue里而不是立刻应用这个状态变更,只回复一个确认,因为它需要等待Master的命令
            2.Master会调用sendCommitToNode()到所有的nodes ,当然,之前它必须得到过半数的确认说收到这个变更才行,那么所有收到这个请求的nodes就会调用handleIncomingClusterStateRequest()去消费这个变更,注意在此之前这些nodes需要消费完queue之前的所有变更才行。而Master会启动一个timer来等待Response。
            3.状态更新是有队列的,队列执行有同步,也就是说,前一个状态没有更新完,下一个状态不会执行。
            
    典型的类似分布式两阶段提交方式

    **会定时广播集群元数据更新(应该也有立即直接更新的)
    [TRACE][o.e.c.InternalClusterInfoService] [node-1] Scheduling next run for updating cluster info in: 30s

线程池threadPool结构,每个线程在做什么

    是一个map,由各种功能map组成的多个线程池集合
    线程池由ExecutorBuilder创建,保存在ExecutorHolder
    维护的线程池是封装的EsThreadPoolExecutor extends ThreadPoolExecutor,这是一个带有队列控制的线程池
    threadpool的类型:
        CACHED:
            没有限制的threadpool,当有pending的请求时就会产生一个线程,线程池中没有使用的线程会在过期时间结束后消亡,这里的过期时间默认为5min,当然也可以自行设置,CACHE类型是专门为GENERIC(下面会说到这个)而设置的。
        FIXED:
            大小固定设置的threadpool,它有一个queue来存放pending的请求,其中pool的大小默认是core*5,queue_size默认是-1(即是无限制);
        SCALING:
            拥有可变大小的pool,其值可在1和设置值之间;

    默认总共有15个线程池:
    CACHED
        GENERIC:通用的操作,比如node的discovery
    FIXED
        LISTENER:主要用作java client的执行,默认大小halfProcMaxAt10;
        GET:用作get操作,默认大小availableProcessors,queue_size为1000;
        INDEX:用作index或delete操作,默认大小availableProcessors,queue_size为200;
        BULK:用作bulk操作,默认大小为availableProcessors,queue_size为50;
        SEARCH:用作count或是search操作,默认大小((availableProcessors * 3) / 2) + 1;queue_size为1000;
        SUGGEST:用作suggest操作,默认大小availableProcessors,queue_size为1000;
        PERCOLATE:用作percolate,默认大小为availableProcessors,queue_size为1000;
        FORCE_MERGE:用作force_merge操作(2.1之前叫做optimize),默认大小为1;
    SCALING
        MANAGEMENT:用作ES的管理,比如集群的管理;默认大小5,keep alive时间为5min;
        FLUSH:用作flush操作,默认大小为halfProcMaxAt5,keep alive时间为5min;
        REFRESH:用作refresh操作,默认大小为halfProcMaxAt10,keep alive时间为5min;
        WARMER:用作index warm-up操作,默认大小为halfProcMaxAt5,keep alive时间为5min;
        SNAPSHOT:用作snapshot操作,默认大小为halfProcMaxAt5,keep alive时间为5min;
        FETCH_SHARD_STARTED:TODO 用作fetch shard开始操作,默认大小availableProcessors * 2,keep alive时间为5min;
        FETCH_SHARD_STORE:TODO 用作fetch shard存储操作,默认大小availableProcessors * 2,keep alive时间为5min

——————————————————————————————————

ES项目经验与优化实践

经历过的一些项目

  • 舆情项目
    对博客、论坛、爬虫数据建索引
    使用spark sql + ES hadoop 插件
    自定义谓词下推(lucene 语法)
    日期分区与分区裁剪
    排序下推与 limit 快速返回
    优化ES RDD的 json序列化反序列化

  • 日志分析
    写了个自定义插件
    存储自定义审计数据,存在自身集群

  • 监测系统数据存储
    X-Pack使用 审计与权限
    指标与事件信息存储,提供检索与排序
    数据周期存储滚动删除(按天、按月、按年)
    冷热分离 标签 + index-lifecycle-management(从es 6.6)

  • URL分析系统
    流量庞大,需要定制入库程序
    跳过ES数据加载调用链:

    HTTP流程、JSON解析、请求转发、mapping检查、mvcc控制、translog等

    直接走:

    Kafka > Avro > Lucene > merge > add Indices > ES shard 目录的流程

  • 使用过的ES监控与管理插件
    bigdesk
    监控,可视化做的较好,缺少查询,REST执行器;缺少索引管理,各种索引相关统计;更注重于监控节点负载;
    head
    监控和可视化不行;有简单好用的查询编辑,REST执行器;有一组快捷菜单,用以展现集群的各种状态
    早期2.x轻量,总共15M不到
    5.x后依赖nodejs,绿色安装包也到150M了
    es-HQ
    监控和可视化不错;对和索引节点能做很多维度的统计和展示;有一组快捷菜单,用以展现集群的各种状态;有REST执行器
    缺点是依赖Python3,做个独立运行环境需要240M
    kopf (已不再维护,迁移到了https://github.com/lmenezes/cerebro)
    监控+统计+执行器基本都有,功能强大
    基于java服务,好修改

  • 界面化安装CDH
    自定义CDH集成parcel
    通过预设角色的方式生成实例

  • 常用分词器

    standard:默认分词,英文按空格切分,中文按照单个汉字切分。
    smartcn:常用的中文分词器,Smartcn是ICTCLAS简化后的版本;没有词性标注;没有人名、地名识别;没有采用隐马模型进行分词,而是采用的动态规划计算最短路径。
    cjk:根据二元索引对中日韩文分词,可以保证查全率。
    nGram:可以将英文按照字母切分,结合ES的短语搜索(match_phrase)使
    IK:比较热门的中文分词,能按照中文语义切分,可以自定义词典。
    pinyin:可以让用户输入拼音,就能查找到相关的关键词。
    aliws:阿里巴巴自研分词,支持多种模型和分词算法,词库丰富,分词结果 准确,适用于电商等对查准要求高的场景。

  • 做过的ES源码二次开发 侵入性改动
    shard的一致性问题 insync状态控制
    ES稳定性优化,能自动检测容忍掉盘,盘只读,节点离线故障

优化总结

从我的使用经验来看,大部分ES特性是用不到的。比如聚合、script、相关度等等。
很多人用ES只是拿来做文档存储检索,精确模糊匹配,最核心的需求是 集群稳定 ,甚至更新删除动作都没有。
很多ES的问题,通常是设置不合理和使用不当的问题。

  • 尽量把索引mapping做到最简化,关闭不必要特性如是否检索,是否索引,norms,docvalues等。
  • 索引shard数要合理 单个shard不要太大(100GB内为佳)
  • 注意设置shard分布控制 防止热点问题 即同一索引的shard尽量打散到集群
    index.routing.allocation.total_shards_per_node
  • 多索引多业务合理使用路由管理相近数据,避免互相干扰
    index.routing.allocation.include._ip
  • 没有frozen功能的版本 注意关掉不用的索引 减少段内存占用
  • 监控集群shard总数变化 经验值是4万个以上的集群 会有各种元数据超时问题(因为gateway要同步集群元数据 shard很多的时候会非常慢)
  • 副本建议要么不要,要么1个即可 多了其实意义不大
  • 会调整一些影响节点磁盘平衡 启动速度控制 rebalance速度控制相关的参数
    cluster.routing.allocation.*
    indices.recovery.*
  • 磁盘选择上SSD综合性能大概是SATA的3~10倍(考虑加载、检索、有无缓存的对照测试)
  • ES遇到的最多的问题是shard unassigned
    ES现有机制:只要replica处于assigned状态(即非unassigned),包括initializing、relocating、started等状态,向primary写入的任何数据都会被同步至replica
    所以有副本情况下,要保证索引及集群不出现red状态,需要保证replica不出现unassigned状态。
    所以不要轻易忽略yellow状态。unassigned通常是磁盘故障或索引、集群设置导致副本不能分配,需要及早排除。
  • 大量入库前 调整refresh参数,batch大小,队列大小等 经验值30MB(plain text)左右是单服务器加载速度上限(Intel E5 x 10 4TB SATA配置的服务器)
  • ES对内存非常依赖(jvm缓存词典、索引文件等以及系统缓存mmap文件映射),因此有时候会触发OS的OOM killer机制 注意不要配置过高比例物理内存

附 一些好的ES学习网站和书籍

<>
很深入的研究ES的源码 二次开发与原理理解推荐。难度不小 慎入

<>
很老的书了,Lucene入门相关。不清楚现在有没有更新的书

Apache中文网-ElasticSearch
目前版本只是5.4 其实就是官网文档的直接翻译
虽然不全但是也值得一看。适合新手 看到满页英文就头大的

Elastic中文社区
这里有大量的实践案例、技术博客。牛人汇聚

ES官网文档
建议真的去读一遍

你可能感兴趣的:(【工作】ElasticSearch技术全脉络)