01 Es入门介绍
lucene是一类jar包,是最先进、功能最强大的搜索库,直接基于lucene开发是非常复杂的。lucene的api复杂(实现一些简单的功能要写大量的java代码),直接使用lucene的api还需要深入理解原理(各种索引结构)。
elasticsearch基于lucene开发,隐藏复杂性,提供简单易用的restful api接口、 java api接口(还有其他语言的api接口),es具有如下特点:
- 分布式的文档存储引擎
- 分布式的搜索引擎和分析引擎
- 分布式,支持PB级数据
- 近实时特性
- 1、从写入数据到数据可以被搜索到有一个小延迟(大概1秒)
- 2、基于es执行搜索和分析可以达到秒级,速度快
- 开箱即用,优秀的默认参数,不需要任何额外设置,完全开源
Elasticsearch索引和Lucene索引的对比:
一个分片就是一个Lucene的索引,也就是一个包含倒排索引的文件目录。它默认存储原始文档的内容,再加上一些额外的信息,如词条字典和词频,这些都能帮助到搜索。词条字典将每个词条和包含该词条的文档映射起来。搜索的时候,Elastisearch没有必要为了某个词条扫描所有的文档,而是根据这个字典快速地识别匹配的文档。
Elasticsearch索引被分解为多块:分片。所以一个Elasticsearch的索引由多个Lucene的索引组成。
词频使得Elasticsearch可以快速地获取某篇文档中某个词条出现的次数。这对于计算结果的相关性得分非常重要。例如,如果搜索“morris",包含多个“morris”的文档通常更为相关。Elasticsearch将给它们更高的得分,让它们出现在结果列表的更前面。
一个集群(cluster)有多个节点(node)组成。每个节点属于哪个集群是通过一个配置(集群名称,默认是elasticsearch) 来决定的,对于中小型应用来说,刚开始一个集群就一个节点很正常。
一个节点可以通过配置集群名称(cluster.name)的方式来加入一个指定的集群。默认情况下,每个节点都会被安排加入到一个叫做“elasticsearch”的集群中,这意味着,如果你在你的网络中启动了若干个节点,并假定它们能够相互发现彼此,它们将会自动地形成并加入到一个叫做“elasticsearch”的集群中。
集群中的一个节点,节点一个一个名称(默认是随机分配的)。节点名称很重要 (在执行运维管理操作的时候),默认节点会去加入一个名称为“elasticsearch’的 集群。如果直接启动一堆节点,那么他们会自动组成一个ES集群。当然,一个节点也可以组成一个集群。
单台机器无法存储大量数据,es可以将一个索引中的数据切分为多个shard,分布在多台服务器上存储。有了shard就可以横向扩展,存储更多数据,让搜索和分析等操作分布到多台服务器上去执行,提升吞吐量和性能。每个shard都是一个lucene index。
任何一个服务器随时可能故障或宕机,此时shard可能就会丢失,因此可以为每个shard创建多个replica副本。replica可以在shard故障时提供备用服务,保证数据不丢失,多个replica还可以提升搜索操作的吞吐量和性能。primary shard(建立索引时一次设置,创建后不能修改,默认5个),replica shard(随时修改数量,默认1个),默认每个索引10个shard,5个primary shard,5个replica shard,最小的高可用配置,是2台服务器。
副本是分片的副本。分片有主分片(primary Shard)和副本分片(replida Shard)之分。
一个Index数据在物理上被分布在多个主分片中,每个主分片只存放部分数据。
每个主分片可以有多个副本,叫副本分片,是主分片的复制。
一个document相当于关系型数据库中的一行记录。是ES中最小的数据单元,一个document就是一条数据。通常用json格式表示。一个document中有多个字段(filed)。
{
"product_id": "1",
"product_name": "高露洁牙膏",
"product_desc": "高效美白",
"category_id": "2",
"category_name": "日化用品"
}
扩展:
文档优势:可以表示对象嵌套关系。是一种面向对象的存储方式。
面向文档的搜索分析引擎
(1)应用系统的数据结构都是面向对象的,复杂的 (2)对象数据存储到数据库中,只能拆解开来,变为扁平的多张表,每次查询的时候还得还原回对象格式,相当麻烦 (3)ES是面向文档的,文档中存储的数据结构,与面向对象的数据结构是一样的,基于这种文档数据结构,es可以提供复杂的索引,全文检索,分析聚合等功能 (4)es的document用json数据格式来表达
public class Employee { private String email; private String firstName; private String lastName; private EmployeeInfo info; private Date joinDate; } private class EmployeeInfo { private String bio; // 性格 private Integer age; private String[] interests; // 兴趣爱好 } EmployeeInfo info = new EmployeeInfo(); info.setBio("curious and modest"); info.setAge(30); info.setInterests(new String[]{"bike", "climb"}); Employee employee = new Employee(); employee.setEmail("zhangsan@sina.com"); employee.setFirstName("san"); employee.setLastName("zhang"); employee.setInfo(info); employee.setJoinDate(new Date());employee对象:里面包含了Employee类自己的属性,还有一个EmployeeInfo对象
两张表:employee表,employee_info表,将employee对象的数据重新拆开来,变成Employee数据和EmployeeInfo数据 employee表:email,first_name,last_name,join_date,4个字段 employee_info表:bio,age,interests,3个字段;此外还有一个外键字段,比如employee_id,关联着employee表
{ "email": "zhangsan@sina.com", "first_name": "san", "last_name": "zhang", "info": { "bio": "curious and modest", "age": 30, "interests": [ "bike", "climb" ] }, "join_date": "2017/01/01" }我们就明白了es的document数据格式和数据库的关系型数据格式的区别
索引,包含一堆有相似结构的文档数据,比如可以有一个客户索引,商品分类索引,订单索引,索引有一个名称。一个index包含很多document,一个index就代表了一类类似的或者相同的document。比如说建立一个product index,商品索引,里面可能就存放了所有的商品数据,所有的商品document。
一个索引可以理解成一个关系型数据库。
类型,每个索引里都可以有一个或多个type,type是index中的一个逻辑数据分类,一个type下的document,都有相同的field,比如博客系统,有一个索引,可以定义用户数据type,博客数据type,评论数据type。
如何理解type?
商品index,里面存放了所有的商品数据,即商品document。但是商品分很多种类,每个种类的document的field可能不太一样,比如说电器商品,可能还包含一些诸如售后时间范围这样的特殊field;生鲜商品,还包含一些诸如生鲜保质期之类的特殊field。
此时,商品index中就有多种type:日化商品type,电器商品type,生鲜商品type
日化商品type:product_id,product_name,product_desc,category_id,category_name 电器商品type:product_id,product_name,product_desc,category_id,category_name,service_period 生鲜商品type:product_id,product_name,product_desc,category_id,category_name,eat_period
每一个type里面,都会包含一堆document。
{
"product_id": "2",
"product_name": "长虹电视机",
"product_desc": "4k高清",
"category_id": "3",
"category_name": "电器",
"service_period": "1年"
}
{
"product_id": "3",
"product_name": "基围虾",
"product_desc": "纯天然,冰岛产",
"category_id": "4",
"category_name": "生鲜",
"eat_period": "7天"
}
一种type就像一类表,比如user表,order表。
注意:
ES 5.x中一个index可以有多种type。
ES 6.x中一个index只能有一种type。
ES 7.X以后已经移除type这个概念。
mapping定义了每个字段的类型等信息。相当于关系型数据库中的表结构。
es提供了一套api,叫做cat api,可以查看es中各种各样的数据
GET /_cat/health?v ?v参数会打印标题栏
如何快速了解集群的健康状况?green、yellow、red?
- green:每个索引的primary shard和replica shard都是active状态的
- yellow:每个索引的primary shard都是active状态的,但是部分replica shard不是active状态,处于不可用的状态(比如因为节点数量不足,导致replica shard无法调度)
- red:不是所有索引的primary shard都是active状态的,部分索引有数据丢失了
为什么现在会处于一个yellow状态?
我们现在就一个笔记本电脑,就启动了一个es进程,相当于就只有一个node。现在es中有一个index,就是kibana自己内置建立的index。由于默认的配置是给每个index分配5个primary shard和5个replica shard,即shards=5,replicas=1,而且primary shard和replica shard不能在同一台机器上(为了容错)。现在kibana自己建立的index是1个primary shard和1个replica shard。当前就一个node,所以只有1个primary shard被分配了和启动了,但是一个replica shard没有第二台机器去启动。
做一个小实验:此时只要启动第二个es进程,就会在es集群中有2个node,然后那1个replica shard就会自动分配过去,然后cluster status就会变成green状态。
epoch timestamp cluster status node.total node.data shards pri relo init unassign pending_tasks max_task_wait_time active_shards_percent
1488006741 15:12:21 elasticsearch yellow 1 1 1 1 0 0 1 0 - 50.0%
epoch timestamp cluster status node.total node.data shards pri relo init unassign pending_tasks max_task_wait_time active_shards_percent
1488007113 15:18:33 elasticsearch green 2 2 2 1 0 0 0 0 - 100.0%
GET /_cat/indices?v
health status index uuid pri rep docs.count docs.deleted store.size pri.store.size
yellow open .kibana rUm9n9wMRQCCrRDEhqneBg 1 1 1 0 3.1kb 3.1kb
索引健康(health),green为正常,yellow表示索引不可靠(单节点),red索引不可用。与集群健康状态一致。
状态(status),表明索引是否打开。
索引名称(index),这里有.kibana和school。
uuid,索引内部随机分配的名称,表示唯一标识这个索引。
主分片(pri),这个就是集群的主分片数。
文档数(docs.count)。
已删除文档数(docs.deleted),这里统计了被删除文档的数量。
索引存储的总容量(store.size),索引的总容量,是主分片总容量的两倍,因为存在一个副本。
主分片的总容量(pri.store.size),主分片容量,是索引总容量的一半。
curl -s http://127.0.0.1:9200/_cat/shards?v
log-000028 0 p STARTED 16456065 4.3gb 100.101.46.23 elasticsearch-3
log-000028 0 r STARTED 16500999 4.1gb 100.101.176.25 elasticsearch-2
log-000024 0 p STARTED 117914041 30.4gb 100.101.46.23 elasticsearch-3
log-000024 0 r STARTED 117914041 30.4gb 100.101.121.22 elasticsearch-4
- index:所有名称
- shard:分片数
- prirep:分片类型,p=pri=primary为主分片,r=rep=replicas为复制分片
- state:分片状态,STARTED为正常分片,INITIALIZING为异常分片
- docs:记录数
- store:存储大小
- ip:es节点ip
- node:es节点名称
PUT /test_index?pretty
health status index uuid pri rep docs.count docs.deleted store.size pri.store.size
yellow open test_index XmS9DTAtSkSZSwWhhGEKkQ 5 1 0 0 650b 650b
yellow open .kibana rUm9n9wMRQCCrRDEhqneBg 1 1 1 0 3.1kb 3.1kb
显示指定mapping创建索引
查看mapping信息
GET /INDEX_NAME
DELETE /test_index?pretty
health status index uuid pri rep docs.count docs.deleted store.size pri.store.size
yellow open .kibana rUm9n9wMRQCCrRDEhqneBg 1 1 1 0 3.1kb 3.1kb
PUT /index/type/id
PUT /ecommerce/product/1
{
"name" : "gaolujie yagao",
"desc" : "gaoxiao meibai",
"price" : 30,
"producer" : "gaolujie producer",
"tags": [ "meibai", "fangzhu" ]
}
{
"_index": "ecommerce",
"_type": "product",
"_id": "1",
"_version": 1,
"result": "created",
"_shards": {
"total": 2,
"successful": 1,
"failed": 0
},
"created": true
}
PUT /ecommerce/product/2
{
"name" : "jiajieshi yagao",
"desc" : "youxiao fangzhu",
"price" : 25,
"producer" : "jiajieshi producer",
"tags": [ "fangzhu" ]
}
PUT /ecommerce/product/3
{
"name" : "zhonghua yagao",
"desc" : "caoben zhiwu",
"price" : 40,
"producer" : "zhonghua producer",
"tags": [ "qingxin" ]
}
es会自动建立index和type,不需要提前创建,而且es默认会对document每个field都建立倒排索引,让其可以被搜索
GET /${INDEX}/${TYPE}/${ID}
GET /ecommerce/product/1
{
"_index": "ecommerce",
"_type": "product",
"_id": "1",
"_version": 1,
"found": true,
"_source": {
"name": "gaolujie yagao",
"desc": "gaoxiao meibai",
"price": 30,
"producer": "gaolujie producer",
"tags": [
"meibai",
"fangzhu"
]
}
}
PUT /ecommerce/product/1
{
"name" : "jiaqiangban gaolujie yagao",
"desc" : "gaoxiao meibai",
"price" : 30,
"producer" : "gaolujie producer",
"tags": [ "meibai", "fangzhu" ]
}
{
"_index": "ecommerce",
"_type": "product",
"_id": "1",
"_version": 1,
"result": "created",
"_shards": {
"total": 2,
"successful": 1,
"failed": 0
},
"created": true
}
{
"_index": "ecommerce",
"_type": "product",
"_id": "1",
"_version": 2,
"result": "updated",
"_shards": {
"total": 2,
"successful": 1,
"failed": 0
},
"created": false
}
PUT /ecommerce/product/1
{
"name" : "jiaqiangban gaolujie yagao"
}
覆盖方式有一个不好,即使必须带上所有的field,才能去进行信息的修改
POST /ecommerce/product/1/_update // _update
{
"doc": {
"name": "jiaqiangban gaolujie yagao"
}
}
{
"_index": "ecommerce",
"_type": "product",
"_id": "1",
"_version": 8,
"result": "updated",
"_shards": {
"total": 2,
"successful": 1,
"failed": 0
}
}
DELETE /ecommerce/product/1 // 根据给出的endpoint,决定删除index、type、doc
{
"found": true,
"_index": "ecommerce",
"_type": "product",
"_id": "1",
"_version": 9,
"result": "deleted",
"_shards": {
"total": 2,
"successful": 1,
"failed": 0
}
}
{
"_index": "ecommerce",
"_type": "product",
"_id": "1",
"found": false
}
为了有个全局的理解,我们首先要知道Elasticsearch索引创建的时候,究竟发生了什么,理解数据在物理是如何上组织的?
默认情况下,每个索引由5个主要分片组成,而每份主要分片又有一个副本,一共10份分片。副本分片对于可靠性和搜索性能很有益处。技术上而言,一份分片是一个目录中的文件,Lucene用这些文件存储索引数据。分片也是Elasticsearch 将数据从一个节点迁移到另一个节点的最小单位。
一个节点是一个Elasticsearch的实例。在服务器上启动Elasticsearch之后,你就拥有了一个节点。如果在另一台服务器上启动Elasticsearch, 这就是另一个节点。甚至可以通过启动多个Elasticsearch进程,在同一台服务器上拥有多个节点。
多个节点可以加入同一个集群。在多节点的集群上,同样的数据可以在多台服务器上传播。这有助于性能,因为Elasticsearch有了更多的资源;这同样有助于稳定性,如果每份分片至少有1个副本分片,那么任何一个节点都可以宕机,而Elasticsearch依然可以进行服务,并返回所有数据。
对于使用Elasticsearch的应用程序,集群中有1个还是多个节点都是透明的。默认情况下,可以连接集群中的任一节点并访问完整的数据集,就好像集群只有单独的一个节点。
一个分片就是一个Lucene的索引,也就是一个包含倒排索引的文件目录。它默认存储原始文档的内容,再加上一些额外的信息,如词条字典和词频,这些都能帮助到搜索。词条字典将每个词条和包含该词条的文档映射起来。搜索的时候,Elastisearch没有必要为了某个词条扫描所有的文档,而是根据这个字典快速地识别匹配的文档。
Elasticsearch索引被分解为多块:分片。所以一个Elasticsearch的索引由多个Lucene的索引组成。
词频使得Elasticsearch可以快速地获取某篇文档中某个词条出现的次数。这对于计算结果的相关性得分非常重要。例如,如果搜索“morris",包含多个“morris”的文档通常更为相关。Elasticsearch将给它们更高的得分,让它们出现在结果列表的更前面。
每个节点启动后,默认就是一个Master eligible节点,可以通过设置node.master:false来改变,Master-eligible节点可以参加选主流程,成为Master节点,每个节点都保存了集群的状态,但只有Master节点才能修改集群的状态信息,主节点主要负责集群方面的轻量级的动作,比如:创建或删除索引,跟踪集群中的节点,决定分片分配到哪一个节点,在集群再平衡的过程中,如何在节点间移动数据等。
可以保存数据的节点,叫做Data Node。负责保存分片数据。在数据扩展上起到了至关重要的作用,每个节点启动后,默认就是一个Data Node节点,可以通过设置node.data:false来改变。
可以在文档建立索引之前设置一些ingest pipeline的预处理逻辑,来丰富和转换文档。每个节点默认启动就是Ingest Node,可用通过node.ingest:false来禁用。
Coordinating Node负责接收Client的请求,将请求分发到合适的节点,最终把结果汇集到一起,每个节点默认都起到了 Coordinating Node的职责,当然如果把Master、Data、Ingest全部禁用,那这个节点就仅是Coordinating Node节点了。
用于机器学习处理的节点。
分片可以是主分片,也可以是副本分片,其中副本分片是主分片的完整副本,副本分片可以用于搜索,或者是在原有主分片丢失后成为新的主分片。写过程先写主分片,成功后再写副分片。
Elasticsearch索引由一个或多个主分片以及零个或多个副本分片构成。副本分片可以在运行的时候进行添加和移除,而主分片不可以。
可以在任何时候改变每个分片的副本分片的数量,因为副本分片总是可以被创建和移除。这并不适用于索引划分为主分片的数量,在创建索引之前,你必须决定主分片的数量。请记住,过少的分片将限制可扩展性,但是过多的分片会影响性能。默认设置主分片的数量是5份。
索引主分片的设置:
put test1
{
"settings": {
"index.number_of_shards": 3,
"index.codec": "best_compression"
}
}
输出结果:
{
"acknowledged" : true,
"shards_acknowledged" : true,
"index" : "test1"
}
get test1/_settings
输出结果:
{
"test1" : {
"settings" : {
"index" : {
"codec" : "best_compression",
"number_of_shards" : "3",
"provided_name" : "test1",
"creation_date" : "1637409687711",
"number_of_replicas" : "1",
"uuid" : "GnRgeIHeTzy8yaUianvD0g",
"version" : {
"created" : "7040099"
}
}
}
}
}
索引副本分片的设置:
put test1/_settings
{
"index.number_of_replicas": 2
}
输出结果:
{
"acknowledged" : true
}
get test1/_settings
输出结果:
{
"test1" : {
"settings" : {
"index" : {
"codec" : "best_compression",
"number_of_shards" : "3",
"provided_name" : "test1",
"creation_date" : "1637409687711",
"number_of_replicas" : "2",
"uuid" : "GnRgeIHeTzy8yaUianvD0g",
"version" : {
"created" : "7040099"
}
}
}
}
}
水平扩展:最简单的Elasticsearch集群只有一个节点:一台机器运行着一个Elasticsearch进程。我们安装并启动了 Elasticsearch之后,就已经建立了一个拥有单节点的集群。随着越来越多的节点被添加到同一个集群中,现有的分片将在所有的节点中自动进行负载均衡。因此,在那些分片上的索引和搜索请求都可以从额外增加的节点中获益。以这种方式进行扩展(在节点中加入更多节点)被称为水平扩展。此方式增加更多节点,然后请求被分发到这些节点上,工作负载就被分摊了。
垂直扩展:这种方式主要通过为Elasticsearch的节点增加更多硬件资源,可能是为虚拟机分配更多处理器,或是为物理机增加更多的内存。尽管垂直扩展几乎每次都能提升性能,它并非总是可行的或经济的。
默认情况下,当索引一篇文档的时候,系统首先根据文档ID的散列值选择一个主分片,并将文档发送到该主分片。这份主分片可能位于另一个节点,不过对于应用程序这一点是透明的。
默认地,文档在分片中均匀分布:对于每篇文档,分片是通过其ID字符串的散列决定的。每份分片拥有相同的散列范围,接收新文档的机会均等。一旦目标主分片确定,接受请求的节点将文档转发到该主分片所在的节点。随后,索引操作在该主分片的所有副本分片中进行。在所有可用副本分片完成文档的索引后,索引命令就会成功返回。这使得副本分片和主分片之间保持数据的同步。数据同步使得副本分片可以服务于搜索请求,并在原有主分片无法访问时自动升级为主分片。
当搜索一个索引时,Elasticsearch需要在该索引的完整分片集合中进行查找。这些分片可以是主分片,也可以是副本分片,原因是对应的主分片和副本分片通常包含一样的文档。Elasticsearch在索引的主分片和副本分片中进行搜索请求的负载均衡,使得副本分片对于搜索性能和容错都有所帮助。
在搜索的时候,接受请求的节点将请求转发到一组包含所有数据的分片。Elasticsearch使用round-robin的轮询机制选择可用的分片(主分片或副本分片),并将搜索请求转发过去,Elasticsearch然后从这些分片收集结果,将其聚集为单一的回复, 然后将回复返回给客户端应用程序。在默认情况下,搜索请求通过round-robin轮询机制选中主分片和副本分片。