Elasticsearch是什么
Elasticsearch是一个基于Apache Lucene(TM)的一个分布式的开源搜索和分析引擎
Elasticsearch实现的功能
-
分布式的搜索引擎
Elasticsearch自动将海量数据分散到多台服务器进行存储和检索
-
全文检索
Elasticsearch提供了模糊搜索等自动度很高的查询方式,并进行相关排名,高亮等功能
-
数据分析
可以做分组聚合
-
对海量数据进行实时处理
因为Elasticsearch是分布式架构,可以采用大量的服务器进行存储和检索数据,可以实现秒级数据搜索和分析
使用场景
- 搜索类场景
- 日志分析 (经典的ELK组合完成日志收集、存储、分析、查询功能)
- 数据预警、分析平台(分析电商平台评论数、访问量、关注度等等)
- 商业BI系统 (分析上一季度消费金额、年龄段、访问时间段的人数分布等等)
全文搜索方案对比
-
solr
Solr是一个有HTTP接口的基于Lucene的查询服务器,封装了很多Lucene细节,自己的应用可以直接利用诸如 .../solr?q=abc 这样的HTTP GET/POST请求去查询,维护修改索引。
-
Elasticsearch
Elasticsearch也是一个建立在全文搜索引擎 Apache Lucene基础上的搜索引擎。采用的策略是分布式实时文件存储,并将每一个字段都编入索引,使其可以被搜索。
两者者之间的一个联系:solr和Elasticsearch都是基于Lucene实现的
区别:
- Solr 利用 Zookeeper 进行分布式管理,而 Elasticsearch 自身带有分布式协调管理功能;
- Solr 支持更多格式的数据,而 Elasticsearch 仅支持json文件格式;
- Solr 官方提供的功能更多,而 Elasticsearch 本身更注重于核心功能,高级功能多有第三方插件提供;
- Solr 在传统的搜索应用中表现好于 Elasticsearch,但在处理实时搜索应用时效率明显低于 Elasticsearch
核心概念
索引(index)
ElasticSearch将它的数据存储在一个或多个索引(index)中。用SQL领域的术语来类比,索引就像数据库,可以向索引写入文档或者从索引中读取文档,并通过ElasticSearch内部使用Lucene将数据写入索引或从索引中检索数据。
类型(type)
类型是模拟mysql中的table概念,一个索引库下可以有不同类型的索引,比如商品索引,订单索引,其数据格式不同。不过这会导致索引库混乱,因此未来版本中会移除这个概念
ES中每个大版本的type类型有很大区别
ES 5.x 中index可以又多种type
ES 6.x 中一个index只能有一个type
ES 7.x以后要逐渐移除这个概念
文档(document)
文档(document)是ElasticSearch中的主要实体。对所有使用ElasticSearch的案例来说,他们最终都可以归结为对文档的搜索。文档由字段构成。
在一个index/type里面,你可以存储任意多的文档。注意,尽管一个文档,物理上存在于一个索引之中,文档必须被索引/赋予一个索引的type。
字段Field
相当于是数据表的字段,对文档数据根据不同属性进行的分类标识
映射 mapping
mapping就相当于我们关系型数据库中的表结构,是处理数据的方式和规则方面做一些限制,如某个字段的数据类型、默认值、分析器、是否被索引等等,这些都是映射里面可以设置的,其它就是处理es里面数据的一些使用规则设置也叫做映射,按着最优规则处理数据对性能提高很大,因此才需要建立映射,并且需要思考如何建立映射才能对性能更好。
集群 cluster
一个集群就是由一个或多个节点组织在一起,它们共同持有整个的数据,并一起提供索引和搜索功能。一个集群由一个唯一的名字标识,这个名字默认就是“elasticsearch”。这个名字是重要的,因为一个节点只能通过指定某个集群的名字,来加入这个集群
可以通过get请求查看集群状态:
http://es地址:9200/_cat/health?v
返回数据:
epoch timestamp cluster status node.total node.data shards pri relo init unassign pending_tasks max_task_wait_time active_shards_percent
1645155625 11:40:25 sxjyes green 3 3 252 126 0 0 0 0 - 100.0%
参数说明:
cluster:集群名称
status:集群状态 green代表健康;yellow代表分配了所有主分片,但至少缺少一个副本,此时集群数据仍旧完整;red代表部分主分片不可用,可能已经丢失数据。
node.total:代表在线的节点总数量
node.data:代表在线的数据节点的数量
shards, active_shards: 存活的分片数量
pri,active_primary_shards: 存活的主分片数量 正常情况下 shards的数量是pri的两倍。
relo, relocating_shards :迁移中的分片数量,正常情况为 0
init, initializing_shards: 初始化中的分片数量 正常情况为 0
unassign, unassigned_shards: 未分配的分片 正常情况为 0
pending_tasks:准备中的任务,任务指迁移分片等 正常情况为 0
max_task_wait_time:任务最长等待时间
active_shards_percent:正常分片百分比 正常情况为 100%
节点 node
一个节点是集群中的一个服务器,作为集群的一部分,它存储数据,参与集群的索引和搜索功能。和集群类似,一个节点也是由一个名字来标识的,默认情况下,这个名字是一个随机的漫威漫画角色的名字,这个名字会在启动的时候赋予节点。这个名字对于管理工作来说挺重要的,因为在这个管理过程中,你会去确定网络中的哪些服务器对应于Elasticsearch集群中的哪些节点。
一个节点可以通过配置集群名称的方式来加入一个指定的集群。默认情况下,每个节点都会被安排加入到一个叫做“elasticsearch”的集群中,这意味着,如果你在你的网络中启动了若干个节点,并假定它们能够相互发现彼此,它们将会自动地形成并加入到一个叫做“elasticsearch”的集群中。
在一个集群里,只要你想,可以拥有任意多个节点。而且,如果当前你的网络中没有运行任何Elasticsearch节点,这时启动一个节点,会默认创建并加入一个叫做“elasticsearch”的集群。
分片和复制 shards&replicas
一个索引可以存储超出单个结点硬件限制的大量数据。比如,一个具有10亿文档的索引占据1TB的磁盘空间,而任一节点都没有这样大的磁盘空间;或者单个节点处理搜索请求,响应太慢。为了解决这个问题,Elasticsearch提供了将索引划分成多份的能力,这些份就叫做分片。当你创建一个索引的时候,你可以指定你想要的分片的数量。每个分片本身也是一个功能完善并且独立的“索引”,这个“索引”可以被放置到集群中的任何节点上。分片很重要,主要有两方面的原因:
1)允许你水平分割/扩展你的内容容量。
2)允许你在分片(潜在地,位于多个节点上)之上进行分布式的、并行的操作,进而提高性能/吞吐量。
至于一个分片怎样分布,它的文档怎样聚合回搜索请求,是完全由Elasticsearch管理的,对于作为用户的你来说,这些都是透明的。
在一个网络/云的环境里,失败随时都可能发生,在某个分片/节点不知怎么的就处于离线状态,或者由于任何原因消失了,这种情况下,有一个故障转移机制是非常有用并且是强烈推荐的。为此目的,Elasticsearch允许你创建分片的一份或多份拷贝,这些拷贝叫做复制分片,或者直接叫复制。
复制之所以重要,有两个主要原因: 在分片/节点失败的情况下,提供了高可用性。因为这个原因,注意到复制分片从不与原/主要(original/primary)分片置于同一节点上是非常重要的。扩展你的搜索量/吞吐量,因为搜索可以在所有的复制上并行运行。总之,每个索引可以被分成多个分片。一个索引也可以被复制0次(意思是没有复制)或多次。一旦复制了,每个索引就有了主分片(作为复制源的原来的分片)和复制分片(主分片的拷贝)之别。分片和复制的数量可以在索引创建的时候指定。在索引创建之后,你可以在任何时候动态地改变复制的数量,但是你事后不能改变分片的数量。
默认情况下,Elasticsearch中的每个索引被分片5个主分片和1个复制,这意味着,如果你的集群中至少有两个节点,你的索引将会有5个主分片和另外5个复制分片(1个完全拷贝),这样的话每个索引总共就有10个分片。
Elasticsearch概念和关系型数据库的对比
关系型数据库(如mysql) | Elasticsearch |
---|---|
数据库 | 索引index |
表(table) | 索引index类型(原为type) |
数据行row | 文档(document) |
数据列(column) | 字段Field |
约束 schema | 映射 mapping |
字段类型
字段类型分为基本类型和复合类型‘
基本类型
字符串类型
-
string
string类型在ElasticSearch 旧版本中使用较多,从ElasticSearch 5.x开始不再支持string,由text和keyword类型替代。
-
text
当一个字段是要被全文搜索的,比如Email内容、产品描述,应该使用text类型。设置text类型以后,字段内容会被分析,在生成倒排索引以前,字符串会被分析器分成一个一个词项。text类型的字段不用于排序,很少用于聚合。
text会分词,然后进行索引,支持模糊、精确查询,不支持聚合
-
keyword
这种类型适用于结构化的字段,例如标签、email 地址、手机号码等等,这种类型的字段可以用作过滤、排序、聚合等。这种字符串也称之为 not-analyzed 字段。
keyword不进行分词,直接索引,支持模糊、精确查询,支持聚合
数字类型
数字类型有long、integer、short、byte、double、float、half_float、scaled_float。
- 在满足需求的情况下,优先使用范围小的字段。字段长度越短,索引和搜索的效率越高。比如,某个字段的取值最大值不会超过100,那么选择byte类型即可。迄今为止吉尼斯记录的人类的年龄的最大值为134岁,对于年龄字段,short足矣字段的长度越短,索引和搜索的效率越高。
- 浮点数,优先考虑使用 scaled_float。
日期类型
由于 JSON 中没有日期类型,所以 es 中的日期类型形式就比较多样:2020-11-11 或者 2020-11-11 11:11:11,一个从 1970.1.1 零点到现在的一个秒数或者毫秒数。es 内部将时间转为 UTC,然后将时间按照 millseconds-since-the-epoch 的长整型来存储。
boolean类型
逻辑类型(布尔类型)可以接受true/false/”true”/”false”值
二进制类型(binary)
二进制字段是指用base64来表示索引中存储的二进制数据,可用来存储二进制形式的数据,例如图像。默认情况下,该类型的字段只存储不索引。二进制类型只支持index_name属性。
范围类型(range)
数据范围类型,一个字段表示一个范围,具体包含以下类型:
- integer_range
- float_range
- double_range
- date_range
- ip_range
所谓的范围类型,就是一个值自身就表明一个范围,如:
PUT range_index
{
"mappings": {
"_doc": {
"properties": {
"expected_attendees": {
"type": "integer_range"
}
}
}
}
}
复合类型
数组类型(array)
es 中没有专门的数组类型。默认情况下,任何字段都可以有一个或者多个值。需要注意的是,数组中的元素必须是同一种类型。添加数组是,数组中的第一个元素决定了整个数组的类型。
对象类型(object)
由于 JSON 本身具有层级关系,所以文档包含内部对象。内部对象中,还可以再包含内部对象。
IK分词器
Elasticsearch自带的标准分词器是针对英文的,所以我们在日常开发中都是使用ik分词器,它是一个针对中文的分词器。
从2016年12月推出1.0版本开始,IKAnalyzer已经推出了三个大版本,最初他是以开源项目Lucene为应用主题的,结合词典分词和文法分析算法的中文分词组件,新版的IKAnalyzer3.0则发展为面向java的公用分词组件,独立于Lucene项目,同时提供了对Lucene的默认优化实现
IKAnalyzer3.0版本特性如下:
- 采用了特有的“正向迭代最细粒度切分算法“,具有50万字/秒的高速处理能力。
- 采用了多子处理器分析模式,支持:英文字母(IP地址、Email、URL)、数字(日期,常用中文数量词,罗马数字,科学计数法),中文词汇(姓名、地名处理)等分词处理。
- 优化的词典存储,更小的内存占用。支持用户词典扩展定义
- 针对Lucene全文检索优化的查询分析器IKQueryParser;采用歧义分析算法优化查询关键字的搜索排列组合,能极大的提高Lucene检索的命中率。
IK分词器有两种分词模式:ik_max_word和ik_smart模式。
ik_max_word
会将文本做最细粒度的拆分,比如会将“中华人民共和国人民大会堂”拆分为“中华人民共和国、中华人民、中华、华人、人民共和国、人民、共和国、大会堂、大会、会堂等词语。
ik_smart
会做最粗粒度的拆分,比如会将“中华人民共和国人民大会堂”拆分为中华人民共和国、人民大会堂。
IK分词器扩展词典
停用词扩展
- 1) 我们找到IK的配置文件,位于ik/config/IKAnalyzer.cfg.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE properties SYSTEM "http://java.sun.com/dtd/properties.dtd">
<properties>
<comment>IK Analyzer 扩展配置</comment>
<!--用户可以在这里配置自己的扩展字典 -->
<entry key="ext_dict">ext_dic.dic</entry>
<!--用户可以在这里配置自己的扩展停止词字典-->
<entry key="ext_stopwords"></entry>
<!--用户可以在这里配置远程扩展字典 -->
<!-- <entry key="remote_ext_dict">words_location</entry> -->
<!--用户可以在这里配置远程扩展停止词字典-->
<!-- <entry key="remote_ext_stopwords">words_location</entry> -->
</properties>
- 2)在配置文件中增加配置的停用词文件的节点:
<entry key="ext_stopwords">my_ext_stopword.dic</entry>
3)在类目前下创建扩展停用词文件my_ext_stopword.dic
4) 在文件my_ext_stopword.dic中添加词,一行一个
扩展字典
- 1) 我们找到IK的配置文件,位于ik/config/IKAnalyzer.cfg.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE properties SYSTEM "http://java.sun.com/dtd/properties.dtd">
<properties>
<comment>IK Analyzer 扩展配置</comment>
<!--用户可以在这里配置自己的扩展字典 -->
<entry key="ext_dict">ext_dic.dic</entry>
<!--用户可以在这里配置自己的扩展停止词字典-->
<entry key="ext_stopwords"></entry>
<!--用户可以在这里配置远程扩展字典 -->
<!-- <entry key="remote_ext_dict">words_location</entry> -->
<!--用户可以在这里配置远程扩展停止词字典-->
<!-- <entry key="remote_ext_stopwords">words_location</entry> -->
</properties>
- 2)在配置文件中增加配置的停用词文件的节点:
<entry key="ext_dict">my_ext_dict.dic</entry>
3)在类目前下创建扩展停用词文件my_ext_dict.dic
4) 在文件my_ext_dict.dic中添加词,一行一个
同义词词典
Elasticsearch 自带一个名为 synonym 的同义词 filter。为了能让 IK 和 synonym 同时工作,我们需要定义新的 analyzer,用 IK 做 tokenizer,synonym 做 filter。
- 打开 /config/elasticsearch.yml 文件,加入以下配置:
index:
analysis:
analyzer:
ik_syno:
type: custom
tokenizer: ik_max_word
filter: [my_synonym_filter]
ik_syno_smart:
type: custom
tokenizer: ik_smart
filter: [my_synonym_filter]
filter:
my_synonym_filter:
type: synonym
synonyms_path: analysis/synonym.txt
以上配置定义了 ik_syno 和 ik_syno_smart 这两个新的 analyzer,分别对应 IK 的 ik_max_word 和 ik_smart 两种分词策略
创建/config/analysis/synonym.txt 文件,输入一些同义词并存为 utf-8 格式。例如
重启es即可