简介
之前一直有计划学习Elasticsearch,刚好最近趁着项目空档期学习了Elasticsearch 7,且也打消了Elasticsearch和Hadoop MapReduce的使用情景有疑问,因为之前认为这两个框架都是对海量数据进行分析,且Elasticsearch的实时性更好为那么Hadoop的使用场景在哪?这是因为Elasticsearch对于精确复杂的统计分析并不支持,其更多是解决全文搜索而生,而MapReduce更多是为了精确分析而生,具体可看维护篇中关于索引的部分。
基本概念
简单的来说就是Elasticsearch对Lucene进行了封装,Lucene是Apache开源的搜索引擎库,但是使用它需要太多的专业知识,Elasticsearch对其进行了封装,并对外提供Rest服务,方便不是很了解全文检索的人使用。
节点篇
Master:主节点,总体机制和其余高可用的主节点没有太多区别,比如写入由主节点统一写入保证数据写入等操作唯一,还有支持部署多个主节点在主节点挂掉后选举出新的主节点,且7.0中加入的防止闹裂等功能。
Data:数据节点,就是保存节点数据的地方,在查询中在各个节点中做查询后统一返回做处理
Coordinating:协调节点,用户请求直接访问到协调节点,由协调节点将数据路由到各个数据节点,数据节点完成查询后,再在协调节点进行统计处理返回,所以ES能承载多少的并发量,部署多少台协调节点是个重要的考虑点
Ingest:ingest 节点可以看作是数据前置处理转换的节点,举个例子在写入文档时不由业务层在每个文档加入时间戳,由ES自动处理,其实这些操作都可以在业务层面去实现,网上看到的说法是,为了解决业务层性能问题(比如上文中说到的每个文档加入时间戳)
Machine learning:机器学习节点,高科技没用过。
由上述节点构成的一组服务器就叫做Cluster集群
索引篇
首先了解一下倒排索引,以下是网上找的说明
倒排索引(英语:Inverted index),也常被称为反向索引、置入档案或反向档案,是一种索引方法,被用来存储在全文搜索下某个单词在一个文档或者一组文档中的存储位置的映射。它是文档检索系统中最常用的数据结构。
用个直观的例子说明一下:下面有两句话
- 中国是社会主义国家
- 美国是资本主义国家
如果我们用关系数据库去查找的话且为这个字段建立了索引,我们做模糊匹配Like搜索,那么其实这个索引的作用就不大,因为他要全表扫描这个字段,让后再在每条记录中和你搜索的字符串做比较,而且假设我现在想搜索同时包含中国和国家的文档,那么关系型数据库就嗝屁了,那么在搜索引擎中为了解决这个问题,对上面的两句话进行了分词(分词的一种原理就是查询字典,比如中国这个词在字典中有没有,如果有的话他就是一个词,如果没有就不是,所以很多分词器为了定制化的远程都支持使用自己的字典库),让后再对每个词建立索引。比如下面那个表格就是一个倒排索引
词 | 文档ID |
---|---|
中国 | 1 |
是 | 1,2 |
社会主义 | 1 |
国家 | 1,2 |
美国 | 2 |
资本主义 | 2 |
Index:索引,就是存储上文说的倒排索引的地方,对比关系型数据库的表(网上资料都将关系型数据库中数据库类比为ES的Index,个人感觉在ES7中有点不太形象,因为7移除了Type,Mapping直接和是和Index关联的)
Mapping:这里可以类比成关系型数据库的表结构
Field:对应关系型数据库表结构下的字段
Document: 文档,这里可以对于关系型数据库中的一条条数据,每条数据存储在Index中,其字段通过Mapping来定义
Sorce
Type: 在7中后移除了这个属性,具体的原因参考官方文档,里面提到了在Lucene 中一个Index中的相同的字段都存储在一起,如果一个Index下后多个不同Type且其存储Mapping字段相同,那么会对索引管理和效率等产生影响,所以删除https://www.elastic.co/guide/en/elasticsearch/reference/current/removal-of-types.html
Score:ES中对文档的搜索的相关度以算分来区分,在多字段查询中其颇为有用,假设我们需要对文档的标题和内容进行检索,假设我们认为标题中如果有搜索词的话其更匹配用户需求,那么我们可以在使用搜索API时提升标题的算分,如果一个字段同时出现在两个文档中,那么标题中有这个文档的算分跟高,就会排在更前面
查询篇
说道查询还是要提起一下上文说道的分词,ES和其它数据库最大的不同就是在分词,所有查询都是围绕分词进行的,在建立索引的时候我们要制定对应Mapping 下的Field字段的类型和采用什么样的分词器,我们查询的时候其实是查询我们查询的词是否命中了倒排索引,而不是像关系型数据库那样全表扫描数据让后再进行比对,如何对查询的词进行分词和组合也是查询的关键
Term:不对查询的词语进行分词,以上文为例,我查询的词语为“中国是”,那么在ES中无法匹配出任何文档,因为假设ES对文档的分词内容如上文所示,那么按照Term查询的定义来说不对查询的词进行分词,那么它就会查询文档中是否有包含“中国是”这个词的索引,显然我们的索引中并没有“中国是”这个词,所以无法命中
Match:对查询的词进行分词,还是以上文为例,我查询“中国资本主义”,那么分词器会把词分为“中国”,“资本主义”去索引中查询文档,那么这里命中了 文档1,2,这两个文档都一起返回,另外Match查询又有Match and (同时匹配中国和资本主义的文档) ,Match Phrase查询(对查询进行分词关联查询)
Aggs:聚合查询,其中又重要包括Bucket和Metric查询,其中Bucket是对数据进行分桶,类比关系型数据库中的Group By,Metric对比关系型数据库的语法为 Min ,Max ,AVG,Distinct等操作,对数据进行统计计算等,一般和关系型数据库一样和Bucket(分桶函数搭配一起使用)
维护篇
Index:和数据库一样,一个索引数据多了为了查询性能就要对数据进行分片,且为了高可用防止硬盘损坏导致数据丢失可对索引做备份,索引如果ES的数据节点不需要对硬盘做磁盘阵列存储数据(如RAID5等),这样节省了硬盘资源。注意:索引一旦创建就不可以修改Mapping(对于动态Mapping可以添加新的字段)和分片信息,如果需要修改只能reIndex重建索引,但是ES和MongoDB一样会对数据进行再平衡,举个例子,我们只有一台数据节点,并且在上建立了一个索引,并且指定了2个主分片,那个新增文档的时候数据会均匀分配在这两个主分片上,假设后续又加入了一个节点做数据节点,那个这两个主分片的一个会自动迁移到另一台数据节点上。至于一个索引要建立多少个分片需要看对数据量进行评估,官方推荐是搜索类文档单分片不超过20G,日志型不超过50G,但是分片也不是越多越好,分片越多查询就越不准确,假设现在执行一个聚合操作(group by)并且返回各个分组数最多的那个,其会在各个数据节点进行分组计算,再将计算后的数据发送到Coordinating节点进行重新聚合,那么数据节点会返回单前节点最大的那个分组给Coordinating节点,但是这样是不准确的,所以业务是需要精确统计的那么ES就不适用,应该选择Hadoop MapReduce这类离线计算的框架,且Index支持冷热数据的切换,假设现在有2台服务器,一台性能很好,一台较差,那么我们可以将热点的Index指定存储到较好配置的节点上,待这部分热点数据变冷后,通过Setting设置将其转移到较差的那台服务器上。
health:Index的分配情况决定了集群是否健康查看集群可通过以下API如果集群中所有索引都是green说明索引分片和副本都正常,如果是yellow说明至少一个副本分片分配失败,如果是red说明至少有一个主分片分配失败,
1
GET /_cat/health
那么我们下一步可以查看所有索引的健康情况,再做对应的处理,如果是红色的话可以查看是否设置了一个node只能有一个分片,如果是黄色,可以查看是否节点不够,因为ES不允许主分片和副本分片同时分配在一台节点上
1
GET _cluster/health?level=indices
Mapping:以下是我之前定义的一个Index的Mapping
我定义了id,label,label_des,sentence这几个字段,每个字段的类型又不同字段,
id:字段类型keyword,该类型不会对其进行分词,因为ID都是进行精确匹配的,且aggs聚合操作不能对Text类型使用
label:字段为整型
label_des,这个字段我们需要注意一下,其类型为text类型,且我指定了使用ik_smart进行分词,并且我指定了第二个数据类型为keyword,这是为了后续我可以使用该字段进行聚合和精确term查找,因为如果是text格式的话其是进行分词存储,所以无法精确匹配和聚合,且定义子数据类型有其它使用场景,比如我们在搜索中文数据时同时需要对拼音和中文进行检索,那么我们可以定义一个子字段,且其采用拼音的分词器,那么在查询时同时匹配中文和拼音字段中的索引
sentence:字段同上,只是区别在与对其存储时和查询时使用了不同的分词器(在进行查询时对查询字段采用ik_smart进行分词)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35PUT /data_test
{
"settings" : {
"number_of_shards" : "1",
"number_of_replicas" : "0"
},
"mappings" : {
"properties" : {
"id" : {
"type" : "keyword",
"ignore_above" : 100
},
"label" : {
"type" : "integer"
},
"label_des" : {
"type" : "text",
"analyzer" : "ik_smart",
"fields":{
"keyword":{
"type":"keyword",
"ignore_above" : 256
}
}
},
"sentence" : {
"type" : "text",
"analyzer" : "ik_max_word",
"search_analyzer" : "ik_smart"
}
}
}
}