概述和摘要
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 类似子查询的聚合??
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 fieldMapping 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
<dependency>
<groupId>org.elasticsearch.plugin</groupId>
<artifactId>x-pack-sql-jdbc</artifactId>
<version>7.6.2</version>
</dependency>
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<ClusterState> 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<String> inSyncAllocationIds = getInSyncAllocationIds(primaryId, clusterState);
** 如果active中的shard 不在inSyncAllocationIds集合里,标记为stale (作用?比如正在恢复或同步中的副本?)
哪些replicate节点需要进行执行?除了跳过unassigned状态的shard,循环执行 并收集结果
performOnReplicas 通过transportservice发送到目标节点
如果副分片写失败,向master发送shardFailed消息下线处理
ES所有TCP请求 (ActionRequest) 的层次
Action封装了请求到处理的路由逻辑。
GenericAction<Request extends ActionRequest, Response extends ActionResponse>
|
\Action<Request extends ActionRequest, Response extends ActionResponse, RequestBuilder extends ActionRequestBuilder<Request, Response, RequestBuilder>>extends GenericAction<Request, Response>
|
\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学习网站和书籍
<<Elasticsearch源码解析与优化实战>>
很深入的研究ES的源码 二次开发与原理理解推荐。难度不小 慎入
<<Lucene原理与代码分析>>
很老的书了,Lucene入门相关。不清楚现在有没有更新的书
Apache中文网-ElasticSearch
目前版本只是5.4 其实就是官网文档的直接翻译
虽然不全但是也值得一看。适合新手 看到满页英文就头大的
Elastic中文社区
这里有大量的实践案例、技术博客。牛人汇聚
ES官网文档
建议真的去读一遍