文章目录
基本介绍
简单介绍
- Elasticsearch 是一个分布式、可扩展、实时的搜索与数据分析引擎。它是全文检索、结构化搜索、分析以及这三个功能的组合。
- Elasticsearch 在 Apache Lucene 的基础上开发而成。
- Elasticsearch 以其简单的 REST 风格API、分布式特性、速度和可扩展性而闻名。
Lucene
基本概念
索引(Index)和分片(Shards)
索引(Index)
索引是ES中最大的数据存储概念,它是由许多具有相同特征的文档组成的一个集合,对应 MySQL 中的 Database 。
由于在ES7.0之后逐渐废除Type类型,所以Index从”数据库“的概念变成了实际上的”表“概念。但是要注意Index只是一个逻辑上的概念,真实的数据是分开存储在各个分片中的。
分片(Index)
每个分片都是一个Lucene索引实例,可以将其视作一个独立的搜索引擎,它能够对Elasticsearch集群中的数据子集进行索引并处理相关查询。
分片包含两种类型:主分片(Primary Shard)、副本分片(Replica Shard)
- 主分片:主分片是一个完整索引的一部分数据
由于所有的数据都会在主分片中存储,所以主分片决定了文档存储数量的上限,但是一个索引的主分片数在创建时一旦指定,那么主分片的数量就不能改变,这是因为当索引一个文档时,它是通过其主键(默认)Hash到对应的主分片上(类似于RDBMS分库分表路由策略),所以我们一旦修改主分片数量,那么将无法定位到具体的主分片上。在mapping时我们可以设置number_of_shards值,最大值默认为1024。
- 副本分片:副本是一个主分片的镜像
我们可以为一个主分片根据实际的硬件资源指定任意数量的副本分片,上边已经说过,每个分片都可以处理查询,所以我们可以增加副本分片的资源(相应硬件资源提升)来提升系统的处理能力。同样,在mapping时,可以通过number_of_replicas参数来设置每个主分片的副本分片数量,对于一个索引,除非重建索引否则不能调整主分片的数目 (number_of_shards),但可以随时调整 replica 的数目 (number_of_replicas)。
副本分片的作用主要
- 容灾:primary 分片丢失,replica 分片就会被顶上去成为新的主分片,同时根据这个新的主分片创建新的 replica,集群数据安然无恙;
- 提高查询性能:replica 和 primary 分片的数据是相同的,所以对于一个 query 既可以查主分片也可以查从分片,在合适的范围内多个 replica 性能会更优(但要考虑资源占用也会提升 [cpu/disk/heap]),另外 Index Request 只能发生在主分片上,replica 不能执行 Index Request。
文档(Document)
- 文档(Document):相当于关系表的数据行,存储数据的载体,包含一个或多个存有数据的字段;
- 字段(Field):文档的一个Key/Value对;
- 词(Term):表示文本中的一个单词;
- 标记(Token):表示在字段中出现的词,由该词的文本、偏移量(开始和结束)以及类型组成;
ElasticSearch是基于Lucene框架的全文搜索引擎,将所有文档的信息写入到倒排索引(Inverted Index)的数据结构中,倒排索引建立的是索引中词和文档之间的映射关系,在倒排索引中,数据是面向词(Term)而不是面向文档的。
段(Segment)
一个段是一个Lucene索引内部的分片,实际上段不是es原生的概念,而是Lucene的特性,每当Lucene写入一次数据并落盘时都会产生一个分段,分段是不可变的,只能读取和删除。
集群(Cluster)与节点(Node)
节点(Node)
一个 ES 节点就是一个运行的 ES 实例,可以实现数据存储并且搜索的功能。每个节点都有一个唯一的名称作为身份标识,如果没有设置名称,默认使用 UUID 作为名称。最好给每个节点都定义上有意义的名称,在集群中区分出各个节点。
一个机器可以有多个实例,所以并不能说一台机器就是一个 node,大多数情况下每个 node 运行在一个独立的环境或虚拟机上。
节点类型
- master 节点: 集群中的一个节点会被选为 master 节点,它将负责管理集群范畴的变更,例如创建或删除索引,添加节点到集群或从集群中删除节点。master 节点无需参与文档层面的变更和搜索,这意味着仅有一个 master 节点并不会因流量增长而成为瓶颈。任意一个节点都可以成为 master 节点。
- data 节点: 持有数据和倒排索引。
默认情况下,每个节点都可以通过设定配置文件 elasticsearch.yml 中的 node.data 属性为 true (默认) 成为数据节点。如果需要一个专门的主节点 (一个节点既可以是 master 节点,同时也可以是 data 节点),应将其 node.data 属性设置为 false。
- client 节点: 如果将 node.master 属性和 node.data 属性都设置为 false,那么该节点就是一个客户端节点,扮演一个负载均衡的角色,将到来的请求路由到集群中的各个节点。
集群(Cluster)
- 在ES中,集群是由一个或多个ES节点组成的,每个集群都有一个唯一的名称/标识符。
- 节点通过设置集群名称,在同一网络中发现具有相同集群名称的节点,组成集群。
- 每一个集群中有一个Master节点,若是这个Master节点挂了,集群可以用其他的节点代替。如果在同一网络中只有一个节点,则这个节点成为一个单节点集群。
集群状态
- Green:所有主分片和从分片都准备就绪(分配成功)
如果此时有一台机器挂了(假设一台机器一个实例),数据都不会丢失,集群会变成 Yellow 状态。 - Yellow:当有副本丢失的时候处于该状态。所有主分片准备就绪,但存在至少一个主分片、对应的从分片没有就绪
此时集群属于警告状态,意味着集群高可用和容灾能力下降,如果刚好 A该分片所在的机器挂了,而从分片还处于未就绪状态,那么 A 的数据就会丢失(查询结果不完整),此时集群进入 Red 状态。 - Red:当有主分片丢失的时候处于该状态,至少有一个主分片没有就绪(直接原因是找不到对应的从分片成为新的主分片),此时查询的结果会出现数据丢失(不完整)。
索引缓冲区(IndexBuffer)
当我们写入一条数据时,数据并不会直接写入底层文件中,而是会先放入某一个实例下的index buffer内,当index buffer触发策略同步索引时才会真正发起一次写入操作,底层Lucene则是会发起一次 segment commit,产生一个新的段,此时数据可以被检索到。这个过程我们可以称之为 refresh
Index Buffer同步策略有2个:
- 当达到 index.refresh_interval的时长,执行一次refresh
- 当IndexBuffer写满时,触发一次 refresh。
策略1默认时间为1秒,策略2 IndexBuffer默认大小为当前es进程内存的10%。因此我们说es是准实时的搜索引擎,数据写入到能被查询到至少有1秒的延迟。
由此我们还可以得出结论:需要实时准确查询得到结果的业务(例如对账)不可以依赖es。
事务日志(TransLog)
用过Mysql、Redis和Hbase等分布式系统的同学对事务日志一定不陌生,这类日志通常也被称作:WAL(Write Ahead Log),即每次写入数据同时都会将写数据行为记录到一个日志中,这个日志包括了数据本身和操作(插入更新删除),作用是用来在节点发生宕机时对数据进行修复。es的事务日志叫做translog,每当一次写请求到来时es将记录这一次操作,当节点宕机后,es会根据commit point对数据进行恢复。
同时translog也是有大小限制的,每当translog达到一定大小或一定时间时会执行一次flush操作,强制将文件系统缓存中的数据同步到磁盘中(即产生一次磁盘IO),并清空日志。
和数据库对比
RDBS | ES |
---|---|
数据库(database) | 索引(index) |
表(table) | 类型(type)(ES6.0之后被废弃,es7中完全删除) |
表结构(schema) | 映射(mapping) |
行(row) | 文档(document) |
SQL | 查询DSL |
索引 | 反向索引 |
列(column) | 字段(field) |
一次请求的过程
一次搜索
-
首先client的请求经过es的 协调节点(coordinate node),协调节点会解析请求中的preference参数决定是走默认的路由策略还是定制的策略。
preference参数、结果震荡问题 -
假设是默认的路由策略,协调节点将会从本地路由表中找到目标索引有哪些主分片或副本以及分别在哪些节点上。随机选择主或副将请求发往每个shard上,例如你有0,1,2三个主分片一个副本,会发往这三个分片号所在的主分片或副本的节点。
-
query : 每个到达分片对应机器的请求,执行查询语法解析分词等一系列操作后,执行查询操作,默认取回10条数据(请求可以设置)的docId作为结果
-
fetch : 每个shard执行完均返回10条数据到发起请求的协调节点,协调节点聚合这N个节点返回的结果到一条优先队列,取top 10(或搜索时指定条数)个docId,再根据这些docId所在shard,去指定的机器获取其他属性值并返回。
一次写入
写入相对复杂点
- 请client求还是来到协调节点,协调节点首先根据文档的id计算一个hash值,并通过hash值定位到应该去哪个主分片;如果用户没有设置id,则会使用 TimeBasedUUID算法并做base64最终生成一个20位长度的字母大小写和数字混合的字符串,保证唯一性,并根据这个id计算hash找到应该去哪个主分片
- 请求路由到对应的主分片,立马写入IndexBuffer,并同时记录下当前操作到Translog,当IndexBufferda’dao触发条件时,refresh数据到文件系统缓存中,此时Lucene产生一个可被检索的segment。
- 当Translog达到触发条件(文件超过大小或一定时间)则会触发一次操作系统的缓存刷写(fsync),产生一次磁盘io,并清空日志,此时数据被真正落地到磁盘。
- 在步骤2执行完后,当前节点会将这个请求同步到其他副本分片上,副本还会重复3这个步骤。根据集群的数据一致性配置,写入请求会决定是立即告诉协调节点写入完成,还是等待半数副本同步完成,还是全部副本同步完成时再告诉协调节点写入完成。
- 协调节点接收到写入完成的响应,并响应client写入完成。
具体使用
集群 节点
添加索引
动态映射
新字段的自动检测和添加称为 动态映射。
要索引文档,不需要先创建索引、定义映射类型和定义字段——只需索引文档,索引、类型和字段将自动显示。
创建data索引、_doc映射类型、字段count和其数据类型long。
PUT data/_doc/1
{ "count": 5 }
可以通过将dynamic参数设置为true或runtime来指示 Elasticsearch 根据传入文档动态创建字段。
当启用动态字段映射时,Elasticsearch 使用下表中的规则来确定如何为每个字段映射数据类型。
下表中的字段数据类型是 Elasticsearch 动态检测的唯一 字段数据类型。您必须显式映射所有其他数据类型。
JSON data type | “dynamic”:“true” | “dynamic”:“runtime” |
---|---|---|
null | No field added | No field added |
true or false | boolean | boolean |
double | float | double |
integer | long | long |
object | object | No field added |
array | 取决于数组中第一个non-null 值 | 取决于数组中第一个non-null 值 |
日期类字符串 | date | date |
数值类字符串 | float or long | double or long |
非日期或数值字符串 | text 并且字段设置为keyword | keyword |
- 可以在document和object级别禁用动态映射 。将dynamic参数设置为 false忽略新字段,并在 Elasticsearch 遇到未知字段时拒绝文档。
- 可以自定义日期和数字的动态字段映射规则 。可以使用dynamic_templates定义其他动态字段的自定义映射规则
显示映射
创建新索引
PUT /my-index-000001
{
"mappings": {
"properties": {
"age": { "type": "integer" },
"email": { "type": "keyword" },
"name": { "type": "text" }
}
}
}
向现有索引添加字段
PUT / my-index-000001 / _mapping
{
"properties":
{
"employee-id":
{
"type": "keyword",
"index": false
}
}
}
除了支持的映射参数之外,我们无法更改现有字段的映射或字段类型。更改现有字段可能会使已编入索引的数据无效。
查看某个索引的映射
GET / my-index-000001 / _mapping
查看特定字段的映射
GET / my-index-000001 / _mapping / field / employee-id
查询
API
常见面试问题
是使用中需要关注的点